sprout-laravel / sprout

A flexible, seamless and easy to use multitenancy solution for Laravel
https://sprout.ollieread.com
MIT License
248 stars 7 forks source link

The provided value for 'model' is not valid for provider [tenants] #74

Open endikainchaurtieta opened 1 week ago

endikainchaurtieta commented 1 week ago

First of all, congratulations on the 1.0-alpha release! This project shows a lot of potential, and I truly appreciate the effort you’ve put into it.

I’ve been experimenting with it and encountered an error I can’t seem to resolve. I’ve installed the package and configured it for subdomain tenancy with an Eloquent model. Here’s my multitenancy.php configuration file:

<?php

use Sprout\TenancyOptions;

return [

    /*
    |--------------------------------------------------------------------------
    | Multitenancy Defaults
    |--------------------------------------------------------------------------
    |
    | This option defines the default tenancy, tenant provider and identity
    | resolver for your application.
    | You may change these values as required, but they're a perfect start
    | for most applications.
    |
    */

    'defaults' => [

        'tenancy'  => 'tenants',
        'provider' => 'tenants',
        'resolver' => 'subdomain',

    ],

    /*
    |--------------------------------------------------------------------------
    | Tenancies
    |--------------------------------------------------------------------------
    |
    | Next, you may define every tenancy type for your application.
    | If you only have one type of tenancy within your application, which will
    | be the case for most people, you can leave it at one.
    |
    | All tenancies have a tenant provider, which defines how the
    | tenants are actually retrieved out of your database or other storage
    | system used by the application.
    |
    | Tenancies can also have options, which is an array of options provided
    | by the TenancyOptions class that lets you fine tune the tenancies
    | behaviour.
    |
    */

    'tenancies' => [

        'tenants' => [
            'provider' => 'tenants',
            'options'  => [
                TenancyOptions::hydrateTenantRelation(),
                TenancyOptions::throwIfNotRelated(),
            ],
        ],

    ],

    /*
    |--------------------------------------------------------------------------
    | Tenant Providers
    |--------------------------------------------------------------------------
    |
    | All tenancies have a tenant provider, which defines how the
    | tenants are actually retrieved out of your database or other storage
    | system used by the application.
    |
    | If you have multiple tenant tables or models, you can configure multiple
    | providers to represent each.
    | These providers may then be assigned to any extra tenancies you have defined.
    |
    | Supported: "database", "eloquent"
    |
    */

    'providers' => [

        'tenants' => [
            'driver' => 'eloquent',
            'model'  => \App\Models\TenantModel::class,
        ],

        // 'backup' => [
        //     'driver' => 'database',
        //     'table'  => 'tenants',
        // ],

    ],

    /*
    |--------------------------------------------------------------------------
    | Identity Resolvers
    |--------------------------------------------------------------------------
    |
    | Where Laravel's auth would have a separate guard for each way that a
    | user can be authenticated in a request (think session, header, etc.),
    | Sprout abstracts this out into identity resolvers.
    | This means that all tenancies within the application can use all
    | configured resolvers.
    |
    | If you have multiple ways that tenant can be identified, say, through
    | subdomain and then a HTTP header for APIs, you can define one for each.
    |
    | There are sensible defaults for each supported driver, though it is
    | recommended that you remove any that you don't need, for simplicity
    | sake.
    |
    | Supported: "subdomain", "header", "path", "cookie" and "session"
    |
    */

    'resolvers' => [

        'subdomain' => [
            'driver'  => 'subdomain',
            'domain'  => 'tenancy.test',
            'pattern' => '.*',
        ],
    ],

];

My tenant model looks like this:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Sprout\Database\Eloquent\Concerns\IsTenant;
use Sprout\Contracts\Tenant;

class TenantModel extends Model implements Tenant
{
    use IsTenant;

    protected $table = 'tenants';

    protected $fillable = [
        'name',
        'identifier',
        'resource_key',
    ];
}

Then, I've set up the route like this:

Route::tenanted(function () {
    Route::middleware('guest')->group(function () {
        Route::get('login', [AuthenticatedSessionController::class, 'create'])->name('tenant.login');
    });
});

Then when I try to access let's say, example.tenancy.test/login I get this error, I've traced and it's being raised by the createEloquentProvider function. The db has a record with the tenant, and I've also tried to add the getTenantKeyName and the getTenantIdentifierName but doesn't work, probably because I'm doing something wrong.

Sprout\Exceptions\MisconfigurationException
The provided value for 'model' is not valid forprovider [tenants]

Appreciate if you can give me a hand here to keep playing with it, thanks!

ollieread commented 12 hours ago

Hello @endikainchaurtieta, apologies for the delay, but I was quite severely ill last week.

Did you manage to solve this? That particular error is thrown by the following code:

if (
    ! class_exists($config['model'])
    || ! is_subclass_of($config['model'], Model::class)
    || ! is_subclass_of($config['model'], Tenant::class)
) {
    throw MisconfigurationException::invalidConfig('model', 'provider', $name);
}

Which means that the exception is only thrown if one of the following conditions isn't met.

From what you've provided, I don't see why that wouldn't be the case, but the only way I can recreate this locally is if I force one of those conditions to not be met.