diff --git a/src/Attribute/AdminRoute.php b/src/Attribute/AdminRoute.php index 48d622e71c..49008e00e6 100644 --- a/src/Attribute/AdminRoute.php +++ b/src/Attribute/AdminRoute.php @@ -53,6 +53,20 @@ public function __construct( * - [FooDashboard::class, BarDashboard::class, ...]: Deny specific dashboards */ public array|false|null $deniedDashboards = false, + + /** + * @var string[]|null $allowedActions If defined at the class level, admin routes will be available only for the specified actions. Possible values: + * - null (default): Not set - allow all actions + * - [Action::INDEX, Action::DELETE, 'customAction', ...]: Allow only specific actions + */ + public ?array $allowedActions = null, + + /** + * @var string[]|null $deniedActions If defined at the class level, admin routes won't be available for the specified actions. Possible values: + * - null (default): Not set - deny no actions + * - [Action::INDEX, Action::DELETE, 'customAction', ...]: Deny specific actions + */ + public ?array $deniedActions = null, ) { } } diff --git a/src/Router/AdminRouteGenerator.php b/src/Router/AdminRouteGenerator.php index fd3ce925d6..c140ae286d 100644 --- a/src/Router/AdminRouteGenerator.php +++ b/src/Router/AdminRouteGenerator.php @@ -214,6 +214,14 @@ private function generateAdminRoutes(): array foreach (array_keys($actionsRouteConfig) as $actionName) { $actionRouteConfig = $actionsRouteConfig[$actionName]; + + if (null !== $crudControllerRouteConfig['allowedActions'] && !\in_array($actionRouteConfig['actionName'], $crudControllerRouteConfig['allowedActions'], true)) { + continue; + } + if (null !== $crudControllerRouteConfig['deniedActions'] && \in_array($actionRouteConfig['actionName'], $crudControllerRouteConfig['deniedActions'], true)) { + continue; + } + $actionNameSnakeCase = strtolower(preg_replace('/[A-Z]/', '_$0', $actionRouteConfig['actionName'])); $actionNameSlug = strtolower(preg_replace('/[A-Z]/', '-$0', $actionRouteConfig['actionName'])); @@ -583,11 +591,11 @@ private function getDashboardsRouteConfig(): array /** * @param class-string $crudControllerFqcn * - * @return array{routeName: string, routePath: string} + * @return array{routeName: string, routePath: string, allowedActions: string[]|null, deniedActions: string[]|null} */ private function getCrudControllerRouteConfig(string $crudControllerFqcn): array { - $crudControllerConfig = []; + $crudControllerConfig = ['allowedActions' => null, 'deniedActions' => null]; // Default to not set $reflectionClass = new \ReflectionClass($crudControllerFqcn); @@ -608,6 +616,12 @@ private function getCrudControllerRouteConfig(string $crudControllerFqcn): array $crudControllerConfig['routeName'] = trim($adminRouteInstance->name, '_'); } + + if (null !== $adminRouteInstance->allowedActions && null !== $adminRouteInstance->deniedActions) { + throw new \RuntimeException(sprintf('In the #[AdminRoute] attribute of the "%s" CRUD controller, you cannot define both "allowedActions" and "deniedActions" at the same time because they are the exact opposite. Use only one of them.', $crudControllerFqcn)); + } + $crudControllerConfig['allowedActions'] = $adminRouteInstance->allowedActions; + $crudControllerConfig['deniedActions'] = $adminRouteInstance->deniedActions; } // if the CRUD controller doesn't define any or all of the route configuration,