mirror of
https://github.com/stancl/tenancy-docs.git
synced 2025-12-12 18:24:03 +00:00
154 lines
7.1 KiB
Markdown
154 lines
7.1 KiB
Markdown
---
|
|
title: Tenancy bootstrappers
|
|
extends: _layouts.documentation
|
|
section: content
|
|
---
|
|
|
|
# Tenancy bootstrappers {#tenancy-bootstrappers}
|
|
|
|
Tenancy bootstrappers are classes which make your application tenant-aware in such a way that you don't have to change a line of your code, yet things will be scoped to the current tenant.
|
|
|
|
The package comes with these bootstrappers out of the box:
|
|
|
|
## Database tenancy bootstrapper {#database-tenancy-bootstrapper}
|
|
|
|
The database tenancy bootstrapper switches the **default** database connection to `tenant` after it constructs the connection for that tenant.
|
|
|
|
[Customizing databases]({{ $page->link('customizing-databases') }})
|
|
|
|
Note that only the **default** connection is switched. If you use another connection explicitly, be it using `DB::connection('...')`, a model `getConnectionName()` method, or a model trait like `CentralConnection`, **it will be respected.** The bootstrapper doesn't **force** any connections, it merely switches the default one.
|
|
|
|
## Cache tenancy bootstrapper {#cache-tenancy-bootstrapper}
|
|
|
|
The cache tenancy bootstrapper replaces the Laravel's CacheManager instance with a custom CacheManager that adds tags with the current tenant's ids to each cache call. This scopes cache calls and lets you selectively clear tenants' caches:
|
|
|
|
```php
|
|
php artisan cache:clear --tag=tenant_123
|
|
```
|
|
|
|
Note that you must use a cache store that supports tagging, e.g. Redis.
|
|
|
|
## Filesystem tenancy bootstrapper {#filesystem-tenancy-boostrapper}
|
|
The filesystem bootstrapper makes your app's `storage_path()` and `asset()` helper and the `Storage` facade tenant-aware by modifying the paths they retrieve.
|
|
|
|
> Note: If you want to bootstrap filesystem tenancy differently (e.g. provision an S3 bucket for each tenant), you can absolutely do that. Take a look at the package's bootstrappers to get an idea of how to write one yourself, and feel free to implement it any way you want.
|
|
|
|
### Storage path helper
|
|
|
|
The bootstrapper suffixes the path retrieved by `storage_path()` to make the helper tenant-aware.
|
|
|
|
- The suffix is `config('tenancy.filesystem.suffix_base') . $tenantKey`
|
|
- The suffix base is 'tenant' by default, but feel free to change it in the tenancy config
|
|
- After suffixing the `storage_path()` helper, it returns `"/$path_to_your_application/storage/$suffix/"`
|
|
|
|
Since `storage_path()` will be suffixed, your folder structure will look like this:
|
|
|
|

