Skip to content

Configuration

Our package is configured in three ways:

  1. The configuration file (config/tenancy.php)
  2. Static properties on various classes
  3. Event listeners: covered separately on the Event system page.

The configuration file

Models section

'tenant' => Stancl\Tenancy\Database\Models\Tenant::class,
'domain' => Stancl\Tenancy\Database\Models\Domain::class,
/**
* Name of the column used to relate models to tenants.
*
* This is used by the HasDomains trait, and models that use the BelongsToTenant trait (used in single-database tenancy).
*/
'tenant_key_column' => 'tenant_id',
/**
* Used for generating tenant IDs.
*
* - Feel free to override this with a custom class that implements the UniqueIdentifierGenerator interface.
* - To use autoincrement IDs, set this to null and update the `tenants` table migration to use an autoincrement column.
*
* SECURITY NOTE: Keep in mind that autoincrement IDs come with potential enumeration issues (such as tenant storage URLs).
*
* @see \Stancl\Tenancy\UniqueIdentifierGenerators\UUIDGenerator
* @see \Stancl\Tenancy\UniqueIdentifierGenerators\RandomHexGenerator
* @see \Stancl\Tenancy\UniqueIdentifierGenerators\RandomStringGenerator
*/
'id_generator' => UniqueIdentifierGenerators\UUIDGenerator::class,

Identification section

/**
* The list of domains hosting your central app.
*
* Only relevant if you're using the domain or subdomain identification middleware.
*/
'central_domains' => [
str(env('APP_URL'))->after('://')->before('/')->toString(),
],
/**
* The default middleware used for tenant identification.
*
* If you use multiple forms of identification, you can set this to the "main" approach you use.
*/
'default_middleware' => Middleware\InitializeTenancyByDomain::class,
/**
* All of the identification middleware used by the package.
*
* If you write your own, make sure to add them to this array.
*/
'middleware' => [
Middleware\InitializeTenancyByDomain::class,
Middleware\InitializeTenancyBySubdomain::class,
Middleware\InitializeTenancyByDomainOrSubdomain::class,
Middleware\InitializeTenancyByPath::class,
Middleware\InitializeTenancyByRequestData::class,
Middleware\InitializeTenancyByOriginHeader::class,
],
/**
* Identification middleware tenancy recognizes as domain identification middleware.
*
* This is used for determining whether to skip the access prevention middleware.
* PreventAccessFromUnwantedDomains is intended to be used only with the middleware included here.
* It will get skipped if it's used with other identification middleware.
*
* If you're using a custom domain identification middleware, add it here.
*
* @see \Stancl\Tenancy\Concerns\UsableWithEarlyIdentification
* @see \Stancl\Tenancy\Middleware\PreventAccessFromUnwantedDomains
*/
'domain_identification_middleware' => [
Middleware\InitializeTenancyByDomain::class,
Middleware\InitializeTenancyBySubdomain::class,
Middleware\InitializeTenancyByDomainOrSubdomain::class,
],
'path_identification_middleware' => [
Middleware\InitializeTenancyByPath::class,
],
/**
* Tenant resolvers used by the package.
*
* Resolvers which implement the CachedTenantResolver contract have options for configuring the caching details.
* If you add your own resolvers, do not add the 'cache' key unless your resolver is based on CachedTenantResolver.
*/
'resolvers' => [
Resolvers\DomainTenantResolver::class => [
'cache' => false,
'cache_ttl' => 3600, // seconds
'cache_store' => null, // null = default
],
Resolvers\PathTenantResolver::class => [
'tenant_parameter_name' => 'tenant',
'tenant_model_column' => null, // null = tenant key
'allowed_extra_model_columns' => [], // used with binding route fields
'cache' => false,
'cache_ttl' => 3600, // seconds
'cache_store' => null, // null = default
],
Resolvers\RequestDataTenantResolver::class => [
'cache' => false,
'cache_ttl' => 3600, // seconds
'cache_store' => null, // null = default
],
],

Bootstrappers section

