Laravel 6 - Setting Up A Multi-tenant Application With Nested Tenants

I am currently trying to figure out the best way to set up multi-tenancy for my system. The issue I'm facing is that a tenant doesn't always have to be a sub-domain but can be set up as part of a sub-domain, where the sub-domain can have multiple tenants. I can't seem to find anything online that would help me set this up in Laravel 6.

System Requirements:

  • A server can have many sub-domains
  • A sub-domain can be a tenant
  • A sub-domain can have many tenants
  • A tenant can have many users
  • a tenant can have different features

The system has to be set up with a single database that will use tenant_id to determine which data belongs to a tenant.

I am currently storing all sub-domain data in a table "subdomains" with the following structure:

subdomain (unique)
nested_tenants (yes/no)

where the column nested_tenants determines whether or not the sub-domain is a tenant(=0) itself or has multiple tenants(=1). if the sub-domain does not have nested tenants then we set tenant_id=subdomain

if the sub-domain does have nested tenants then we store all of these in a table with structure:

subdomain (the sub-domain it belongs to)
tenant (the tenant - unique field)

and we set tenant_id=tenant from this table.

if we have nested tenants for a sub-domain then we cannot determine what the current tenant is until the user logs in. we would have to get the tenant_id from the user details.

My current set up:

I've been following this article and have set up the following:

I have two models Subdomain, Tenant


    'middleware' => \App\Http\Middleware\IdentifySubdomain::class,
    'as' => 'tenant:',
    'namespace' => 'Tenant'
], function () {
    // custom auth routes
    Route::get('/login', 'Auth\[email protected]')->name('login');

    Route::post('/login', 'Auth\[email protected]');

    Route::get('/home', '[email protected]')->name('home');

Middleware IdentifySubdomain:

class IdentifySubdomain

    protected $tenantManager;

    public function __construct(TenantManager $tenantManager) {
        $this->tenantManager = $tenantManager;

    public function handle($request, Closure $next)
        /** need to check whether subdomain is valid 
        * if subdomain is valid return the request page else error message. 
        * if subdomain is true it will check the nested_tenants value from db. 
        * if nested_tenants is false it will set the tenant to current subdomain 
        * else the tenant is not set yet. 
        // get host domain and subdomain domain
        $host = $request->getHost();

        // get subdomain position
        $pos = strpos($host, env('TENANT_DOMAIN'));
        $subdomain = substr($host, 0, $pos - 1);

        if ($pos !== false && $this->tenantManager->checkSubdomain($subdomain)) {
            return $next($request);

        throw new NotFoundHttpException;


class TenantManager {

    private $tenant;

    public function setTenant(?Tenant $tenant) {
        $this->tenant = $tenant;
        return $this;

    public function getTenant(): ?Tenant {
        return $this->tenant;

    public function loadTenant(string $identifier): bool {

        $tenant = Tenant::query()->where('tenant', '=', $identifier)->first();
        if ($tenant) {
            return true;

        return false;

    public function checkSubdomain(string $identifier) : bool {
        $subdomain = Subdomain::query()->where('subdomain', '=', $identifier)->first();

        if ($subdomain) {
            if ($subdomain->nested_tenants) {
                // tenant not found yet so do not set tenant
                return true;
            } else {
                return $this->loadTenant($identifier);


        return false;

Service Provider

class TenantServiceProvider extends ServiceProvider
    public function register()
        $manager = new TenantManager;
        $this->app->instance(TenantManager::class, $manager);
        $this->app->bind(Tenant::class, function() use ($manager) {   
            $tenant = $manager->getTenant();
            if ($tenant === null) {
                return new Tenant;
            return $manager->getTenant();


Login Controller:

class LoginController extends Controller
    public function __construct()
    public function login(Request $request, Tenant $tenant) {
            'email' => ['required', 'email', 'max:255'],
            'password' => ['required'],
        $credentials = $request->only('email', 'password');
        $credentials['status'] = 1;
        if ($tenant->id) {    
            $credentials['tenant_id'] = $tenant->tenant;            
        if (Auth::attempt($credentials)) {                           
            return redirect()->intended('home');           

        return Redirect::to('login')->withSuccess('Login Failed! You entered invalid credentials');


My main concern is that I don't feel like this is the best approach to keeping track of the tenant. I need it so that once the tenant is set I can use it throughout the application, without always first checking if the user is authenticated first - to then get the tenant. I am currently adding Tenant $tenant to the controller methods where I need tenant related data, but is a there better way of going about this?

Any advice on how I could improve my current set up would be helpful.



I think you should implement Traits to add tenant constraints for example: in models:


public static function bootBelongsToTenantModelTrait(){
       static::addGlobalScope(function ($model){
            //Or any similar logic

and other traits to controllers if needed.
You may also add middlewares like AuthTenant if needed as well.
I think this way should decouple the tenant-related logic as much as possible.
Let me know what you think.