true]` by default) */ class TenancyUrlGenerator extends UrlGenerator { /** * Parameter which bypasses the behavior modification of route() and temporarySignedRoute(). * * E.g. route('tenant') => app.test/{tenant}/tenant (or app.test/tenant?tenant=tenantKey if the route doesn't accept the tenant parameter) * route('tenant', [$bypassParameter => true]) => app.test/tenant. */ public static string $bypassParameter = 'central'; /** * Determine if the route names passed to `route()` or `temporarySignedRoute()` * should get prefixed with the tenant route name prefix. * * This is useful when using path identification with packages that generate URLs, * like Jetstream, so that you don't have to manually prefix route names passed to each route() call. */ public static bool $prefixRouteNames = false; /** * Determine if the tenant parameter should get passed * to the links generated by `route()` or `temporarySignedRoute()` whenever available * (enabled by default – works with both path and query string identification). * * With path identification, you can disable this and use URL::defaults() instead (as an alternative solution). */ public static bool $passTenantParameterToRoutes = true; /** * Override the route() method so that the route name gets prefixed * and the tenant parameter gets added when in tenant context. */ public function route($name, $parameters = [], $absolute = true) { [$name, $parameters] = $this->prepareRouteInputs($name, Arr::wrap($parameters)); return parent::route($name, $parameters, $absolute); } /** * Override the temporarySignedRoute() method so that the route name gets prefixed * and the tenant parameter gets added when in tenant context. */ public function temporarySignedRoute($name, $expiration, $parameters = [], $absolute = true) { [$name, $parameters] = $this->prepareRouteInputs($name, Arr::wrap($parameters)); return parent::temporarySignedRoute($name, $expiration, $parameters, $absolute); } /** * Return bool indicating if the bypass parameter was in $parameters. */ protected function routeBehaviorModificationBypassed(mixed $parameters): bool { if (isset($parameters[static::$bypassParameter])) { return (bool) $parameters[static::$bypassParameter]; } return false; } /** * Takes a route name and an array of parameters to return the prefixed route name * and the route parameters with the tenant parameter added. * * To skip these modifications, pass the bypass parameter in route parameters. * Before returning the modified route inputs, the bypass parameter is removed from the parameters. */ protected function prepareRouteInputs(string $name, array $parameters): array { if (! $this->routeBehaviorModificationBypassed($parameters)) { $name = $this->prefixRouteName($name); $parameters = $this->addTenantParameter($parameters); } // Remove bypass parameter from the route parameters unset($parameters[static::$bypassParameter]); return [$name, $parameters]; } /** * If $prefixRouteNames is true, prefix the passed route name. */ protected function prefixRouteName(string $name): string { $tenantPrefix = PathTenantResolver::tenantRouteNamePrefix(); if (static::$prefixRouteNames && ! str($name)->startsWith($tenantPrefix)) { $name = str($name)->after($tenantPrefix)->prepend($tenantPrefix)->toString(); } return $name; } /** * If `tenant()` isn't null, add tenant paramter to the passed parameters. */ protected function addTenantParameter(array $parameters): array { return tenant() && static::$passTenantParameterToRoutes ? array_merge($parameters, [PathTenantResolver::tenantParameterName() => tenant()->getTenantKey()]) : $parameters; } }