// Basic Laravel features
Bootstrappers\DatabaseTenancyBootstrapper::class,
Bootstrappers\CacheTenancyBootstrapper::class,
// Bootstrappers\CacheTagsBootstrapper::class, // Alternative to CacheTenancyBootstrapper
Bootstrappers\FilesystemTenancyBootstrapper::class,
Bootstrappers\QueueTenancyBootstrapper::class,
// Bootstrappers\RedisTenancyBootstrapper::class, // Note: phpredis is needed
// Support for edge cases
Bootstrappers\DatabaseSessionBootstrapper::class,
Bootstrappers\JobBatchBootstrapper::class,
// Configurable bootstrappers
// Bootstrappers\RootUrlBootstrapper::class,
// Bootstrappers\UrlGeneratorBootstrapper::class,
// Bootstrappers\MailConfigBootstrapper::class, // Note: Queueing mail requires using QueueTenancyBootstrapper with $forceRefresh set to true
// Bootstrappers\BroadcastingConfigBootstrapper::class,
// Bootstrappers\BroadcastChannelPrefixBootstrapper::class,
// Integration bootstrappers
// Bootstrappers\Integrations\FortifyRouteBootstrapper::class,
// Bootstrappers\Integrations\ScoutPrefixBootstrapper::class,

Database section

'central_connection' => env('DB_CONNECTION', 'central'),
/**
* Connection used as a "template" for the dynamically created tenant database connection.
* Note: don't name your template connection tenant. That name is reserved by package.
*/
'template_tenant_connection' => null,
/**
* The name of the temporary connection used for creating and deleting tenant databases.
*/
'tenant_host_connection_name' => 'tenant_host_connection',
/**
* Tenant database names are created like this:
* prefix + tenant_id + suffix.
*/
'prefix' => 'tenant',
'suffix' => '',
/**
* TenantDatabaseManagers are classes that handle the creation & deletion of tenant databases.
*/
'managers' => [
'sqlite' => Stancl\Tenancy\Database\TenantDatabaseManagers\SQLiteDatabaseManager::class,
'mysql' => Stancl\Tenancy\Database\TenantDatabaseManagers\MySQLDatabaseManager::class,
'pgsql' => Stancl\Tenancy\Database\TenantDatabaseManagers\PostgreSQLDatabaseManager::class,
'sqlsrv' => Stancl\Tenancy\Database\TenantDatabaseManagers\MicrosoftSQLDatabaseManager::class,
/**
* Use these database managers to have a DB user created for each tenant database.
* You can customize the grants given to these users by changing the $grants property.
*/
// 'mysql' => Stancl\Tenancy\Database\TenantDatabaseManagers\PermissionControlledMySQLDatabaseManager::class,
// 'sqlsrv' => Stancl\Tenancy\TenantDatabaseManagers\PermissionControlledMicrosoftSQLServerDatabaseManager::class,
/**
* Disable the pgsql manager above, and enable the one below if you
* want to separate tenant DBs by schemas rather than databases.
*/
// 'pgsql' => Stancl\Tenancy\Database\TenantDatabaseManagers\PostgreSQLSchemaManager::class, // Separate by schema instead of database
],
// todo@docblock
'drop_tenant_databases_on_migrate_fresh' => false,

Cache section

'prefix' => 'tenant_%tenant%_', // This format, with the %tenant% replaced by the tenant key, and prepended by the original store prefix, will form a cache prefix that will be used for every cache key.
'stores' => [
env('CACHE_STORE'),
],
/*
* Should sessions be tenant-aware (only used when your session driver is cache-based).
*
* Note: This will implicitly add your configured session store to the list of prefixed stores above.
*/
'scope_sessions' => true,
'tag_base' => 'tenant', // This tag_base, followed by the tenant_id, will form a tag that will be applied on each cache call.

Filesystem section

