1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-13 19:04: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:
lukinovec 2023-08-28 13:17:17 +02:00 committed by GitHub
parent 4d4639450e
commit f7d9f02fd4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 222 additions and 111 deletions

View file

@ -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,33 +139,13 @@ 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;
$newRoute = $this->createNewRoute($route);
// 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 {
$newRoute->middleware('tenant');
}
$this->copyMiscRouteProperties($route, $newRoute);
if (! tenancy()->routeHasMiddleware($route, 'tenant')) {
$newRoute->middleware('tenant');
}
$this->copyMiscRouteProperties($route, $newRoute);
}
protected function createNewRoute(Route $route): Route