Tenants

A tenant can be any model that implements the Stancl\Tenancy\Contracts\Tenant interface.

The package comes with a base Tenant model that's ready for common things, though will require extending in most cases as it attempts not to be too opinionated.

The base model has the following features on top of the ones that are necessary by the interface:

  • Forced central connection (lets you interact with Tenant models even in the tenant context)
  • Data column trait — lets you store arbitrary keys. Attributes that don't exist as columns on your tenants table go to the data column as serialized JSON.
  • Id generation trait — when you don't supply an ID, a random uuid will be generated. An alternative to this would be using AUTOINCREMENT columns. If you wish to use numerical ids, change the create_tenants_table migration to use bigIncrements() or some such column type, and set tenancy.id_generator config to null. That will disable the ID generation altogether, falling back to the database's autoincrement mechanism.

Tenant Model

Most applications using this package will want domain/subdomain identification and tenant databases. To do this, create a new model, e.g. App\Tenant, that looks like this:

<?php

namespace App;

use Stancl\Tenancy\Database\Models\Tenant as BaseTenant;
use Stancl\Tenancy\Contracts\TenantWithDatabase;
use Stancl\Tenancy\Database\Concerns\HasDatabase;
use Stancl\Tenancy\Database\Concerns\HasDomains;

class Tenant extends BaseTenant implements TenantWithDatabase
{
    use HasDatabase, HasDomains;
}

Then, configure the package to use this model in config/tenancy.php:

'tenant_model' => \App\Tenant::class,

If you want to customize the Domain model, you can do that too.

If you don't need domains or databases, ignore the steps above. Everything will work just as well.

Creating tenants

You can create tenants like any other models:

$tenant = Tenant::create([
    'plan' => 'free',
]);

After the tenant is created, an event will be fired. This will result in things like the database being created and migrated, depending on what jobs listen to the event.

Custom columns

Attributes of the tenant model which don't have their own column will be stored in the data JSON column. You can set these attributes like you'd set normal model attributes:

$tenant->update([
    'attributeThatHasNoColumn' => 'value', // stored in the `data` JSON column
    'plan' => 'free' // stored in the dedicated `plan` column (see below)
]);

or

$tenant->customAttribute = 'value'; // stored in the `data` JSON column
$tenant->plan = 'free'; // stored in the `plan` column (see below)
$tenant->save();

You may define the custom columns (that won't be stored in the data JSON column) by overriding the getCustomColumns() method on your Tenant model:

public static function getCustomColumns(): array
{
    return [
        'id',
        'plan',
    ];
}

Don't forget to keep id in the custom columns!

If you want to rename the data column, rename it in a migration and implement this method on your model:

public static function getDataColumn(): string
{
    return 'my-data-column';
}

Note that querying data inside the data column with where() will require that you do for example:

where('data->foo', 'bar')

The data column is encoded/decoded only on model retrieval and saving.

Also a good rule of thumb is that when you need to query the data with WHERE clauses, it should have a dedicated column. This will improve performance and you won't have to think about the data-> prefixing.

Running commands in the tenant context

You may run commands in a tenant's context and then return to the previous context (be it central, or another tenant's) by passing a callable to the run() method on the tenant object. For example:

$tenant->run(function () {
    User::create(...);
});

Internal keys

Keys that start with the internal prefix (tenancy_ by default, but you can customize this by overriding the internalPrefix() method) are for internal use, so don't start any attribute/column names with that.

Events

The Tenant model dispatches Eloquent events, all of which have their own respective class. You can read more about this on the Event system page.

Accessing the current tenant

You may access the current tenant using the tenant() helper. You can also pass an argument to get an attribute from that tenant model, e.g. tenant('id').

Alternatively, you may typehint the Stancl\Tenancy\Contracts\Tenant interface to inject the model using the service container.

Incrementing IDs

By default, the migration uses string for the id column, and the model generates UUIDs when you don't supply an id during tenant creation.

If you'd like to use incrementing ids instead, you can override the getIncrementing() method:

public function getIncrementing()
{
    return true;
}