|
|
|
|
Logs will be saved in `storage/logs` regardless of any changes to `storage_path()` and regardless of the tenant.
|
|
|
|
### Storage facade
|
|
|
|
The bootstrapper also makes the `Storage` facade tenant-aware by suffixing the roots of disks listed in `config('tenancy.filesystem.disks')` and by overriding the roots in `config('tenancy.filesystem.root_override')`.
|
|
|
|
The root of each disk listed in `config('tenancy.filesystem.disks')` will be suffixed. Doing that alone could cause unwanted behavior since Laravel does its own suffixing, so the filesystem config has the `root_override` section, which lets you override the disk roots **after** tenancy has been initialized:
|
|
|
|
```php
|
|
// Tenancy config (tenancy.filesystem.root_override)
|
|
// %storage_path% gets replaced by storage_path()'s output
|
|
// E.g. Storage::disk('local')->path('') will return "/$path_to_your_application/storage/$suffix/app"
|
|
'root_override' => [
|
|
'local' => '%storage_path%/app/',
|
|
'public' => '%storage_path%/app/public/',
|
|
],
|
|
```
|
|
|
|
To make the tenant-aware `Storage` facade work with a custom disk, add the disk's name to `config('tenancy.filesystem.disks')` and override its root in `config('tenancy.filesystem.root_override')` as shown above.
|
|
|
|
### Assets
|
|
|
|
The bootstrapper modifies the links retrieved by the `asset()` helper, so they link to the files *of the currently initialized and identified tenant*.
|
|
|
|
Before using the `asset()` helper, make sure to [assign the identification middleware you're using in your app to TenantAssetsController's `$tenancyMiddleware`]({{ $page->link('configuration#static-properties') }}):
|
|
|
|
```php
|
|
// TenancyServiceProvider (don't forget to import the classes)
|
|
|
|
public function boot()
|
|
{
|
|
// Update the middleware used by the asset controller
|
|
TenantAssetsController::$tenancyMiddleware = InitializeTenancyByDomainOrSubdomain::class;
|
|
}
|
|
```
|
|
|
|
> Note: You can disable tenancy of `asset()` in the config (`tenancy.filesystem.asset_helper_tenancy`) and explicitly use `tenant_asset()` instead. You may want to do that if you're facing issues using a package that utilizes `asset()` inside the tenant app.
|
|
>
|
|
> For non-tenant-specific assets (assets shared among all tenants or JS/CSS assets), you can use `global_asset()` and `mix()`.
|
|
|
|
If `config('app.asset_url')` has been set, the bootstrapper suffixes the configured asset URL the same way as `storage_path()` (useful if you're using Laravel Vapor or similar).
|
|
|
|
If `config('app.asset_url')` is null (as it is by default), `asset()` will return a URL pointing to the TenantAssetsController (`/tenancy/assets/...`) which returns a file response:
|
|
|
|
```php
|
|
// TenantAssetsController
|
|
return response()->file(storage_path('app/public/' . $path));
|
|
```
|
|
|
|
When `config('app.asset_url')` is not set, **you need to store the assets in your tenant's `app/public/` directory**. So for non-private (global) assets, you may want to create a disk and use URLs from that disk instead (don't add that disk to `config(tenancy.filesystem.disks)`). For example:
|
|
|
|
```php
|
|
Storage::disk('app-public')->url('tenants/logos/' . tenant()->id . '.png');
|
|
```
|
|
|
|
## Queue tenancy bootstrapper {#queue-tenancy-bootstrapper}
|
|
|
|
This bootstrapper adds the current tenant's ID to the queued job payloads, and initializes tenancy based on this ID when jobs are being processed.
|
|
|
|
You can read more about this on the *Queues* page:
|
|
|
|
[Queues]({{ $page->link('queues') }})
|
|
|
|
## Redis tenancy bootstrapper {#redis-tenancy-bootstrapper}
|
|
|
|
If you're using `Redis` calls (not cache calls, **direct** Redis calls) inside the tenant app, you will want to scope Redis data too. To do this, use this bootstrapper. It changes the Redis prefix for each tenant.
|
|
|
|
Note that you need phpredis, predis won't work.
|
|
|
|
## Writing custom bootstrappers {#writing-custom-bootstrappers}
|
|
|
|
If you want to bootstrap tenancy for something not covered by this package — or something covered by this package, but you want different behavior — you can do that by creating a bootstrapper class.
|
|
|
|
The class must implement the `Stancl\Tenancy\Contracts\TenancyBootstrapper` interface:
|
|
|
|
```php
|
|
namespace App;
|
|
|
|
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
|
use Stancl\Tenancy\Contracts\Tenant;
|
|
|
|
class MyBootstrapper implements TenancyBootstrapper
|
|
{
|
|
public function bootstrap(Tenant $tenant)
|
|
{
|
|
// ...
|
|
}
|
|
|
|
public function revert()
|
|
{
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
Then, register it in the `tenancy.bootstrappers` config:
|
|
|
|
```php
|
|
'bootstrappers' => [
|
|
Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper::class,
|
|
Stancl\Tenancy\Bootstrappers\CacheTenancyBootstrapper::class,
|
|
Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper::class,
|
|
Stancl\Tenancy\Bootstrappers\QueueTenancyBootstrapper::class,
|
|
|
|
App\MyBootstrapper::class,
|
|
],
|
|
```
|