Skip to content

Commit af40b24

Browse files
committed
bug EasyCorp#7148 Don't generate duplicate routes for customized CRUD routes (javiereguiluz)
This PR was merged into the 4.x branch. Discussion ---------- Don't generate duplicate routes for customized CRUD routes Commits ------- dad6725 Don't generate duplicate routes for customize CRUD routes
2 parents 630b999 + dad6725 commit af40b24

File tree

3 files changed

+151
-0
lines changed

3 files changed

+151
-0
lines changed

src/Router/AdminRouteGenerator.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,25 @@ private function generateAdminRoutes(): array
161161

162162
$crudControllerRouteConfig = $this->getCrudControllerRouteConfig($crudControllerFqcn);
163163
$actionsRouteConfig = array_replace_recursive($defaultRoutesConfig, $this->getCustomActionsConfig($crudControllerFqcn));
164+
165+
// if a controller overrides the name of a route for a built-in action (e.g. 'update' for edit() action)
166+
// remove the default route config for that built-in action (e.g. 'edit'); otherwise, we'd generate two
167+
// different routes for the same built-in action: the custom one ('update') and the default one ('edit')
168+
$builtInActionKeys = array_keys(self::DEFAULT_ROUTES_CONFIG);
169+
foreach ($actionsRouteConfig as $key => $config) {
170+
// $actionsRouteConfig contains both the built-in and custom routes; skip entries that are built-in
171+
if (\in_array($key, $builtInActionKeys, true)) {
172+
continue;
173+
}
174+
175+
$actionName = $config['actionName'];
176+
177+
// if this custom route is for a built-in action (e.g. 'edit'), remove the default route entry associated to it
178+
if (isset($actionsRouteConfig[$actionName]) && \in_array($actionName, $builtInActionKeys, true)) {
179+
unset($actionsRouteConfig[$actionName]);
180+
}
181+
}
182+
164183
// by default, the 'detail' route uses a catch-all route pattern (/{entityId});
165184
// so, if the user hasn't customized the 'detail' route path, we need to sort the actions
166185
// to make sure that the 'detail' action is always the last one
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace EasyCorp\Bundle\EasyAdminBundle\Tests\AdminRouteTestApplication\Controller;
4+
5+
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminRoute;
6+
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
7+
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
8+
use EasyCorp\Bundle\EasyAdminBundle\Tests\AdminRouteTestApplication\Entity\Product;
9+
10+
/**
11+
* Test CRUD controller that overrides the route names and paths of built-in CRUD actions.
12+
* This tests that when a built-in action has a custom route, only the custom route is generated
13+
* and the default route is NOT generated (avoiding duplicates).
14+
*/
15+
class BuiltInActionCrudController extends AbstractCrudController
16+
{
17+
public static function getEntityFqcn(): string
18+
{
19+
return Product::class;
20+
}
21+
22+
// override the 'index' action with a custom route name
23+
#[AdminRoute(name: 'list')]
24+
public function index(AdminContext $context)
25+
{
26+
return parent::index($context);
27+
}
28+
29+
// override the 'new' action with a custom route name and path
30+
#[AdminRoute(path: '/create', name: 'create')]
31+
public function new(AdminContext $context)
32+
{
33+
return parent::new($context);
34+
}
35+
36+
// override the 'edit' action with a custom route name
37+
#[AdminRoute(name: 'update')]
38+
public function edit(AdminContext $context)
39+
{
40+
return parent::edit($context);
41+
}
42+
43+
// override the 'detail' action with a custom route name
44+
#[AdminRoute(name: 'show')]
45+
public function detail(AdminContext $context)
46+
{
47+
return parent::detail($context);
48+
}
49+
50+
// keep 'delete' action with default route (no override) to test mixed scenarios
51+
}

tests/Functional/AdminRouteTest.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,4 +478,85 @@ public function testMultipleAdminRoutesOnSameCrudAction(): void
478478
$this->assertNotNull($router->getRouteCollection()->get('second_admin_multiple_route_action2_path2'));
479479
$this->assertNotNull($router->getRouteCollection()->get('second_admin_multiple_route_action2_path3'));
480480
}
481+
482+
public function testBuiltInActionsWithCustomRouteNames(): void
483+
{
484+
$client = static::createClient();
485+
$router = $client->getContainer()->get('router');
486+
487+
// Test that when built-in actions have custom route names, only the custom routes are generated
488+
// and the default routes are NOT generated (avoiding duplicates)
489+
490+
// 'index' action was customized with route name 'list' (path not customized, so it uses the default '/')
491+
$indexCustomRoute = $router->getRouteCollection()->get('admin_built_in_action_list');
492+
$this->assertNotNull($indexCustomRoute, 'Custom route for index action should exist');
493+
$this->assertSame('/admin/built-in-action/index', $indexCustomRoute->getPath());
494+
$this->assertSame(
495+
'EasyCorp\Bundle\EasyAdminBundle\Tests\AdminRouteTestApplication\Controller\BuiltInActionCrudController::index',
496+
$indexCustomRoute->getDefault('_controller')
497+
);
498+
499+
// The default 'index' route should NOT exist
500+
$indexDefaultRoute = $router->getRouteCollection()->get('admin_built_in_action_index');
501+
$this->assertNull($indexDefaultRoute, 'Default route for index action should NOT exist when overridden');
502+
503+
// 'new' action was customized with route name 'create' and path '/create'
504+
$newCustomRoute = $router->getRouteCollection()->get('admin_built_in_action_create');
505+
$this->assertNotNull($newCustomRoute, 'Custom route for new action should exist');
506+
$this->assertSame('/admin/built-in-action/create', $newCustomRoute->getPath());
507+
$this->assertSame(
508+
'EasyCorp\Bundle\EasyAdminBundle\Tests\AdminRouteTestApplication\Controller\BuiltInActionCrudController::new',
509+
$newCustomRoute->getDefault('_controller')
510+
);
511+
512+
// The default 'new' route should NOT exist
513+
$newDefaultRoute = $router->getRouteCollection()->get('admin_built_in_action_new');
514+
$this->assertNull($newDefaultRoute, 'Default route for new action should NOT exist when overridden');
515+
516+
// 'edit' action was customized with route name 'update' (path not customized, so it auto-generates based on action name)
517+
$editCustomRoute = $router->getRouteCollection()->get('admin_built_in_action_update');
518+
$this->assertNotNull($editCustomRoute, 'Custom route for edit action should exist');
519+
$this->assertSame('/admin/built-in-action/edit', $editCustomRoute->getPath());
520+
$this->assertSame(
521+
'EasyCorp\Bundle\EasyAdminBundle\Tests\AdminRouteTestApplication\Controller\BuiltInActionCrudController::edit',
522+
$editCustomRoute->getDefault('_controller')
523+
);
524+
525+
// The default 'edit' route should NOT exist
526+
$editDefaultRoute = $router->getRouteCollection()->get('admin_built_in_action_edit');
527+
$this->assertNull($editDefaultRoute, 'Default route for edit action should NOT exist when overridden');
528+
529+
// 'detail' action was customized with route name 'show' (path not customized, so it auto-generates based on action name)
530+
$detailCustomRoute = $router->getRouteCollection()->get('admin_built_in_action_show');
531+
$this->assertNotNull($detailCustomRoute, 'Custom route for detail action should exist');
532+
$this->assertSame('/admin/built-in-action/detail', $detailCustomRoute->getPath());
533+
$this->assertSame(
534+
'EasyCorp\Bundle\EasyAdminBundle\Tests\AdminRouteTestApplication\Controller\BuiltInActionCrudController::detail',
535+
$detailCustomRoute->getDefault('_controller')
536+
);
537+
538+
// The default 'detail' route should NOT exist
539+
$detailDefaultRoute = $router->getRouteCollection()->get('admin_built_in_action_detail');
540+
$this->assertNull($detailDefaultRoute, 'Default route for detail action should NOT exist when overridden');
541+
542+
// 'delete' action was NOT customized, so it should use the default route
543+
$deleteDefaultRoute = $router->getRouteCollection()->get('admin_built_in_action_delete');
544+
$this->assertNotNull($deleteDefaultRoute, 'Default route for delete action should exist when NOT overridden');
545+
$this->assertSame('/admin/built-in-action/{entityId}/delete', $deleteDefaultRoute->getPath());
546+
$this->assertSame(
547+
'EasyCorp\Bundle\EasyAdminBundle\Tests\AdminRouteTestApplication\Controller\BuiltInActionCrudController::delete',
548+
$deleteDefaultRoute->getDefault('_controller')
549+
);
550+
551+
// Test that routes work for second dashboard too
552+
$this->assertNotNull($router->getRouteCollection()->get('second_admin_built_in_action_list'));
553+
$this->assertNull($router->getRouteCollection()->get('second_admin_built_in_action_index'));
554+
$this->assertNotNull($router->getRouteCollection()->get('second_admin_built_in_action_create'));
555+
$this->assertNull($router->getRouteCollection()->get('second_admin_built_in_action_new'));
556+
$this->assertNotNull($router->getRouteCollection()->get('second_admin_built_in_action_update'));
557+
$this->assertNull($router->getRouteCollection()->get('second_admin_built_in_action_edit'));
558+
$this->assertNotNull($router->getRouteCollection()->get('second_admin_built_in_action_show'));
559+
$this->assertNull($router->getRouteCollection()->get('second_admin_built_in_action_detail'));
560+
$this->assertNotNull($router->getRouteCollection()->get('second_admin_built_in_action_delete'));
561+
}
481562
}

0 commit comments

Comments
 (0)