Multi-Tenancy for Laravel and Laravel-Doctrine.

This library provides the necessary infra-structure for a complex multi-tenant application. Multi-tenancy allows an application to be silo'd into protected areas by some form of tenant identifier. This could be by sub-domain, URL parameter or some other scheme.

Article image for: Multi-Tenancy for Laravel and Laravel-Doctrine

Routing.

Terminology
Types of Tenancy
Getting Started
Security Models
Routing
Middleware & Views
Issues / Links

Github Print

Routing

For any route within the tenant group and provided that the placeholder name is tenant_creator_id, any route generated for a tenant controller will automatically embed the current tenant information. In fact both the owner and creator are automatically checked for and injected when creating routes.

This is done by overriding the default UrlGenerator with one that adds the Tenant entity and then checking the route information for both {tenant_owner_id} and {tenant_creator_id}. The properties are then automatically injected.

This only occurs when using named routes and within a tenancy group.

For paths, you will have to include the tenant information yourself; similarly when creating a tenant selection list, you must supply the tenant information as parameters when outputting the links.

The tenant parameters can of course be overridden by simply setting them when calling link_to_route.

You should use named routes for tenancy, as this makes it easier to make changes to the routing structure.

Finally: as with repositories you should clearly label tenant based routes so that they are not confused with standard routes.

In addition any un-authenticated routes should be excluded from the tenancy group - unless you implement a tenant aware anonymous user (not recommended).

Multi-Site Routing

In a multi-site setup you may want to have different routes per site. In this case you will need to remove your RouteServiceProvider entirely and switch it for the TenantRouteResolver middleware. Then you will need to either create per tenant domain route files (which can include() shared routes) or symlink the files if you wish to use the exact same routes.

A middleware is provided to handle loading the routes for a multi-site setup. This must be loaded after the TenantSiteResolver, but before any other middleware. In addition you must disable / remove the default App/Providers/RouteServiceProvider. This provider is registered too early and must be delayed / resolved via the TenantRouteResolver instead.

The reasons for this setup are to ensure that only the chosen tenants routes are loaded, and not appended to any existing routing files.

Note: these are not route middleware but Kernel middleware.

Your Kernel.php will end up looking like the following:

class Kernel extends HttpKernel
{
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,

        // must appear AFTER maintenance mode, but before everything else
        \Somnambulist\Tenancy\Http\Middleware\TenantSiteResolver::class,
        \Somnambulist\Tenancy\Http\Middleware\TenantRouteResolver::class,

        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
    ];

    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'auth.tenant' => \Somnambulist\Tenancy\Http\Middleware\AuthenticateTenant::class,
        'auth.tenant.type' => \Somnambulist\Tenancy\Http\Middleware\EnsureTenantType::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    ];
}

The auth.tenant / auth.tenant.type are optional in multi-site, and should only be included if you are using multi-account tenancy.

Again: ensure that the previous RouteServiceProvider in config/app.php has been removed.

Note: you must not use the standard route:list, route:cache in a multi-site setup. Tenant aware versions of these commands are automatically registered if a multi-site setup is detected in the configuration settings and are prefixed with tenant:.

Route Namespace

When using the TenantRouteResolver, you must specify the route namespace in the tenancy config file under the multi_site configuration block:

// config/tenancy.php
return [
    // other stuff...
    'multi_site' => [
        'router' => [
            'namespace' => 'App\Http\Controller', // default
        ],
    ],
    // more stuff...
];

If left out, the default App\Http\Controller is used. If set to an empty string, then no namespace prefix will be set on any routes.

Route Patterns

Like the namespace, patterns can still be set by adding them to your config/tenancy.php under multi_site.router.patterns. This is an associative array of identifier and pattern. They are registered with the router when the routes are resolved.

// config/tenancy.php
return [
    // other stuff...
    'multi_site' => [
        'router' => [
            'namespace' => 'App\Http\Controller', // default
            'patterns' => [
                'id' => '[0-9]+',
            ],
        ],
    ],
    // more stuff...
];