mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-12 19:54:03 +00:00
Improve route cloning action (#8)
* Allow cloning routes when only kernel identification is used, explicitly enable specific cloning modes * Explicitly enable needed clone modes in tests, use "clone" instead of "reregister" * Fix code style (php-cs-fixer) * Use "cloning" instead of "re-registration" in UniversalRouteTest * Only clone routes using path identification * Revert clone mode changes * Fix code style (php-cs-fixer) * Update comment * Skip cloning 'stancl.tenancy.asset' by default * Decide which routes should get cloned in the filtering step, improve method organization * Return `RouteMode::UNIVERSAL` in getMiddlewareContext if route is universal * Give universal route the path ID MW so that it gets cloned * Fix code style (php-cs-fixer) * Simplify UsableWithEarlyIdentification code * Handle universal route mode in ForgetTenantParameter * Fix code style (php-cs-fixer) * Rename getMiddlewareContext to getRouteMode * Append '/' to the route prefix * Rename variable * Wrap part of condition in parentheses * Refresh name lookups after cloning routes * Test giving tenant flag to cloned routes * Add routeIsUniversal method * Correct ForgetTenantParameter condition * Improve tenant flag giving logic * Improve test name * Delete leftover testing code * Put part of condition into `()` * Improve CloneRoutesAsTenant code + comments * Extract route mode-related code into methods, refactor and improve code * Improve ForgetTenantParameter, test tenant parameter removing in universal routes * Fix code style (php-cs-fixer) * Fix test * Simplify adding tenant flag * Don't skip stancl.tenancy.asset route cloning * clean up comment * fix in_array() argument * Fix code style (php-cs-fixer) --------- Co-authored-by: PHP CS Fixer <phpcsfixer@example.com> Co-authored-by: Samuel Štancl <samuel.stancl@gmail.com>
This commit is contained in:
parent
4d4639450e
commit
f7d9f02fd4
9 changed files with 222 additions and 111 deletions
|
|
@ -191,7 +191,7 @@ class TenancyServiceProvider extends ServiceProvider
|
|||
* $route->middleware('tenant');
|
||||
* });
|
||||
*
|
||||
* To see the default behavior of re-registering the universal routes, check out the cloneRoute() method in CloneRoutesAsTenant.
|
||||
* To see the default behavior of cloning the universal routes, check out the cloneRoute() method in CloneRoutesAsTenant.
|
||||
* @see CloneRoutesAsTenant
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||
namespace Stancl\Tenancy\Actions;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Config\Repository;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Support\Collection;
|
||||
|
|
@ -42,30 +41,16 @@ class CloneRoutesAsTenant
|
|||
|
||||
public function __construct(
|
||||
protected Router $router,
|
||||
protected Repository $config,
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$tenantParameterName = PathIdentificationManager::getTenantParameterName();
|
||||
$routePrefix = '/{' . $tenantParameterName . '}';
|
||||
$this->router
|
||||
->prefix('/{' . PathIdentificationManager::getTenantParameterName() . '}/')
|
||||
->group(fn () => $this->getRoutesToClone()->each(fn (Route $route) => $this->cloneRoute($route)));
|
||||
|
||||
/** @var Collection<Route> $routesToClone Only clone non-skipped routes without the tenant parameter. */
|
||||
$routesToClone = collect($this->router->getRoutes()->get())->filter(function (Route $route) use ($tenantParameterName) {
|
||||
return ! (in_array($tenantParameterName, $route->parameterNames()) || in_array($route->getName(), $this->skippedRoutes));
|
||||
});
|
||||
|
||||
if ($this->config->get('tenancy.default_route_mode') !== RouteMode::UNIVERSAL) {
|
||||
// Only clone routes with route-level path identification and universal routes
|
||||
$routesToClone = $routesToClone->where(function (Route $route) {
|
||||
$routeIsUniversal = tenancy()->routeHasMiddleware($route, 'universal');
|
||||
|
||||
return PathIdentificationManager::pathIdentificationOnRoute($route) || $routeIsUniversal;
|
||||
});
|
||||
}
|
||||
|
||||
$this->router->prefix($routePrefix)->group(fn () => $routesToClone->each(fn (Route $route) => $this->cloneRoute($route)));
|
||||
$this->router->getRoutes()->refreshNameLookups();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -88,6 +73,56 @@ class CloneRoutesAsTenant
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function getRoutesToClone(): Collection
|
||||
{
|
||||
$tenantParameterName = PathIdentificationManager::getTenantParameterName();
|
||||
|
||||
/**
|
||||
* Clone all routes that:
|
||||
* - don't have the tenant parameter
|
||||
* - aren't in the $skippedRoutes array
|
||||
* - are using path identification (kernel or route-level).
|
||||
*
|
||||
* Non-universal cloned routes will only be available in the tenant context,
|
||||
* universal routes will be available in both contexts.
|
||||
*/
|
||||
return collect($this->router->getRoutes()->get())->filter(function (Route $route) use ($tenantParameterName) {
|
||||
if (in_array($tenantParameterName, $route->parameterNames(), true) || in_array($route->getName(), $this->skippedRoutes, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$routeHasPathIdentificationMiddleware = PathIdentificationManager::pathIdentificationOnRoute($route);
|
||||
$pathIdentificationMiddlewareInGlobalStack = PathIdentificationManager::pathIdentificationInGlobalStack();
|
||||
$routeHasNonPathIdentificationMiddleware = tenancy()->routeHasIdentificationMiddleware($route) && ! $routeHasPathIdentificationMiddleware;
|
||||
|
||||
/**
|
||||
* The route should get cloned if:
|
||||
* - it has route-level path identification middleware, OR
|
||||
* - it uses kernel path identification (it doesn't have any route-level identification middleware) and the route is tenant or universal.
|
||||
*
|
||||
* The route is considered tenant if:
|
||||
* - it's flagged as tenant, OR
|
||||
* - it's not flagged as tenant or universal, but it has the identification middleware
|
||||
*
|
||||
* The route is considered universal if it's flagged as universal, and it doesn't have the tenant flag
|
||||
* (it's still considered universal if it has route-level path identification middleware + the universal flag).
|
||||
*
|
||||
* If the route isn't flagged, the context is determined using the default route mode.
|
||||
*/
|
||||
$pathIdentificationUsed = (! $routeHasNonPathIdentificationMiddleware) &&
|
||||
($routeHasPathIdentificationMiddleware || $pathIdentificationMiddlewareInGlobalStack);
|
||||
|
||||
$routeMode = tenancy()->getRouteMode($route);
|
||||
$routeIsUniversalOrTenant = $routeMode === RouteMode::TENANT || $routeMode === RouteMode::UNIVERSAL;
|
||||
|
||||
if ($pathIdentificationUsed && $routeIsUniversalOrTenant) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a route using a callback specified in the $cloneRouteUsing property (using the cloneUsing method).
|
||||
* If there's no callback specified for the route, use the default way of cloning routes.
|
||||
|
|
@ -104,34 +139,14 @@ class CloneRoutesAsTenant
|
|||
return;
|
||||
}
|
||||
|
||||
$routesAreUniversalByDefault = $this->config->get('tenancy.default_route_mode') === RouteMode::UNIVERSAL;
|
||||
$routeHasPathIdentification = PathIdentificationManager::pathIdentificationOnRoute($route);
|
||||
$pathIdentificationMiddlewareInGlobalStack = PathIdentificationManager::pathIdentificationInGlobalStack();
|
||||
$routeHasNonPathIdentificationMiddleware = tenancy()->routeHasIdentificationMiddleware($route) && ! $routeHasPathIdentification;
|
||||
|
||||
// Determine if the passed route should get cloned
|
||||
// The route should be cloned if it has path identification middleware
|
||||
// Or if the route doesn't have identification middleware and path identification middleware
|
||||
// Is not used globally or the routes are universal by default
|
||||
$shouldCloneRoute = ! $routeHasNonPathIdentificationMiddleware &&
|
||||
($routesAreUniversalByDefault || $routeHasPathIdentification || $pathIdentificationMiddlewareInGlobalStack);
|
||||
|
||||
if ($shouldCloneRoute) {
|
||||
$newRoute = $this->createNewRoute($route);
|
||||
$routeConsideredUniversal = tenancy()->routeHasMiddleware($newRoute, 'universal') || $routesAreUniversalByDefault;
|
||||
|
||||
if ($routeHasPathIdentification && ! $routeConsideredUniversal && ! tenancy()->routeHasMiddleware($newRoute, 'tenant')) {
|
||||
// Skip adding tenant flag
|
||||
// Non-universal routes with identification middleware are already considered tenant
|
||||
// Also skip adding the flag if the route already has the flag
|
||||
// So that the route only has the 'tenant' middleware group once
|
||||
} else {
|
||||
if (! tenancy()->routeHasMiddleware($route, 'tenant')) {
|
||||
$newRoute->middleware('tenant');
|
||||
}
|
||||
|
||||
$this->copyMiscRouteProperties($route, $newRoute);
|
||||
}
|
||||
}
|
||||
|
||||
protected function createNewRoute(Route $route): Route
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,42 +5,61 @@ declare(strict_types=1);
|
|||
namespace Stancl\Tenancy\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Http\Kernel;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Route as RouteFacade;
|
||||
use Stancl\Tenancy\Enums\Context;
|
||||
use Stancl\Tenancy\Enums\RouteMode;
|
||||
|
||||
// todo1 Name – maybe DealsWithMiddlewareContexts?
|
||||
trait DealsWithEarlyIdentification
|
||||
{
|
||||
/**
|
||||
* Get route's middleware context (tenant or central).
|
||||
* Get route's middleware context (tenant, central or universal).
|
||||
* The context is determined by the route's middleware.
|
||||
*
|
||||
* If the route has the 'central' middleware, the context is central.
|
||||
* If the route has the 'tenant' middleware, or any tenancy identification middleware, the context is tenant.
|
||||
* If the route has the 'tenant' middleware, or any tenancy identification middleware (and the route isn't flagged as universal), the context is tenant.
|
||||
*
|
||||
* If the route doesn't have any of the mentioned middleware,
|
||||
* the context is determined by the `tenancy.default_route_mode` config.
|
||||
*/
|
||||
public static function getMiddlewareContext(Route $route): RouteMode
|
||||
public static function getRouteMode(Route $route): RouteMode
|
||||
{
|
||||
if (static::routeHasMiddleware($route, 'central')) {
|
||||
return RouteMode::CENTRAL;
|
||||
}
|
||||
|
||||
$defaultRouteMode = config('tenancy.default_route_mode');
|
||||
$routeIsUniversal = $defaultRouteMode === RouteMode::UNIVERSAL || static::routeHasMiddleware($route, 'universal');
|
||||
$routeIsUniversal = static::routeIsUniversal($route);
|
||||
|
||||
// If a route has identification middleware AND the route isn't universal, don't consider the context tenant
|
||||
if (static::routeHasMiddleware($route, 'tenant') || static::routeHasIdentificationMiddleware($route) && ! $routeIsUniversal) {
|
||||
// If the route is flagged as tenant, consider it tenant
|
||||
// If the route has an identification middleware and the route is not universal, consider it tenant
|
||||
if (
|
||||
static::routeHasMiddleware($route, 'tenant') ||
|
||||
(static::routeHasIdentificationMiddleware($route) && ! $routeIsUniversal)
|
||||
) {
|
||||
return RouteMode::TENANT;
|
||||
}
|
||||
|
||||
return $defaultRouteMode;
|
||||
// If the route is universal, you have to determine its actual context using
|
||||
// The identification middleware's determineUniversalRouteContextFromRequest
|
||||
if ($routeIsUniversal) {
|
||||
return RouteMode::UNIVERSAL;
|
||||
}
|
||||
|
||||
return config('tenancy.default_route_mode');
|
||||
}
|
||||
|
||||
public static function routeIsUniversal(Route $route): bool
|
||||
{
|
||||
$routeFlaggedAsTenantOrCentral = static::routeHasMiddleware($route, 'tenant') || static::routeHasMiddleware($route, 'central');
|
||||
$routeFlaggedAsUniversal = static::routeHasMiddleware($route, 'universal');
|
||||
$universalFlagUsedInGlobalStack = app(Kernel::class)->hasMiddleware('universal');
|
||||
$defaultRouteModeIsUniversal = config('tenancy.default_route_mode') === RouteMode::UNIVERSAL;
|
||||
|
||||
return ! $routeFlaggedAsTenantOrCentral && ($routeFlaggedAsUniversal || $universalFlagUsedInGlobalStack || $defaultRouteModeIsUniversal);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -93,6 +112,31 @@ trait DealsWithEarlyIdentification
|
|||
return in_array($middleware, static::getRouteMiddleware($route));
|
||||
}
|
||||
|
||||
public function routeIdentificationMiddleware(Route $route): string|null
|
||||
{
|
||||
foreach (static::getRouteMiddleware($route) as $routeMiddleware) {
|
||||
if (in_array($routeMiddleware, static::middleware())) {
|
||||
return $routeMiddleware;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function kernelIdentificationMiddleware(): string|null
|
||||
{
|
||||
/** @var Kernel $kernel */
|
||||
$kernel = app(Kernel::class);
|
||||
|
||||
foreach (static::middleware() as $identificationMiddleware) {
|
||||
if ($kernel->hasMiddleware($identificationMiddleware)) {
|
||||
return $identificationMiddleware;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a route has identification middleware.
|
||||
*/
|
||||
|
|
@ -107,6 +151,14 @@ trait DealsWithEarlyIdentification
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if route uses kernel identification (identification middleare is in the global stack and the route doesn't have route-level identification middleware).
|
||||
*/
|
||||
public static function routeUsesKernelIdentification(Route $route): bool
|
||||
{
|
||||
return ! static::routeHasIdentificationMiddleware($route) && static::kernelIdentificationMiddleware();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a route uses domain identification.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -29,21 +29,13 @@ trait UsableWithEarlyIdentification
|
|||
{
|
||||
/**
|
||||
* Skip middleware if the route is universal and uses path identification or if the route is universal and the context should be central.
|
||||
* Universal routes using path identification should get re-registered using ReregisterRoutesAsTenant.
|
||||
* Universal routes using path identification should get cloned using CloneRoutesAsTenant.
|
||||
*
|
||||
* @see \Stancl\Tenancy\Actions\CloneRoutesAsTenant
|
||||
*/
|
||||
protected function shouldBeSkipped(Route $route): bool
|
||||
{
|
||||
$routeMiddleware = tenancy()->getRouteMiddleware($route);
|
||||
$universalFlagUsed = in_array('universal', $routeMiddleware);
|
||||
$defaultToUniversalRoutes = config('tenancy.default_route_mode') === RouteMode::UNIVERSAL;
|
||||
|
||||
// Route is universal only if it doesn't have the central/tenant flag
|
||||
$routeIsUniversal = ($universalFlagUsed || $defaultToUniversalRoutes) &&
|
||||
! (in_array('central', $routeMiddleware) || in_array('tenant', $routeMiddleware));
|
||||
|
||||
if ($routeIsUniversal && $this instanceof IdentificationMiddleware) {
|
||||
if (tenancy()->routeIsUniversal($route) && $this instanceof IdentificationMiddleware) {
|
||||
/** @phpstan-ignore-next-line */
|
||||
throw_unless($this instanceof UsableWithUniversalRoutes, MiddlewareNotUsableWithUniversalRoutesException::class);
|
||||
|
||||
|
|
@ -71,10 +63,15 @@ trait UsableWithEarlyIdentification
|
|||
|
||||
// Check if this is the identification middleware the route should be using
|
||||
// Route-level identification middleware is prioritized
|
||||
$middlewareUsed = tenancy()->routeHasMiddleware($route, static::class) || ! tenancy()->routeHasIdentificationMiddleware($route) && static::inGlobalStack();
|
||||
$globalIdentificationUsed = ! tenancy()->routeHasIdentificationMiddleware($route) && static::inGlobalStack();
|
||||
$routeLevelIdentificationUsed = tenancy()->routeHasMiddleware($route, static::class);
|
||||
|
||||
/** @var UsableWithUniversalRoutes $this */
|
||||
return $middlewareUsed && $this->requestHasTenant($request) ? Context::TENANT : Context::CENTRAL;
|
||||
if (($globalIdentificationUsed || $routeLevelIdentificationUsed) && $this->requestHasTenant($request)) {
|
||||
return Context::TENANT;
|
||||
}
|
||||
|
||||
return Context::CENTRAL;
|
||||
}
|
||||
|
||||
protected function shouldIdentificationMiddlewareBeSkipped(Route $route): bool
|
||||
|
|
@ -88,10 +85,9 @@ trait UsableWithEarlyIdentification
|
|||
if (! $request->attributes->get('_tenancy_kernel_identification_skipped')) {
|
||||
if (
|
||||
// Skip identification if the current route is central
|
||||
// The route is central if defaulting is set to central and the route isn't flagged as tenant or it doesn't have identification middleware
|
||||
tenancy()->getMiddlewareContext($route) === RouteMode::CENTRAL
|
||||
// Don't skip identification if the central route is considered universal
|
||||
&& (config('tenancy.default_route_mode') !== RouteMode::UNIVERSAL || ! tenancy()->routeHasMiddleware($route, 'universal'))
|
||||
// The route is central if it's flagged as central
|
||||
// Or if it isn't flagged and the default route mode is set to central
|
||||
tenancy()->getRouteMode($route) === RouteMode::CENTRAL
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,17 +19,16 @@ use Stancl\Tenancy\PathIdentificationManager;
|
|||
* We remove the {tenant} parameter from the hydrated route when
|
||||
* 1) the InitializeTenancyByPath middleware is in the global stack, AND
|
||||
* 2) the matched route does not have identification middleware (so that {tenant} isn't forgotten when using route-level identification), AND
|
||||
* 3) the route has tenant middleware context (so that {tenant} doesn't get accidentally removed from central routes).
|
||||
* 3) the route isn't in the central context (so that {tenant} doesn't get accidentally removed from central routes).
|
||||
*/
|
||||
class ForgetTenantParameter
|
||||
{
|
||||
public function handle(RouteMatched $event): void
|
||||
{
|
||||
if (
|
||||
PathIdentificationManager::pathIdentificationInGlobalStack() &&
|
||||
! tenancy()->routeHasIdentificationMiddleware($event->route) &&
|
||||
tenancy()->getMiddlewareContext($event->route) === RouteMode::TENANT
|
||||
) {
|
||||
$kernelPathIdentificationUsed = PathIdentificationManager::pathIdentificationInGlobalStack() && ! tenancy()->routeHasIdentificationMiddleware($event->route);
|
||||
$routeModeIsTenant = tenancy()->getRouteMode($event->route) === RouteMode::TENANT;
|
||||
|
||||
if ($kernelPathIdentificationUsed && $routeModeIsTenant) {
|
||||
$event->route->forgetParameter(PathIdentificationManager::getTenantParameterName());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,15 +76,15 @@ class InitializeTenancyByPath extends IdentificationMiddleware implements Usable
|
|||
/**
|
||||
* Path identification request has a tenant if the middleware context is tenant.
|
||||
*
|
||||
* With path identification, we can just check the MW context because we're re-registering the universal routes,
|
||||
* With path identification, we can just check the MW context because we're cloning the universal routes,
|
||||
* and the routes are flagged with the 'tenant' MW group (= their MW context is tenant).
|
||||
*
|
||||
* With other identification middleware, we have to determine the context differently because we only have one
|
||||
* truly universal route available ('truly universal' because with path identification, applying 'universal' to a route just means that
|
||||
* it should get re-registered, whereas with other ID MW, it means that the route you apply the 'universal' flag to will be accessible in both contexts).
|
||||
* it should get cloned, whereas with other ID MW, it means that the route you apply the 'universal' flag to will be accessible in both contexts).
|
||||
*/
|
||||
public function requestHasTenant(Request $request): bool
|
||||
{
|
||||
return tenancy()->getMiddlewareContext(tenancy()->getRoute($request)) === RouteMode::TENANT;
|
||||
return tenancy()->getRouteMode(tenancy()->getRoute($request)) === RouteMode::TENANT;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,8 @@ class PreventAccessFromUnwantedDomains
|
|||
public function handle(Request $request, Closure $next): mixed
|
||||
{
|
||||
$route = tenancy()->getRoute($request);
|
||||
$routeIsUniversal = tenancy()->routeHasMiddleware($route, 'universal') || config('tenancy.default_route_mode') === RouteMode::UNIVERSAL;
|
||||
|
||||
if ($this->shouldBeSkipped($route) || $routeIsUniversal) {
|
||||
if ($this->shouldBeSkipped($route) || tenancy()->routeIsUniversal($route)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
|
@ -52,13 +51,13 @@ class PreventAccessFromUnwantedDomains
|
|||
|
||||
protected function accessingTenantRouteFromCentralDomain(Request $request, Route $route): bool
|
||||
{
|
||||
return tenancy()->getMiddlewareContext($route) === RouteMode::TENANT // Current route's middleware context is tenant
|
||||
return tenancy()->getRouteMode($route) === RouteMode::TENANT // Current route's middleware context is tenant
|
||||
&& $this->isCentralDomain($request); // The request comes from a domain that IS present in the configured `tenancy.central_domains`
|
||||
}
|
||||
|
||||
protected function accessingCentralRouteFromTenantDomain(Request $request, Route $route): bool
|
||||
{
|
||||
return tenancy()->getMiddlewareContext($route) === RouteMode::CENTRAL // Current route's middleware context is central
|
||||
return tenancy()->getRouteMode($route) === RouteMode::CENTRAL // Current route's middleware context is central
|
||||
&& ! $this->isCentralDomain($request); // The request comes from a domain that ISN'T present in the configured `tenancy.central_domains`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -263,6 +263,14 @@ test('the tenant parameter is only removed from tenant routes when using path id
|
|||
->middleware('tenant')
|
||||
->name('tenant-route');
|
||||
|
||||
RouteFacade::get($pathIdentification ? '/universal-route' : '/universal-route/{tenant?}', [ControllerWithMiddleware::class, 'routeHasTenantParameter'])
|
||||
->middleware('universal')
|
||||
->name('universal-route');
|
||||
|
||||
/** @var CloneRoutesAsTenant */
|
||||
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
$cloneRoutesAction->handle();
|
||||
|
||||
$tenant = Tenant::create();
|
||||
$tenantKey = $tenant->getTenantKey();
|
||||
|
||||
|
|
@ -274,12 +282,24 @@ test('the tenant parameter is only removed from tenant routes when using path id
|
|||
// Tenant parameter is removed from tenant routes using kernel path identification (Stancl\Tenancy\Listeners\ForgetTenantParameter)
|
||||
$response = pest()->get($tenantKey . '/tenant-route')->assertOk();
|
||||
expect((bool) $response->getContent())->toBeFalse();
|
||||
|
||||
// The tenant parameter gets removed from the cloned universal route
|
||||
$response = pest()->get($tenantKey . '/universal-route')->assertOk();
|
||||
expect((bool) $response->getContent())->toBeFalse();
|
||||
} else {
|
||||
// Tenant parameter is not removed from tenant routes using other kernel identification MW
|
||||
$tenant->domains()->create(['domain' => $domain = $tenantKey . '.localhost']);
|
||||
|
||||
$response = pest()->get("http://{$domain}/{$tenantKey}/tenant-route")->assertOk();
|
||||
expect((bool) $response->getContent())->toBeTrue();
|
||||
|
||||
// The tenant parameter does not get removed from the universal route when accessing it through the central domain
|
||||
$response = pest()->get("http://localhost/universal-route/$tenantKey")->assertOk();
|
||||
expect((bool) $response->getContent())->toBeTrue();
|
||||
|
||||
// The tenant parameter gets removed from the universal route when accessing it through the tenant domain
|
||||
$response = pest()->get("http://{$domain}/universal-route")->assertOk();
|
||||
expect((bool) $response->getContent())->toBeFalse();
|
||||
}
|
||||
} else {
|
||||
RouteFacade::middlewareGroup('tenant', [$pathIdentification ? InitializeTenancyByPath::class : InitializeTenancyByDomain::class]);
|
||||
|
|
@ -370,7 +390,7 @@ test('route level identification is prioritized over kernel identification', fun
|
|||
'default to central routes' => RouteMode::CENTRAL,
|
||||
]);
|
||||
|
||||
test('routes with path identification middleware can get prefixed using the reregister action', function() {
|
||||
test('routes with path identification middleware can get prefixed using the clone action', function() {
|
||||
$tenantKey = Tenant::create()->getTenantKey();
|
||||
|
||||
RouteFacade::get('/home', fn () => tenant()?->getTenantKey())->name('home')->middleware(InitializeTenancyByPath::class);
|
||||
|
|
|
|||
|
|
@ -169,10 +169,10 @@ test('a route can be universal using path identification', function (array $rout
|
|||
: 'Tenancy is not initialized.';
|
||||
})->middleware($routeMiddleware);
|
||||
|
||||
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
|
||||
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
/** @var CloneRoutesAsTenant $cloneRoutesAction */
|
||||
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
|
||||
$reregisterRoutesAction->handle();
|
||||
$cloneRoutesAction->handle();
|
||||
|
||||
$tenantKey = Tenant::create()->getTenantKey();
|
||||
|
||||
|
|
@ -234,10 +234,10 @@ test('correct exception is thrown when route is universal and tenant could not b
|
|||
|
||||
RouteFacade::get('/foo', fn () => tenant() ? 'Tenancy is initialized.' : 'Tenancy is not initialized.')->middleware($routeMiddleware)->name('foo');
|
||||
|
||||
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
|
||||
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
/** @var CloneRoutesAsTenant $cloneRoutesAction */
|
||||
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
|
||||
$reregisterRoutesAction->handle();
|
||||
$cloneRoutesAction->handle();
|
||||
|
||||
pest()->expectException(TenantCouldNotBeIdentifiedByPathException::class);
|
||||
$this->withoutExceptionHandling()->get('http://localhost/non_existent/foo');
|
||||
|
|
@ -309,7 +309,7 @@ test('a route can be flagged as universal in both route modes', function (RouteM
|
|||
'default to central routes' => RouteMode::CENTRAL,
|
||||
]);
|
||||
|
||||
test('ReregisterRoutesAsTenant registers prefixed duplicates of universal routes correctly', function (bool $kernelIdentification, bool $useController) {
|
||||
test('CloneRoutesAsTenant registers prefixed duplicates of universal routes correctly', function (bool $kernelIdentification, bool $useController) {
|
||||
$routeMiddleware = ['universal'];
|
||||
|
||||
if ($kernelIdentification) {
|
||||
|
|
@ -328,10 +328,10 @@ test('ReregisterRoutesAsTenant registers prefixed duplicates of universal routes
|
|||
expect($routes = RouteFacade::getRoutes()->get())->toContain($universalRoute);
|
||||
expect($routes)->toContain($centralRoute);
|
||||
|
||||
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
|
||||
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
/** @var CloneRoutesAsTenant $cloneRoutesAction */
|
||||
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
|
||||
$reregisterRoutesAction->handle();
|
||||
$cloneRoutesAction->handle();
|
||||
|
||||
expect($routesAfterRegisteringDuplicates = RouteFacade::getRoutes()->get())
|
||||
->toContain($universalRoute)
|
||||
|
|
@ -340,6 +340,7 @@ test('ReregisterRoutesAsTenant registers prefixed duplicates of universal routes
|
|||
$newRoute = collect($routesAfterRegisteringDuplicates)->filter(fn ($route) => ! in_array($route, $routes))->first();
|
||||
|
||||
expect($newRoute->uri())->toBe('{' . $tenantParameterName . '}' . '/' . $universalRoute->uri());
|
||||
|
||||
expect(tenancy()->getRouteMiddleware($newRoute))->toBe(array_merge(tenancy()->getRouteMiddleware($universalRoute), ['tenant']));
|
||||
|
||||
$tenant = Tenant::create();
|
||||
|
|
@ -386,51 +387,55 @@ test('CloneRoutesAsTenant only clones routes with path identification by default
|
|||
|
||||
expect($currentRouteCount())->toBe($newRouteCount = $initialRouteCount + 2);
|
||||
|
||||
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
|
||||
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
/** @var CloneRoutesAsTenant $cloneRoutesAction */
|
||||
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
|
||||
$reregisterRoutesAction->handle();
|
||||
$cloneRoutesAction->handle();
|
||||
|
||||
// Only one of the two routes gets cloned
|
||||
expect($currentRouteCount())->toBe($newRouteCount + 1);
|
||||
});
|
||||
|
||||
test('custom callbacks can be used for reregistering universal routes', function () {
|
||||
RouteFacade::get('/home', fn () => tenant() ? 'Tenancy initialized.' : 'Tenancy not initialized.')->middleware('universal')->name($routeName = 'home');
|
||||
test('custom callbacks can be used for cloning universal routes', function () {
|
||||
RouteFacade::get('/home', fn () => tenant() ? 'Tenancy initialized.' : 'Tenancy not initialized.')->middleware(['universal', InitializeTenancyByPath::class])->name($routeName = 'home');
|
||||
|
||||
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
|
||||
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
/** @var CloneRoutesAsTenant $cloneRoutesAction */
|
||||
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
$currentRouteCount = fn () => count(RouteFacade::getRoutes()->get());
|
||||
$initialRouteCount = $currentRouteCount();
|
||||
|
||||
$cloneRoutesAction;
|
||||
|
||||
// Skip cloning the 'home' route
|
||||
$reregisterRoutesAction->cloneUsing($routeName, function (Route $route) {
|
||||
$cloneRoutesAction->cloneUsing($routeName, function (Route $route) {
|
||||
return;
|
||||
})->handle();
|
||||
|
||||
// Expect route count to stay the same because the 'home' route re-registration gets skipped
|
||||
// Expect route count to stay the same because the 'home' route cloning gets skipped
|
||||
expect($initialRouteCount)->toEqual($currentRouteCount());
|
||||
|
||||
// Modify the 'home' route re-registration so that a different route is registered
|
||||
$reregisterRoutesAction->cloneUsing($routeName, function (Route $route) {
|
||||
RouteFacade::get('/newly-registered-route', fn() => true)->name('new.home');
|
||||
// Modify the 'home' route cloning so that a different route is cloned
|
||||
$cloneRoutesAction->cloneUsing($routeName, function (Route $route) {
|
||||
RouteFacade::get('/cloned-route', fn () => true)->name('new.home');
|
||||
})->handle();
|
||||
|
||||
expect($currentRouteCount())->toEqual($initialRouteCount + 1);
|
||||
});
|
||||
|
||||
test('reregistration of specific routes can get skipped', function () {
|
||||
test('cloning of specific routes can get skipped', function () {
|
||||
RouteFacade::get('/home', fn () => tenant() ? 'Tenancy initialized.' : 'Tenancy not initialized.')->middleware('universal')->name($routeName = 'home');
|
||||
|
||||
/** @var CloneRoutesAsTenant $reregisterRoutesAction */
|
||||
$reregisterRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
/** @var CloneRoutesAsTenant $cloneRoutesAction */
|
||||
$cloneRoutesAction = app(CloneRoutesAsTenant::class);
|
||||
$currentRouteCount = fn () => count(RouteFacade::getRoutes()->get());
|
||||
$initialRouteCount = $currentRouteCount();
|
||||
|
||||
// Skip cloning the 'home' route
|
||||
$reregisterRoutesAction->skipRoute($routeName)->handle();
|
||||
$cloneRoutesAction->skipRoute($routeName);
|
||||
|
||||
// Expect route count to stay the same because the 'home' route re-registration gets skipped
|
||||
$cloneRoutesAction->handle();
|
||||
|
||||
// Expect route count to stay the same because the 'home' route cloning gets skipped
|
||||
expect($initialRouteCount)->toEqual($currentRouteCount());
|
||||
});
|
||||
|
||||
|
|
@ -458,6 +463,31 @@ test('identification middleware works with universal routes only when it impleme
|
|||
$this->withoutExceptionHandling()->get('http://localhost/custom-mw-universal-route');
|
||||
});
|
||||
|
||||
test('routes except nonuniversal routes with path id mw are given the tenant flag after cloning', function (array $routeMiddleware, array $globalMiddleware) {
|
||||
foreach ($globalMiddleware as $middleware) {
|
||||
if ($middleware === 'universal') {
|
||||
config(['tenancy.default_route_mode' => RouteMode::UNIVERSAL]);
|
||||
} else {
|
||||
app(Kernel::class)->pushMiddleware($middleware);
|
||||
}
|
||||
}
|
||||
|
||||
$route = RouteFacade::get('/home', fn () => tenant() ? 'Tenancy initialized.' : 'Tenancy not initialized.')
|
||||
->middleware($routeMiddleware)
|
||||
->name($routeName = 'home');
|
||||
|
||||
app(CloneRoutesAsTenant::class)->handle();
|
||||
|
||||
$clonedRoute = RouteFacade::getRoutes()->getByName('tenant.' . $routeName);
|
||||
|
||||
// Non-universal routes with identification middleware are already considered tenant, so they don't get the tenant flag
|
||||
if (! tenancy()->routeIsUniversal($route) && tenancy()->routeHasIdentificationMiddleware($clonedRoute)) {
|
||||
expect($clonedRoute->middleware())->not()->toContain('tenant');
|
||||
} else {
|
||||
expect($clonedRoute->middleware())->toContain('tenant');
|
||||
}
|
||||
})->with('path identification types');
|
||||
|
||||
foreach ([
|
||||
'domain identification types' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyByDomain::class],
|
||||
'subdomain identification types' => [PreventAccessFromUnwantedDomains::class, InitializeTenancyBySubdomain::class],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue