/Multitenancy

A simple and opinionated package for providing subdomain based multi-tenancy to Laravel

Primary LanguagePHPMIT LicenseMIT

Multitenancy Package

Total Downloads

This package is meant to be a quick and easy way to add multitenancy to your Laravel application. It simply creates models and relationships for Tenants and models. The package identifies incoming traffic by subdomain, and finds a corresponding tenant in the Tenant table. If none are found or the user is not associated with a particular subdomain, the user is met with a 403 error.

The admin subdomain is reserved for the package. It is used to automatically remove all scopes from users with a Super Administrator role.

To scope a resource to the currently accessed subdomain, you simply need to add a single trait to the model and add a foreign key relationship to the model's table. The package middleware will automatically apply the scopes for the relevant models.

Any resources saved while accessing a scoped subdomain will automatically be saved against the current tenant, based on subdomain.

Installation

You can install the package via composer:

composer require romegadigital/multitenancy

In Laravel 5.5 the service provider will automatically get registered. In older versions of the framework you should add the service provider in the config/app.php file:

'providers' => [
    // ...
    RomegaDigital\Multitenancy\MultitenancyServiceProvider::class,
];

You can publish the config file with:

php artisan vendor:publish --provider="RomegaDigital\Multitenancy\MultitenancyServiceProvider" --tag="config"

If you want the user to automatically be assigned to the Tenant it is created on you can enable this functionality by simply enabling the ignore_tenant_on_user_creation setting.

You can automate most of the setup by running:

php artisan multitenancy:install

It will:

  • publish and migrate required migrations
  • add a Super Administrator role and access admin permission
  • add an admin Tenant model

Usage

First, add the RomegaDigital\Multitenancy\Traits\HasTenants and Spatie\Permission\Traits\HasRoles traits to your User model(s):

use Spatie\Permission\Traits\HasRoles;
use RomegaDigital\Multitenancy\Traits\HasTenants;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasTenants, HasRoles;

    // ...
}

The package relies on Eloquent, so you may access the User's tenants with User::tenants()->get().

Inversely, you may access the Tenant's users with Tenant::users()->get().

Tenants require a name to identify the tenant and and a subdomain that is associated with that user. Example:

tenant1.example.com

tenant2.example.com

These values could be added to the database like so:

Tenant::create([
    'name'    => 'An Identifying Name',
    'domain'  => 'tenant1'
]);
Tenant::create([
    'name'    => 'A Second Customer',
    'domain'  => 'tenant2'
]);

You can then attach user models to the Tenant:

$user = User::first();
Tenant::first()->users()->save($user);

Middleware

Authenticated

This package comes with TenantMiddleware middleware which extends Laravel's Illuminate\Auth\Middleware\Authenticate. You can add it inside your app/Http/Kernel.php file.

protected $routeMiddleware = [
    // ...
    'tenant.auth' => \RomegaDigital\Multitenancy\Middleware\TenantMiddleware::class,
];

Then you can bring multitenancy to your routes using middleware rules:

Route::group(['middleware' => ['tenant.auth']], function () {
    // ...
});

Guest Tenant

This package comes with GuestTenantMiddleware middleware which applies the tenant scope to all models and can be used for allowing guest users to access Tenant related pages. You can add it inside your app/Http/Kernel.php file.

protected $routeMiddleware = [
    // ...
    'tenant.guest' => \RomegaDigital\Multitenancy\Middleware\GuestTenantMiddleware::class,
];

Then you can bring multitenancy to your routes using middleware rules:

Route::group(['middleware' => ['tenant.guest']], function () {
    // ...
});

Tenant Assignment for Models

Models can automatically inherit scoping of the current tenant by adding a trait and migration to a model. This would allow users to access tenant1.example.com and return the data from tenant1 only.

For example, say you wanted Tenants to manage their own Product. In your Product model, add the BelongsToTenant trait. Then run the provided console command to add the necessary relationship column to your existing products table.

use Illuminate\Database\Eloquent\Model;
use RomegaDigital\Multitenancy\Traits\BelongsToTenant;

class Product extends Model
{
    use BelongsToTenant;

    // ...
}

Hint If the user is assigned Super Administrator access, they will be able to access the admin subdomain and the tenant scope will not register. This allows you to manage the data across all instances without needing individual access to each Tenant's account.

Get Current Tenant

You can get the current tenant model:

app('multitenancy')->currentTenant();

Providing Access to Admin Domain

In order to access the admin.example.com subdomain, a user will need the access admin permission. This package relies on Spatie's Laravel Permission package and is automatically included as a dependency when installing this package. We also provide a Super Administrator role on migration that has the relevant permission already associated with it. Assign the Super Administrator role to an admin user to provide the access they need. See the Laravel Permission documentation for more on adding users to the appropriate role and permission.

A subdomain for the admin portal will be created automatically during installation, but you can manually add it like this:

Tenant::create([
    'name'      => 'Admin Portal',
    'domain'    => 'admin'
]);

Console Commands

Generate a migration to add tenancy to an existing model's table:

php artisan multitenancy:migration products

Assign a user Super Administration rights and the admin Tenant:

php artisan multitenancy:super-admin admin@example.com

Managing with Nova

There is a separate Nova Package available that allows you to manage the resources utilized in this package in Nova.