/**
* Each disk listed in the 'disks' array will be suffixed by the suffix_base, followed by the tenant_id.
*/
'suffix_base' => 'tenant',
'disks' => [
'local',
'public',
// 's3',
],
/**
* Use this for local disks.
*
* See https://tenancyforlaravel.com/docs/v3/tenancy-bootstrappers/#filesystem-tenancy-boostrapper
*/
'root_override' => [
// Disks whose roots should be overridden after storage_path() is suffixed.
'local' => '%storage_path%/app/',
'public' => '%storage_path%/app/public/',
],
/*
* Tenant-aware Storage::disk()->url() can be enabled for specific local disks here
* by mapping the disk's name to a name with '%tenant%' (this will be used as the public name of the disk).
* Doing that will override the disk's default URL with a URL containing the current tenant's key.
*
* For example, Storage::disk('public')->url('') will return https://your-app.test/storage/ by default.
* After adding 'public' => 'public-%tenant%' to 'url_override',
* the returned URL will be https://your-app.test/public-1/ (%tenant% gets substitued by the current tenant's key).
*
* Use `php artisan tenants:link` to create a symbolic link from the tenant's storage to its public directory.
*/
'url_override' => [
// Note that the local disk you add must exist in the tenancy.filesystem.root_override config
// todo@v4 Rename url_override to something that describes the config key better
'public' => 'public-%tenant%',
],
/*
* Should the `file` cache driver be tenant-aware.
*
* When this is enabled, cache files will be stored in storage/{tenant}/framework/cache.
*/
'scope_cache' => true,
/*
* Should the `file` session driver be tenant-aware.
*
* When this is enabled, session files will be stored in storage/{tenant}/framework/sessions.
*/
'scope_sessions' => true,
/**
* Should storage_path() be suffixed.
*
* Note: Disabling this will likely break local disk tenancy. Only disable this if you're using an external file storage service like S3.
*
* For the vast majority of applications, this feature should be enabled. But in some
* edge cases, it can cause issues (like using Passport with Vapor - see #196), so
* you may want to disable this if you are experiencing these edge case issues.
*/
'suffix_storage_path' => true,
/**
* Setting this to true makes asset() calls multi-tenant. You can use global_asset() and mix()
* for global, non-tenant-specific assets. However, you might have some issues when using
* packages that use asset() calls inside the tenant app. To avoid such issues, you can
* leave asset() helper tenancy disabled and explicitly use tenant_asset() calls in places
* where you want to use tenant-specific assets (product images, avatars, etc).
*/
'asset_helper_tenancy' => false, // todo@rename asset_helper_override?

Redis section

'prefix' => 'tenant_%tenant%_', // Each key in Redis will be prepended by this prefix format, with %tenant% replaced by the tenant key.
'prefixed_connections' => [ // Redis connections whose keys are prefixed, to separate one tenant's keys from another.
'default',
// 'cache', // Enable this if you want to scope cache using RedisTenancyBootstrapper
],

Features section

In this section, you can enable Feature classes.

// Stancl\Tenancy\Features\UserImpersonation::class,
// Stancl\Tenancy\Features\TelescopeTags::class,
// Stancl\Tenancy\Features\TenantConfig::class,
// Stancl\Tenancy\Features\CrossDomainRedirect::class,
// Stancl\Tenancy\Features\ViteBundler::class,

Misc settings

This is the final part of the config file. These keys are top-level (not part of a specific section/array).

This section includes various unrelated keys.

/**
* Should tenancy routes be registered.
*
* Tenancy routes include tenant asset routes. By default, this route is
* enabled. But it may be useful to disable them if you use external
* storage (e.g. S3 / Dropbox) or have a custom asset controller.
*/
'routes' => true,
/**
* Make all routes central, tenant, or universal by default.
*
* To override the default route mode, apply the middleware of another route mode ('central', 'tenant', 'universal') to the route.
*/
'default_route_mode' => RouteMode::CENTRAL,
/**
* Pending tenants config.
* This is useful if you're looking for a way to always have a tenant ready to be used.
*/
'pending' => [
/**
* If disabled, pending tenants will be excluded from all tenant queries.
* You can still use ::withPending(), ::withoutPending() and ::onlyPending() to include or exclude the pending tenants regardless of this setting.
* Note: when disabled, this will also ignore pending tenants when running the tenant commands (migration, seed, etc.)
*/
'include_in_queries' => true,
/**
* Defines how many pending tenants you want to have ready in the pending tenant pool.
* This depends on the volume of tenants you're creating.
*/
'count' => env('TENANCY_PENDING_COUNT', 5),
],
/**
* Parameters used by the tenants:migrate command.
*/
'migration_parameters' => [
'--force' => true, // This needs to be true to run migrations in production.
'--path' => [database_path('migrations/tenant')],
'--schema-path' => database_path('schema/tenant-schema.dump'),
'--realpath' => true,
],
/**
* Parameters used by the tenants:seed command.
*/
'seeder_parameters' => [
'--class' => 'Database\Seeders\DatabaseSeeder', // root seeder class
// '--force' => true,
],

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 document them all here, so you should discover these by source diving the package. 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 56 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):

src/Providers/TenancyServiceProvider.php
public function boot()
{
ClassName::$propertyName = 'value';
// ...
}