Tenants
Tenants are database models that represent the tenants used by this package.
A tenant can be any model that implements the Stancl\Tenancy\Contracts\Tenant
interface. To tell the package to use a custom tenant model, set the tenancy.models.tenant
config key:
Default tenant model
By default, the package uses the Stancl\Tenancy\Database\Models\Tenant
model. Out of the box, it adds the following behavior:
VirtualColumn
: a trait from our VirtualColumn package- Forced central connection: so that you can interact with the tenant model even while connected to a tenant database (tenants are stored in the central database)
- ID generation logic: on creation, the tenant’s id is automatically filled with a UUID instead of using the default autoincrement behavior
TenantRun
+InitializationHelpers
: helper methods for running code in the tenant context, see below for details- Cached resolver invalidation logic: any change made to a tenant will invalidate all resolver cache for that tenant
- Some additional helpful methods and events, take a look at the code for details
Using a custom model
In most applications, you will want to use a custom tenant model. Usually, you’ll want to extend our base model to get the
features outlined above. That said, it isn’t needed to extend our base
model, and the Tenant
interface doesn’t have too many requirements, but implementing them manually can be time-consuming.
For instance, to use domain identification and multi-database, you’ll want to create a model like this:
Creating tenants
Since tenants are normal Eloquent models (with the exception of having the VirtualColumn
trait, letting you store any data
on them without having to add columns to migrations), you can create them as you’d expect:
Custom columns
To store some data in custom columns, i.e. not the JSON data
column, add the names of these columns to the array returned
by getCustomColumns()
:
This is especially useful for data you may query often, or columns you want to add indices on.
Running code in the tenant context
The TenantRun
trait lets you easily run code in a tenant context:
The run()
method safely reverts back to the previous context (central/another tenant) after the passed callback has finished
executing.
The InitializationHelpers
trait lets you use the enter()
and leave()
methods:
Accessing the current tenant
You may use the tenant()
helper to access the current tenant, if tenancy has been initialized:
If your model is based on the base Stancl\Tenancy\Database\Models\Tenant
model, you can also use the current()
and
currentOrFail()
methods:
Tenant keys
The default model and tenants
table migration use UUIDs for primary keys. The reason for this is that your tenant keys
must not be enumerable or derived from unsanitized user input. This is because:
- with some setups, you may want to avoid path enumeration attacks e.g. for tenant assets,
- tenant keys are used in raw queries e.g. for creating databases.
In the paragraphs above, we’ve used the term “tenant key”. Does that simply refer to the id? Yes by default, but not necessarily.
Similar to how Laravel lets you define separate keys to be used as model primary keys and keys for routing:
Tenancy lets you define a separate column to be used as the “tenant key”. Tenancy always uses the tenant key to interact with your models, so you may separate them from your primary keys:
Unique identifier generators
The only requirements for tenant keys is that they’re non-enumerable, unique, and not derived from unsanitized user input.
This means that randomly generated values work perfectly for this use case, which is why we use UUIDs (v4) out of the box.
However, UUIDs can be ugly and they’re excessively unique (long) for most applications. You won’t have so many tenants that you’d need a UUID. So while UUID is a good default, we let you customize how these values are generated.
To change this Unique Identifier Generator, update the tenancy.models.id_generator
config. Any class that implements the
Stancl\Tenancy\Contracts\UniqueIdentifierGenerator
trait can be used as a unique identifier generator.
Out of the box, the package ships with these generators:
Generator | Configurable | Unique combinations | Output length |
---|---|---|---|
UUIDGenerator | No | 2^122 | 36 |
RandomHexGenerator | Yes, $bytes property | 2^(8 * $bytes ) | 2 * $bytes |
RandomStringGenerator | Yes, $length property | 61^$length | $length |
With default configuration, these produce:
Generator | Configuration | Unique combinations | Sample output |
---|---|---|---|
UUIDGenerator | None | 2^122 | 01058b4a-9382-496e-a29c-cfdacf3dac2c |
RandomHexGenerator | 6 bytes | 281,474,976,710,656 | 2a0b3a915249 |
RandomStringGenerator | 8 characters | 191,707,312,997,281 | 5UYXaYFc |
RandomHexGenerator
This generator can be a good alternative to the UUID generator. It lets you exactly configure how many bytes you want your tenant keys to have. The resulting strings are only contain lowercase hexadecimal values (0-9, a-f).
Configuration:
Bytes | Unique combinations | String length | Sample string | |
---|---|---|---|---|
1 | 256 | 2.56E+02 | 2 | 0b |
2 | 65536 | 6.55E+04 | 4 | 0bd4 |
3 | 16777216 | 1.68E+07 | 6 | 0bd428 |
4 | 4294967296 | 4.29E+09 | 8 | 0bd428ff |
5 | 1099511627776 | 1.10E+12 | 10 | 0bd428ff1d |
6 | 281474976710656 | 2.81E+14 | 12 | 0bd428ff1d0a |
7 | 72057594037927900 | 7.21E+16 | 14 | 0bd428ff1d0a14 |
8 | 18446744073709600000 | 1.84E+19 | 16 | 0bd428ff1d0a1490 |
9 | 4722366482869650000000 | 4.72E+21 | 18 | 0bd428ff1d0a14905d |
10 | 1208925819614630000000000 | 1.21E+24 | 20 | 0bd428ff1d0a14905db7 |
11 | 309485009821345000000000000 | 3.09E+26 | 22 | 0bd428ff1d0a14905db770 |
12 | 79228162514264300000000000000 | 7.92E+28 | 24 | 0bd428ff1d0a14905db77098 |
13 | 20282409603651700000000000000000 | 2.03E+31 | 26 | 0bd428ff1d0a14905db77098b6 |
14 | 5192296858534830000000000000000000 | 5.19E+33 | 28 | 0bd428ff1d0a14905db77098b60e |
15 | 1329227995784920000000000000000000000 | 1.33E+36 | 30 | 0bd428ff1d0a14905db77098b60e62 |
16 | 340282366920938000000000000000000000000 | 3.40E+38 | 32 | 0bd428ff1d0a14905db77098b60e62cc |
17 | 87112285931760200000000000000000000000000 | 8.71E+40 | 34 | 0bd428ff1d0a14905db77098b60e62cc27 |
18 | 22300745198530600000000000000000000000000000 | 2.23E+43 | 36 | 0bd428ff1d0a14905db77098b60e62cc27d8 |
RandomStringGenerator
The random string uses Laravel’s Str::random()
, which produces essentially a stripped-down and URL safe version of base64
encoding random bytes.
There are 61 possible values per character, packing more bytes per character into the string than the hex generator. However, the strings include the entire alphabet including uppercase letters, which can look uglier than hex strings.
Configuration:
Length | Unique combinations | Sample string | |
---|---|---|---|
1 | 61 | 6.10E+01 | v |
2 | 3721 | 3.72E+03 | vD |
3 | 226981 | 2.27E+05 | vDw |
4 | 13845841 | 1.38E+07 | vDw8 |
5 | 844596301 | 8.45E+08 | vDw8d |
6 | 51520374361 | 5.15E+10 | vDw8dW |
7 | 3142742836021 | 3.14E+12 | vDw8dWQ |
8 | 191707312997281 | 1.92E+14 | vDw8dWQc |
9 | 11694146092834100 | 1.17E+16 | vDw8dWQcM |
10 | 713342911662883000 | 7.13E+17 | vDw8dWQcMP |
11 | 43513917611435800000 | 4.35E+19 | vDw8dWQcMPY |
12 | 2654348974297590000000 | 2.65E+21 | vDw8dWQcMPYz |
13 | 161915287432153000000000 | 1.62E+23 | vDw8dWQcMPYzc |
14 | 9876832533361320000000000 | 9.88E+24 | vDw8dWQcMPYzcc |
15 | 602486784535040000000000000 | 6.02E+26 | vDw8dWQcMPYzcc7 |
16 | 36751693856637500000000000000 | 3.68E+28 | vDw8dWQcMPYzcc7Q |
17 | 2241853325254890000000000000000 | 2.24E+30 | vDw8dWQcMPYzcc7QE |
18 | 136753052840548000000000000000000 | 1.37E+32 | vDw8dWQcMPYzcc7QE8 |
Internal keys
Tenancy stores some internal values on tenants, such as tenancy_db_name
.
This is the main reason behind using the VirtualColumn
trait: if we need more internal values in the future, you won’t have
to create new migrations. The values will simply get written to the data
JSON column.
To explicitly interact with internal values, you may use:
Events
Since much of the tenancy logic is event-based, the base model fires some dedicated events: