mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-12 10:54:04 +00:00
Add identification section to config, refactor static properties
This commit is contained in:
parent
e5bc8ddb77
commit
ccaba05272
17 changed files with 153 additions and 97 deletions
|
|
@ -144,16 +144,8 @@ class TenancyServiceProvider extends ServiceProvider
|
|||
|
||||
protected function makeTenancyMiddlewareHighestPriority()
|
||||
{
|
||||
$tenancyMiddleware = [
|
||||
// Even higher priority than the initialization middleware
|
||||
Middleware\PreventAccessFromCentralDomains::class,
|
||||
|
||||
Middleware\InitializeTenancyByDomain::class,
|
||||
Middleware\InitializeTenancyBySubdomain::class,
|
||||
Middleware\InitializeTenancyByDomainOrSubdomain::class,
|
||||
Middleware\InitializeTenancyByPath::class,
|
||||
Middleware\InitializeTenancyByRequestData::class,
|
||||
];
|
||||
$tenancyMiddleware = array_merge([Middleware\PreventAccessFromCentralDomains::class], config('tenancy.identification.middleware'));
|
||||
|
||||
foreach (array_reverse($tenancyMiddleware) as $middleware) {
|
||||
$this->app[\Illuminate\Contracts\Http\Kernel::class]->prependToMiddlewarePriority($middleware);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
|||
|
||||
use Stancl\Tenancy\Database\Models\Domain;
|
||||
use Stancl\Tenancy\Database\Models\Tenant;
|
||||
use Stancl\Tenancy\Middleware;
|
||||
use Stancl\Tenancy\Resolvers;
|
||||
|
||||
return [
|
||||
'tenant_model' => Tenant::class,
|
||||
|
|
@ -21,6 +23,56 @@ return [
|
|||
'localhost',
|
||||
],
|
||||
|
||||
'identification' => [
|
||||
/**
|
||||
* 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,// todo@identification add this to a 'tenancy' mw group
|
||||
|
||||
/**
|
||||
* 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,
|
||||
],
|
||||
|
||||
/**
|
||||
* 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, // default
|
||||
],
|
||||
Resolvers\PathTenantResolver::class => [
|
||||
'tenant_parameter_name' => 'tenant',
|
||||
|
||||
'cache' => false,
|
||||
'cache_ttl' => 3600, // seconds
|
||||
'cache_store' => null, // default
|
||||
],
|
||||
Resolvers\RequestDataTenantResolver::class => [
|
||||
'cache' => false,
|
||||
'cache_ttl' => 3600, // seconds
|
||||
'cache_store' => null, // default
|
||||
],
|
||||
],
|
||||
|
||||
// todo@docs update integration guides to use Stancl\Tenancy::defaultMiddleware()
|
||||
],
|
||||
|
||||
/**
|
||||
* Tenancy bootstrappers are executed when tenancy is initialized.
|
||||
* Their responsibility is making Laravel features tenant-aware.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Stancl\Tenancy\Controllers\TenantAssetController;
|
||||
|
||||
Route::get('/tenancy/assets/{path?}', 'Stancl\Tenancy\Controllers\TenantAssetsController@asset')
|
||||
Route::get('/tenancy/assets/{path?}', [TenantAssetController::class, 'asset'])
|
||||
->where('path', '(.*)')
|
||||
->name('stancl.tenancy.asset');
|
||||
|
|
|
|||
|
|
@ -4,18 +4,16 @@ declare(strict_types=1);
|
|||
|
||||
namespace Stancl\Tenancy\Controllers;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Stancl\Tenancy\Tenancy;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Throwable;
|
||||
|
||||
class TenantAssetsController extends Controller // todo rename this to TenantAssetController & update references in docs
|
||||
class TenantAssetController extends Controller // todo@docs this was renamed from TenantAssetsController
|
||||
{
|
||||
public static string|array|Closure $tenancyMiddleware = \Stancl\Tenancy\Middleware\InitializeTenancyByDomain::class;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware(static::$tenancyMiddleware);
|
||||
$this->middleware(Tenancy::defaultMiddleware());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -5,22 +5,15 @@ declare(strict_types=1);
|
|||
namespace Stancl\Tenancy\Database\Concerns;
|
||||
|
||||
use Stancl\Tenancy\Contracts\Tenant;
|
||||
use Stancl\Tenancy\Resolvers;
|
||||
use Stancl\Tenancy\Resolvers\Contracts\CachedTenantResolver;
|
||||
use Stancl\Tenancy\Tenancy;
|
||||
|
||||
trait InvalidatesResolverCache
|
||||
{
|
||||
/** @var array<class-string<CachedTenantResolver>> */
|
||||
public static $resolvers = [ // todo@deprecated, move this to a config key? related to a todo in InvalidatesTenantsResolverCache
|
||||
Resolvers\DomainTenantResolver::class,
|
||||
Resolvers\PathTenantResolver::class,
|
||||
Resolvers\RequestDataTenantResolver::class,
|
||||
];
|
||||
|
||||
public static function bootInvalidatesResolverCache(): void
|
||||
{
|
||||
static::saved(function (Tenant $tenant) {
|
||||
foreach (static::$resolvers as $resolver) {
|
||||
foreach (Tenancy::cachedResolvers() as $resolver) {
|
||||
/** @var CachedTenantResolver $resolver */
|
||||
$resolver = app($resolver);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,25 +5,18 @@ declare(strict_types=1);
|
|||
namespace Stancl\Tenancy\Database\Concerns;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Stancl\Tenancy\Resolvers;
|
||||
use Stancl\Tenancy\Resolvers\Contracts\CachedTenantResolver;
|
||||
use Stancl\Tenancy\Tenancy;
|
||||
|
||||
/**
|
||||
* Meant to be used on models that belong to tenants.
|
||||
*/
|
||||
trait InvalidatesTenantsResolverCache
|
||||
{
|
||||
/** @var array<class-string<CachedTenantResolver>> */
|
||||
public static array $resolvers = [ // todo single source of truth for this here and in InvalidatesResolverCache
|
||||
Resolvers\DomainTenantResolver::class,
|
||||
Resolvers\PathTenantResolver::class,
|
||||
Resolvers\RequestDataTenantResolver::class,
|
||||
];
|
||||
|
||||
public static function bootInvalidatesTenantsResolverCache(): void
|
||||
{
|
||||
static::saved(function (Model $model) {
|
||||
foreach (static::$resolvers as $resolver) {
|
||||
foreach (Tenancy::cachedResolvers() as $resolver) {
|
||||
/** @var CachedTenantResolver $resolver */
|
||||
$resolver = app($resolver);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class RouteIsMissingTenantParameterException extends Exception
|
|||
{
|
||||
public function __construct()
|
||||
{
|
||||
$parameter = PathTenantResolver::$tenantParameterName;
|
||||
$parameter = PathTenantResolver::tenantParameterName();
|
||||
|
||||
parent::__construct("The route's first argument is not the tenant id (configured paramter name: $parameter).");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,14 +34,8 @@ class InitializeTenancyByPath extends IdentificationMiddleware
|
|||
// Only initialize tenancy if tenant is the first parameter
|
||||
// We don't want to initialize tenancy if the tenant is
|
||||
// simply injected into some route controller action.
|
||||
if ($route->parameterNames()[0] === PathTenantResolver::$tenantParameterName) {
|
||||
// Set tenant as a default parameter for the URLs in the current request
|
||||
Event::listen(InitializingTenancy::class, function (InitializingTenancy $event) {
|
||||
/** @var Tenant $tenant */
|
||||
$tenant = $event->tenancy->tenant;
|
||||
|
||||
URL::defaults([PathTenantResolver::$tenantParameterName => $tenant->getTenantKey()]);
|
||||
});
|
||||
if ($route->parameterNames()[0] === PathTenantResolver::tenantParameterName()) {
|
||||
$this->setDefaultTenantForRouteParametersWhenTenancyIsInitialized();
|
||||
|
||||
return $this->initializeTenancy(
|
||||
$request,
|
||||
|
|
@ -54,4 +48,16 @@ class InitializeTenancyByPath extends IdentificationMiddleware
|
|||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
protected function setDefaultTenantForRouteParametersWhenTenancyIsInitialized(): void
|
||||
{
|
||||
Event::listen(InitializingTenancy::class, function (InitializingTenancy $event) {
|
||||
/** @var Tenant $tenant */
|
||||
$tenant = $event->tenancy->tenant;
|
||||
|
||||
URL::defaults([
|
||||
PathTenantResolver::tenantParameterName() => $tenant->getTenantKey(),
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,23 +11,17 @@ use Stancl\Tenancy\Contracts\TenantResolver;
|
|||
|
||||
abstract class CachedTenantResolver implements TenantResolver
|
||||
{
|
||||
public static bool $shouldCache = false; // todo docblocks for these
|
||||
|
||||
public static int $cacheTTL = 3600; // seconds
|
||||
|
||||
public static string|null $cacheStore = null; // default
|
||||
|
||||
/** @var Repository */
|
||||
protected $cache;
|
||||
|
||||
public function __construct(Factory $cache)
|
||||
{
|
||||
$this->cache = $cache->store(static::$cacheStore);
|
||||
$this->cache = $cache->store(static::cacheStore());
|
||||
}
|
||||
|
||||
public function resolve(mixed ...$args): Tenant
|
||||
{
|
||||
if (! static::$shouldCache) {
|
||||
if (! static::shouldCache()) {
|
||||
return $this->resolveWithoutCache(...$args);
|
||||
}
|
||||
|
||||
|
|
@ -42,14 +36,14 @@ abstract class CachedTenantResolver implements TenantResolver
|
|||
}
|
||||
|
||||
$tenant = $this->resolveWithoutCache(...$args);
|
||||
$this->cache->put($key, $tenant, static::$cacheTTL);
|
||||
$this->cache->put($key, $tenant, static::cacheTTL());
|
||||
|
||||
return $tenant;
|
||||
}
|
||||
|
||||
public function invalidateCache(Tenant $tenant): void
|
||||
{
|
||||
if (! static::$shouldCache) {
|
||||
if (! static::shouldCache()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -75,4 +69,19 @@ abstract class CachedTenantResolver implements TenantResolver
|
|||
* @return array[]
|
||||
*/
|
||||
abstract public function getArgsForTenant(Tenant $tenant): array;
|
||||
|
||||
public static function shouldCache(): bool
|
||||
{
|
||||
return config('tenancy.identification.resolvers.' . static::class . '.cache') ?? false;
|
||||
}
|
||||
|
||||
public static function cacheTTL(): int
|
||||
{
|
||||
return config('tenancy.identification.resolvers.' . static::class . '.cache_ttl') ?? 3600;
|
||||
}
|
||||
|
||||
public static function cacheStore(): string|null
|
||||
{
|
||||
return config('tenancy.identification.resolvers.' . static::class . '.cache_store');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,6 @@ class DomainTenantResolver extends Contracts\CachedTenantResolver
|
|||
/** The model representing the domain that the tenant was identified on. */
|
||||
public static Domain $currentDomain; // todo |null?
|
||||
|
||||
public static bool $shouldCache = false;
|
||||
|
||||
public static int $cacheTTL = 3600; // seconds
|
||||
|
||||
public static string|null $cacheStore = null; // default
|
||||
|
||||
public function resolveWithoutCache(mixed ...$args): Tenant
|
||||
{
|
||||
$domain = $args[0];
|
||||
|
|
|
|||
|
|
@ -10,21 +10,13 @@ use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedByPathException;
|
|||
|
||||
class PathTenantResolver extends Contracts\CachedTenantResolver
|
||||
{
|
||||
public static string $tenantParameterName = 'tenant';
|
||||
|
||||
public static bool $shouldCache = false;
|
||||
|
||||
public static int $cacheTTL = 3600; // seconds
|
||||
|
||||
public static string|null $cacheStore = null; // default
|
||||
|
||||
public function resolveWithoutCache(mixed ...$args): Tenant
|
||||
{
|
||||
/** @var Route $route */
|
||||
$route = $args[0];
|
||||
|
||||
if ($id = (string) $route->parameter(static::$tenantParameterName)) {
|
||||
$route->forgetParameter(static::$tenantParameterName);
|
||||
if ($id = (string) $route->parameter(static::tenantParameterName())) {
|
||||
$route->forgetParameter(static::tenantParameterName());
|
||||
|
||||
if ($tenant = tenancy()->find($id)) {
|
||||
return $tenant;
|
||||
|
|
@ -40,4 +32,9 @@ class PathTenantResolver extends Contracts\CachedTenantResolver
|
|||
[$tenant->getTenantKey()],
|
||||
];
|
||||
}
|
||||
|
||||
public static function tenantParameterName(): string
|
||||
{
|
||||
return config('tenancy.identification.resolvers.' . static::class . '.tenant_parameter_name') ?? 'tenant';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class Tenancy
|
|||
}
|
||||
}
|
||||
|
||||
// todo0 for phpstan this should be $this->tenant?, but first I want to clean up the $initialized logic and explore removing the property
|
||||
// todo1 for phpstan this should be $this->tenant?, but first I want to clean up the $initialized logic and explore removing the property
|
||||
if ($this->initialized && $this->tenant->getTenantKey() === $tenant->getTenantKey()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -157,7 +157,7 @@ class Tenancy
|
|||
$tenants = is_string($tenants) ? [$tenants] : $tenants;
|
||||
|
||||
// Use all tenants if $tenants is falsey
|
||||
$tenants = $tenants ?: $this->model()->cursor(); // todo0 phpstan thinks this isn't needed, but tests fail without it
|
||||
$tenants = $tenants ?: $this->model()->cursor(); // todo1 phpstan thinks this isn't needed, but tests fail without it
|
||||
|
||||
$originalTenant = $this->tenant;
|
||||
|
||||
|
|
@ -177,4 +177,41 @@ class Tenancy
|
|||
$this->end();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached tenant resolvers used by the package.
|
||||
*
|
||||
* @return array<class-string<Resolvers\Contracts\CachedTenantResolver>>
|
||||
*/
|
||||
public static function cachedResolvers(): array
|
||||
{
|
||||
$resolvers = config('tenancy.identification.resolvers', []);
|
||||
|
||||
$cachedResolvers = array_filter($resolvers, function (array $options) {
|
||||
// Resolvers based on CachedTenantResolver have the 'cache' option in the resolver config
|
||||
return isset($options['cache']);
|
||||
});
|
||||
|
||||
return array_keys($cachedResolvers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenant identification middleware used by the package.
|
||||
*
|
||||
* @return array<class-string<Middleware\IdentificationMiddleware>>
|
||||
*/
|
||||
public static function middleware(): array
|
||||
{
|
||||
return config('tenancy.identification.middleware', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default tenant identification middleware used by the package.
|
||||
*
|
||||
* @return class-string<Middleware\IdentificationMiddleware>
|
||||
*/
|
||||
public static function defaultMiddleware(): string
|
||||
{
|
||||
return config('tenancy.identification.default_middleware', Middleware\InitializeTenancyByDomain::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ class TenancyServiceProvider extends ServiceProvider
|
|||
if ($event instanceof TenancyEvent) {
|
||||
match (tenancy()->logMode()) {
|
||||
LogMode::SILENT => tenancy()->logEvent($event),
|
||||
LogMode::INSTANT => dump($event), // todo0 perhaps still log
|
||||
LogMode::INSTANT => dump($event), // todo1 perhaps still log
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ use Illuminate\Support\Facades\DB;
|
|||
use Stancl\Tenancy\Resolvers\DomainTenantResolver;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
|
||||
afterEach(function () {
|
||||
DomainTenantResolver::$shouldCache = false;
|
||||
});
|
||||
// todo@v4 test this with other resolvers as well?
|
||||
|
||||
test('tenants can be resolved using the cached resolver', function () {
|
||||
$tenant = Tenant::create();
|
||||
|
|
@ -27,14 +25,14 @@ test('the underlying resolver is not touched when using the cached resolver', fu
|
|||
|
||||
DB::enableQueryLog();
|
||||
|
||||
DomainTenantResolver::$shouldCache = false;
|
||||
config(['tenancy.identification.resolvers.' . DomainTenantResolver::class . '.cache' => false]);
|
||||
|
||||
expect($tenant->is(app(DomainTenantResolver::class)->resolve('acme')))->toBeTrue();
|
||||
DB::flushQueryLog();
|
||||
expect($tenant->is(app(DomainTenantResolver::class)->resolve('acme')))->toBeTrue();
|
||||
pest()->assertNotEmpty(DB::getQueryLog()); // not empty
|
||||
|
||||
DomainTenantResolver::$shouldCache = true;
|
||||
config(['tenancy.identification.resolvers.' . DomainTenantResolver::class . '.cache' => true]);
|
||||
|
||||
expect($tenant->is(app(DomainTenantResolver::class)->resolve('acme')))->toBeTrue();
|
||||
DB::flushQueryLog();
|
||||
|
|
@ -50,7 +48,7 @@ test('cache is invalidated when the tenant is updated', function () {
|
|||
|
||||
DB::enableQueryLog();
|
||||
|
||||
DomainTenantResolver::$shouldCache = true;
|
||||
config(['tenancy.identification.resolvers.' . DomainTenantResolver::class . '.cache' => true]);
|
||||
|
||||
expect($tenant->is(app(DomainTenantResolver::class)->resolve('acme')))->toBeTrue();
|
||||
DB::flushQueryLog();
|
||||
|
|
@ -74,7 +72,7 @@ test('cache is invalidated when a tenants domain is changed', function () {
|
|||
|
||||
DB::enableQueryLog();
|
||||
|
||||
DomainTenantResolver::$shouldCache = true;
|
||||
config(['tenancy.identification.resolvers.' . DomainTenantResolver::class . '.cache' => true]);
|
||||
|
||||
expect($tenant->is(app(DomainTenantResolver::class)->resolve('acme')))->toBeTrue();
|
||||
DB::flushQueryLog();
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ use Stancl\Tenancy\Resolvers\PathTenantResolver;
|
|||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
|
||||
beforeEach(function () {
|
||||
PathTenantResolver::$tenantParameterName = 'tenant';
|
||||
|
||||
Route::group([
|
||||
'prefix' => '/{tenant}',
|
||||
'middleware' => InitializeTenancyByPath::class,
|
||||
|
|
@ -26,11 +24,6 @@ beforeEach(function () {
|
|||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
// Global state cleanup
|
||||
PathTenantResolver::$tenantParameterName = 'tenant';
|
||||
});
|
||||
|
||||
test('tenant can be identified by path', function () {
|
||||
Tenant::create([
|
||||
'id' => 'acme',
|
||||
|
|
@ -101,7 +94,7 @@ test('an exception is thrown when the routes first parameter is not tenant', fun
|
|||
});
|
||||
|
||||
test('tenant parameter name can be customized', function () {
|
||||
PathTenantResolver::$tenantParameterName = 'team';
|
||||
config(['tenancy.identification.resolvers.' . PathTenantResolver::class . '.tenant_parameter_name' => 'team']);
|
||||
|
||||
Route::group([
|
||||
'prefix' => '/{team}',
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ use Illuminate\Support\Facades\Event;
|
|||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper;
|
||||
use Stancl\Tenancy\Controllers\TenantAssetsController;
|
||||
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData;
|
||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||
|
||||
|
|
@ -21,13 +19,8 @@ beforeEach(function () {
|
|||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
// Cleanup
|
||||
TenantAssetsController::$tenancyMiddleware = InitializeTenancyByDomain::class;
|
||||
});
|
||||
|
||||
test('asset can be accessed using the url returned by the tenant asset helper', function () {
|
||||
TenantAssetsController::$tenancyMiddleware = InitializeTenancyByRequestData::class;
|
||||
config(['tenancy.identification.default_middleware' => InitializeTenancyByRequestData::class]);
|
||||
|
||||
$tenant = Tenant::create();
|
||||
tenancy()->initialize($tenant);
|
||||
|
|
@ -95,7 +88,7 @@ test('asset helper tenancy can be disabled', function () {
|
|||
});
|
||||
|
||||
test('test asset controller returns a 404 when no path is provided', function () {
|
||||
TenantAssetsController::$tenancyMiddleware = InitializeTenancyByRequestData::class;
|
||||
config(['tenancy.identification.default_middleware' => InitializeTenancyByRequestData::class]);
|
||||
|
||||
$tenant = Tenant::create();
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
|||
'--realpath' => true,
|
||||
'--force' => true,
|
||||
],
|
||||
'tenancy.bootstrappers.redis' => RedisTenancyBootstrapper::class, // todo0 change this to []? two tests in TenantDatabaseManagerTest are failing with that
|
||||
'tenancy.bootstrappers.redis' => RedisTenancyBootstrapper::class, // todo1 change this to []? two tests in TenantDatabaseManagerTest are failing with that
|
||||
'queue.connections.central' => [
|
||||
'driver' => 'sync',
|
||||
'central' => true,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue