77use EasyCorp \Bundle \EasyAdminBundle \Attribute \AdminDashboard ;
88use EasyCorp \Bundle \EasyAdminBundle \Config \Option \EA ;
99use EasyCorp \Bundle \EasyAdminBundle \Contracts \Router \AdminRouteGeneratorInterface ;
10+ use Psr \Cache \CacheItemPoolInterface ;
1011use Symfony \Component \Routing \Route ;
1112use Symfony \Component \Routing \RouteCollection ;
1213
2223 */
2324final class AdminRouteGenerator implements AdminRouteGeneratorInterface
2425{
26+ public const ADMIN_ROUTES_CACHE_KEY = 'easyadmin.generated_routes ' ;
2527 private const DEFAULT_ROUTES_CONFIG = [
2628 'index ' => [
2729 'routePath ' => '/ ' ,
@@ -63,13 +65,59 @@ final class AdminRouteGenerator implements AdminRouteGeneratorInterface
6365 public function __construct (
6466 private iterable $ dashboardControllers ,
6567 private iterable $ crudControllers ,
68+ private CacheItemPoolInterface $ cache ,
6669 ) {
6770 }
6871
6972 public function generateAll (): RouteCollection
7073 {
7174 $ collection = new RouteCollection ();
75+ $ adminRoutes = $ this ->generateAdminRoutes ();
76+
77+ foreach ($ adminRoutes as $ routeName => $ route ) {
78+ $ collection ->add ($ routeName , $ route );
79+ }
80+
81+ // save the generated routes in the cache; this will allow to detect
82+ // if pretty URLs are being used in the application and also improves
83+ // performance when finding a route name using the {dashboard, CRUD controller, action} tuple
84+ $ adminRoutesCache = [];
85+ foreach ($ adminRoutes as $ routeName => $ route ) {
86+ $ adminRoutesCache [$ route ->getOption (EA ::DASHBOARD_CONTROLLER_FQCN )][$ route ->getOption (EA ::CRUD_CONTROLLER_FQCN )][$ route ->getOption (EA ::CRUD_ACTION )] = $ routeName ;
87+ }
88+ $ cachedAdminRoutes = $ this ->cache ->getItem (self ::ADMIN_ROUTES_CACHE_KEY );
89+ $ cachedAdminRoutes ->set ($ adminRoutesCache );
90+ $ this ->cache ->save ($ cachedAdminRoutes );
91+
92+ return $ collection ;
93+ }
94+
95+ // Temporary utility method to be removed in EasyAdmin 5, when the pretty URLs will be mandatory
96+ // TODO: remove this method in EasyAdmin 5.x
97+ public function usesPrettyUrls (): bool
98+ {
99+ $ cachedAdminRoutes = $ this ->cache ->getItem (self ::ADMIN_ROUTES_CACHE_KEY )->get ();
100+
101+ return null !== $ cachedAdminRoutes && [] !== $ cachedAdminRoutes ;
102+ }
103+
104+ public function findRouteName (string $ dashboardFqcn , string $ crudControllerFqcn , string $ actionName ): ?string
105+ {
106+ $ adminRoutes = $ this ->cache ->getItem (self ::ADMIN_ROUTES_CACHE_KEY )->get ();
107+
108+ return $ adminRoutes [$ dashboardFqcn ][$ crudControllerFqcn ][$ actionName ] ?? null ;
109+ }
110+
111+ /**
112+ * @return array<string, Route>
113+ */
114+ private function generateAdminRoutes (): array
115+ {
116+ /** @var array<string, Route> $adminRoutes Stores the collection of admin routes created for the app */
117+ $ adminRoutes = [];
118+ /** @var array<string> $addedRouteNames Temporary cache that stores the route names to ensure that we don't add duplicated admin routes */
72119 $ addedRouteNames = [];
120+
73121 foreach ($ this ->dashboardControllers as $ dashboardController ) {
74122 $ dashboardFqcn = $ dashboardController ::class;
75123 [$ allowedCrudControllers , $ deniedCrudControllers ] = $ this ->getAllowedAndDeniedControllers ($ dashboardFqcn );
@@ -104,8 +152,12 @@ public function generateAll(): RouteCollection
104152
105153 foreach (array_keys ($ actionsRouteConfig ) as $ actionName ) {
106154 $ actionRouteConfig = $ actionsRouteConfig [$ actionName ];
107- $ crudActionPath = sprintf ('%s/%s/%s ' , $ dashboardRouteConfig ['routePath ' ], $ crudControllerRouteConfig ['routePath ' ], ltrim ($ actionRouteConfig ['routePath ' ], '/ ' ));
108- $ crudActionRouteName = sprintf ('%s_%s_%s ' , $ dashboardRouteConfig ['routeName ' ], $ crudControllerRouteConfig ['routeName ' ], $ actionRouteConfig ['routeName ' ]);
155+ $ adminRoutePath = sprintf ('%s/%s/%s ' , $ dashboardRouteConfig ['routePath ' ], $ crudControllerRouteConfig ['routePath ' ], ltrim ($ actionRouteConfig ['routePath ' ], '/ ' ));
156+ $ adminRouteName = sprintf ('%s_%s_%s ' , $ dashboardRouteConfig ['routeName ' ], $ crudControllerRouteConfig ['routeName ' ], $ actionRouteConfig ['routeName ' ]);
157+
158+ if (\in_array ($ adminRouteName , $ addedRouteNames , true )) {
159+ throw new \RuntimeException (sprintf ('When using pretty URLs, all CRUD controllers must have unique PHP class names to generate unique route names. However, your application has at least two controllers with the FQCN "%s", generating the route "%s". Even if both CRUD controllers are in different namespaces, they cannot have the same class name. Rename one of these controllers to resolve the issue. ' , $ crudControllerFqcn , $ adminRouteName ));
160+ }
109161
110162 $ defaults = [
111163 '_controller ' => $ crudControllerFqcn .':: ' .$ actionName ,
@@ -117,34 +169,14 @@ public function generateAll(): RouteCollection
117169 EA ::CRUD_ACTION => $ actionName ,
118170 ];
119171
120- $ route = new Route ($ crudActionPath , defaults: $ defaults , options: $ options , methods: $ actionRouteConfig ['methods ' ]);
121-
122- if (\in_array ($ crudActionRouteName , $ addedRouteNames , true )) {
123- throw new \RuntimeException (sprintf ('When using pretty URLs, all CRUD controllers must have unique PHP class names to generate unique route names. However, your application has at least two controllers with the FQCN "%s", generating the route "%s". Even if both CRUD controllers are in different namespaces, they cannot have the same class name. Rename one of these controllers to resolve the issue. ' , $ crudControllerFqcn , $ crudActionRouteName ));
124- }
125-
126- $ collection ->add ($ crudActionRouteName , $ route );
127- $ addedRouteNames [] = $ crudActionRouteName ;
172+ $ adminRoute = new Route ($ adminRoutePath , defaults: $ defaults , options: $ options , methods: $ actionRouteConfig ['methods ' ]);
173+ $ adminRoutes [$ adminRouteName ] = $ adminRoute ;
174+ $ addedRouteNames [] = $ adminRouteName ;
128175 }
129176 }
130177 }
131178
132- return $ collection ;
133- }
134-
135- public function findRouteName (string $ dashboardFqcn , string $ crudControllerFqcn , string $ actionName ): ?string
136- {
137- $ defaultRoutesConfig = $ this ->getDefaultRoutesConfig ($ dashboardFqcn );
138- $ actionsRouteConfig = array_replace_recursive ($ defaultRoutesConfig , $ this ->getCustomActionsConfig ($ crudControllerFqcn ));
139- if (!isset ($ actionsRouteConfig [$ actionName ])) {
140- return null ;
141- }
142-
143- $ dashboardRouteConfig = $ this ->getDashboardsRouteConfig ()[$ dashboardFqcn ];
144- $ crudControllerRouteConfig = $ this ->getCrudControllerRouteConfig ($ crudControllerFqcn );
145- $ actionRouteConfig = $ actionsRouteConfig [$ actionName ];
146-
147- return sprintf ('%s_%s_%s ' , $ dashboardRouteConfig ['routeName ' ], $ crudControllerRouteConfig ['routeName ' ], $ actionRouteConfig ['routeName ' ]);
179+ return $ adminRoutes ;
148180 }
149181
150182 /**
0 commit comments