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 thedata
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 usebigIncrements()
or some such column type, and settenancy.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;
}