martinbean

I’m building a “multi-tenant” CMS at the minute, and the solution looks similar to yours, @nate.a.johnson and @ricardoarg.

I have a “domain resolver” service class with a resolveFromRequest() method. I call this in my app service provider which will attempt to the resolve the tenant based on the hostname, and then store that tenant model instance in the service container:

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        if (!$this->app->runningInConsole()) {
            $this->app[Resolver::class]->resolveFromRequest($this->app['request']);
        }
    }

    public function register()
    {
        $this->app->singleton(Resolver::class, function ($app) {
            return new Resolver($app);
        });
    }

    public function provides()
    {
        return [
            Resolver::class,
        ];
    }
}

The Resolver class looks something like this:

class Resolver
{
    public function __construct(Application $app)
    {
        $this->app = $app;
    }

    public function resolveFromRequest(Request $request)
    {
        $domain = Domain::with('tenant')->whereName($host)->firstOrFail();

        // If domain found, store tenant in service container
        $this->app->instance(Tenant::class, $domain->tenant);
    }
}

By storing the Tenant in the service container, I can then inject it in my app where I need to, such as controllers and other service classes.

I also have repositories to access scoped data, by accessing it through the Tenant:

class ArticleRepository
{
    // Inject Tenant as it’s in the service container
    public function __construct(Tenant $tenant)
    {
        $this->articles = $tenant->articles();
    }

    public function paginate()
    {
        return $this->articles->paginate();
    }
}
class ArticleController extends Controller
{
    public function __construct(ArticleRepository $articles)
    {
        $this->articles = $articles;
    }

    public function index()
    {
        $articles = $this->articles->paginate();

        return view('article.index', compact('articles'));
    }
}

If I access data via these repositories, then I know I’m not going to get data polluted with other tenants’.

Return to Thread...