Configuration
Our package is configured in three ways:
- The configuration file (
config/tenancy.php) - Static properties on various classes
- Event listeners: covered separately on the Event system page.
The configuration file
Section titled “The configuration file”The configuration file is thoroughly annotated and comes with good defaults for multi-database applications.
Individual pages of this documentation cover the main parts of the configuration as well as common changes.
You can find an online version of the configuration file here.
Static properties
Section titled “Static properties”Many classes in our package are configurable via static properties. These are used for more niche use cases that would clutter up our config, but should still be configurable.
We want the package to be as configurable as possible, and setting public static properties is much more ergonomic than having to extend classes and replace container bindings.
There are too many static properties to fully document them all here, however an auto-generated (though possibly outdated) index is included below. It only includes the properties and their docblocks, so if you need more context than the name of the property or the docblock, it is recommended to simply take a look at the class to see how the property is being used.
That’s the best way to discover these static properties anyway. Whenever you’re using a some class from the package directly and wish to customize something, there’s a good chance there’s a static property for configuring what you need. Most classes are not too complicated on their own, so source diving shouldn’t be difficult.
As of the time of writing, there are 45 configurable static properties in the codebase. Some are documented on specific pages of the documentation.
To change static properties, use this syntax in any service provider (it’s recommended to use the TenancyServiceProvider to keep all tenancy-related logic in one place):
public function boot(){ ClassName::$propertyName = 'value';
// ...}Static property index
Section titled “Static property index”/*** Closures overriding broadcasters with custom broadcasters that prefix the channel names with the tenant keys.** The key is the broadcaster's name, and the value is a closure that should prefix the broadcaster's channels.* $broadcasterOverrides['custom'] = fn () => ...; // Custom override closure** For more info, see the default override methods in this class (pusher() and ably()).*/public static array $broadcasterOverrides = [];/*** Tenant properties to be mapped to config (similarly to the TenantConfig feature).** For example:* [* 'config.key.name' => 'tenant_property',* ]*/public static array $credentialsMap = [];
public static string|null $broadcaster = null;
public static array $mapPresets = [/* ... */];public static string $cacheManagerWithTags = \Stancl\Tenancy\Overrides\CacheManager::class;/** @var Closure(Tenant, string): string */public static Closure|null $prefixGenerator = null;/*** Cache stores to scope.** If null, all cache stores that use the database driver will be scoped.* If an array, only the specified stores will be scoped. These all must use the database driver.*/public static array|null $stores = null;
/*** Should scoped stores be adjusted on the global cache manager to use the central connection.** You may want to set this to false if you don't use the built-in global cache and instead provide* a list of stores to scope (static::$stores), with your own global store excluded that you then* use manually. But in such a scenario you likely wouldn't be using global cache at all which means* the callbacks for adjusting it wouldn't be executed in the first place.*/public static bool $adjustGlobalCacheManager = true;/*** Fortify redirects that should be used in tenant context.** Syntax: ['redirect_name' => 'tenant_route_name']*/public static array $fortifyRedirectMap = [];
/*** Should the tenant parameter be passed to fortify routes in the tenant context.** This should be enabled with path/query string identification and disabled with domain identification.** You may also disable this when using path/query string identification if passing the tenant parameter* is handled in another way (TenancyUrlGenerator::$passTenantParameter for both,* UrlGeneratorBootstrapper:$addTenantParameterToDefaults for path identification).*/public static bool $passTenantParameter = false;
/*** Tenant route that serves as Fortify's home (e.g. a tenant dashboard route).* This route will always receive the tenant parameter.*/public static string|null $fortifyHome = 'tenant.dashboard';
/*** Follow the query_parameter config instead of the tenant_parameter_name (path identification) config.** This only has an effect when:* - $passTenantParameter is enabled, and* - the tenant_parameter_name config for the path resolver differs from the query_parameter config for the request data resolver.** In such a case, instead of adding ['tenant' => '...'] to the route parameters (or whatever your tenant_parameter_name is if not 'tenant'),* the query_parameter will be passed instead, e.g. ['team' => '...'] if your query_parameter config is 'team'.** This is enabled by default because typically you will not need $passTenantParameter with path identification.* UrlGeneratorBootstrapper::$addTenantParameterToDefaults is recommended instead when using path identification.** On the other hand, when using request data identification (specifically query string) you WILL need to* pass the parameter therefore you would use $passTenantParameter.*/public static bool $passQueryParameter = true;/*** Tenant properties to be mapped to config (similarly to the TenantConfig feature).** For example:* [* 'config.key.username' => 'tenant_property',* 'config.key.password' => 'nested.tenant_property',* ]*/public static array $credentialsMap = [];
public static string|null $mailer = null;
public static array $mapPresets = [/* ... */];/*** A closure that accepts the tenant and the original root URL and returns the new root URL.* When null, the root URL is not altered in any way.** We recommend setting this property in the TenancyServiceProvider's overrideUrlInTenantContext() method.*/public static Closure|null $rootUrlOverride = null;
/*** Overriding the root url may cause issues in *some* tests, so you can disable* the behavior by setting this property to false.*/public static bool $rootUrlOverrideInTests = true;/*** Should the tenant route parameter get added to TenancyUrlGenerator::defaults().** This is recommended when using path identification since defaults() generally has better support in integrations,* namely Ziggy, compared to TenancyUrlGenerator::$passTenantParameterToRoutes.** With query string identification, this has no effect since URL::defaults() only works for route paramaters.*/public static bool $addTenantParameterToDefaults = true;/*** Force, rather than just enable, the created RLS policies.** By default, table owners bypass RLS policies. When this is enabled,* they also need the BYPASSRLS permission. If your setup lets you create* a user with BYPASSRLS, you may prefer leaving this on for additional* safety. Otherwise, if you can't use BYPASSRLS, you can set this to false* and depend on the behavior of table owners bypassing RLS automatically.** This setting generally doesn't affect behavior at all with "default"* setups, however if you have a more custom setup, with additional users* involved (e.g. central connection user not being the same user that* creates tables, or the created "RLS user" creating some tables) you* should take care with how you configure this.*/public static bool $forceRls = true;/*** Used for adding custom headers to the response.** @var (Closure(Request): array)|array*/public static Closure|array $headers = [];
/*** Additional middleware to be used on the route to this controller.** @var array<string>*/public static array $middleware = [];public static string $pendingSinceCast = 'timestamp';/*** Database username generator (can be set by the developer.).** @var Closure(Model&Tenant, self): string*/public static Closure $usernameGenerator;
/*** Database password generator (can be set by the developer.).** @var Closure(Model&Tenant, self): string*/public static Closure $passwordGenerator;
/*** Database name generator (can be set by the developer.).** @var Closure(Model&Tenant, self): string*/public static Closure $databaseNameGenerator;/** You can set this property to customize the table name */public static string $tableName = 'tenant_user_impersonation_tokens';/** @var string[] */public static array $grants = [/* ... */];/** @var string[] */public static array $grants = [/* ... */];/*** SQLite database directory path.** Defaults to database_path().*/public static string|null $path = null;public static string|false|null $extensionPath = null;/** @var array<string, string|array> */public static array $storageToConfigMap = [/* ... */];/** The lifespan of impersonation tokens (in seconds). */public static int $ttl = 60;public static bool $ignoreExisting = false;public static bool $shouldQueue = false;public static ?Closure $onFail = null;public static ?Closure $onFail = null;public static ?Closure $onFail = null;public static ?Closure $onFail = null;
public static bool $requireCookieEncryption = false;/*** The index of the subdomain fragment in the hostname* split by `.`. 0 for first fragment, 1 if you prefix* your subdomain fragments with `www`.** @var int*/public static $subdomainIndex = 0;
public static ?Closure $onFail = null;/*** Set this property if you want to customize the on-fail behavior.*/public static ?Closure $abortRequest;public static string $tenantIdKey = '_tenant_id';
/** @var Closure(Request): mixed */public static Closure|null $onFail = null;/*** Names of broadcasters to always recreate using $this->resolve() (even when they're* cached and available in the $broadcasters property).** The reason for recreating the broadcasters is* to make your app use the correct broadcaster credentials when tenancy is initialized.*/public static array $tenantBroadcasters = ['pusher', 'ably'];/*** Parameter which works as a flag for bypassing the behavior modification of route() and temporarySignedRoute().** For example, in tenant context:* Route::get('/', ...)->name('home');* // query string identification* Route::get('/tenant', ...)->middleware(InitializeTenancyByRequestData::class)->name('tenant.home');* - route('home') => app.test/tenant?tenant=tenantKey* - route('home', [$bypassParameter => true]) => app.test/* - route('tenant.home', [$bypassParameter => true]) => app.test/tenant -- no query string added** Note: UrlGeneratorBootstrapper::$addTenantParameterToDefaults is not affected by this, though* it doesn't matter since it doesn't pass any extra parameters when not needed.** @see UrlGeneratorBootstrapper*/public static string $bypassParameter = 'central';
/*** Should route names passed to route() or temporarySignedRoute()* get prefixed with the tenant route name prefix.** This is useful when using e.g. path identification with third-party packages* where you don't have control over all route() calls or don't want to change* too many files. Often this will be when using route cloning.*/public static bool $prefixRouteNames = false;
/*** Should the tenant parameter be passed to route() or temporarySignedRoute() calls.** This is useful with path or query parameter identification. The former can be handled* more elegantly using UrlGeneratorBootstrapper::$addTenantParameterToDefaults.** @see UrlGeneratorBootstrapper*/public static bool $passTenantParameterToRoutes = false;
/*** Route name overrides.** Note: This behavior can be bypassed using $bypassParameter just like* $prefixRouteNames and $passTenantParameterToRoutes.** Example from a Jetstream integration:* [* 'profile.show' => 'tenant.profile.show',* 'two-factor.login' => 'tenant.two-factor.login',* ]** In the tenant context:* - `route('profile.show')` will return a URL as if you called `route('tenant.profile.show')`.* - `route('profile.show', ['central' => true])` will return a URL as if you called `route('profile.show')`.*/public static array $overrides = [];
/*** Follow the query_parameter config instead of the tenant_parameter_name (path identification) config.** This only has an effect when:* - $passTenantParameterToRoutes is enabled, and* - the tenant_parameter_name config for the path resolver differs from the query_parameter config for the request data resolver.** In such a case, instead of adding ['tenant' => '...'] to the route parameters (or whatever your tenant_parameter_name is if not 'tenant'),* the query_parameter will be passed instead, e.g. ['team' => '...'] if your query_parameter config is 'team'.** This is enabled by default because typically you will not need $passTenantParameterToRoutes with path identification.* UrlGeneratorBootstrapper::$addTenantParameterToDefaults is recommended instead when using path identification.** On the other hand, when using request data identification (specifically query string) you WILL need to pass the parameter* directly to route() calls, therefore you would use $passTenantParameterToRoutes to avoid having to do that manually.*/public static bool $passQueryParameter = true;/*** When true, all valid constraints are considered while generating paths for RLS policies,* unless explicitly marked with a 'no-rls' comment.** When false, only columns explicitly marked with 'rls' or 'rls table.column' comments are considered.*/public static bool $scopeByDefault = true;/** @var Closure(): array<Model> */public static Closure|null $modelDiscoveryOverride = null;
/*** Directories in which the manager will discover your models.* Subdirectories of the specified directories are also scanned.** For example, specifying 'app/Models' will discover all models in the 'app/Models' directory and all of its subdirectories.* Specifying 'app/Models/*' will discover all models in the subdirectories of 'app/Models' (+ their subdirectories),* but not the models present directly in the 'app/Models' directory.*/public static array $modelDirectories = ['app/Models'];
/*** Scope queries of all tenant models using RLS by default.** To use RLS scoping only for some models, you can keep this disabled and* make the models of your choice implement the RLSModel interface.*/public static bool $implicitRLS = false;
/** @var array<class-string<Model>> */public static array $excludedModels = [];/** The model representing the domain that the tenant was identified on. */public static Domain|null $currentDomain = null;public static bool $shouldCache = false;
public static int $cacheTTL = 3600; // seconds
public static string|null $cacheStore = null; // defaultpublic static bool $shouldQueue = false;public static bool $shouldQueue = false;public static bool $shouldQueue = false;public static bool $shouldQueue = false;public static bool $shouldQueue = false;
/*** This static property allows you to scope the "get model query"* that's responsible for finding the resources that should get synced (in the getModel() method).** For example, to include soft deleted records while syncing (excluded by default), you can use this closure:** UpdateOrCreateSyncedResource::$scopeGetModelQuery = function (Builder $query) {* if ($query->hasMacro('withTrashed')) {* $query->withTrashed();* }* };*/public static Closure|null $scopeGetModelQuery = null;/*** List of relations to eager load when fetching a tenant via tenancy()->find().*/public static array $findWith = [];public static Closure|null $configure = null;
public static bool $registerForgetTenantParameterListener = true;
public static bool $migrateFreshOverride = true;public static int $bytes = 6;public static int $min = 0;
public static int $max = PHP_INT_MAX;public static int $length = 8;