Skip to content

Commit 2916dbf

Browse files
committed
feature EasyCorp#7412 Introduce an AdminControllerRegistry (javiereguiluz)
This PR was squashed before being merged into the 4.x branch. Discussion ---------- Introduce an AdminControllerRegistry This is the last piece we need to be able to release the 5.x version. `CrudControllerRegistry` and `DashboardControllerRegistry` are mostly internal classes that holds a registry of all the controllers that define a CRUD controller or a Dashboard controller. Some of their features are outdated and make no sense when using pretty URLs. Since 5.x requires pretty URLs and deep Symfony controller integrations via `#[AdminRoute]` we can simplify many things. So, let's merge both registries into a new single `AdminControllerRegistry` that stores everything and provides useful methods that still must be used. Commits ------- 7c6b291 Introduce an AdminControllerRegistry
2 parents a1be12e + 7c6b291 commit 2916dbf

24 files changed

Lines changed: 812 additions & 43 deletions

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ make checks-before-pr
132132
- Braces required for all control structures
133133
- Trailing commas in multi-line arrays
134134
- Blank line before `return` (unless only statement in block)
135+
- Don't add comments in classes as separators (e.g. `// === Methods for dashboards ===`)
135136

136137
### Naming
137138
- Variables/methods: `camelCase`

config/services.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@
8181
use EasyCorp\Bundle\EasyAdminBundle\Orm\EntityUpdater;
8282
use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;
8383
use EasyCorp\Bundle\EasyAdminBundle\Provider\FieldProvider;
84+
use EasyCorp\Bundle\EasyAdminBundle\Registry\AdminControllerRegistry;
8485
use EasyCorp\Bundle\EasyAdminBundle\Registry\CrudControllerRegistry;
85-
use EasyCorp\Bundle\EasyAdminBundle\Registry\DashboardControllerRegistry;
8686
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminRouteGenerator;
8787
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminRouteLoader;
8888
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
@@ -181,7 +181,7 @@
181181
->arg(5, service('cache.easyadmin'))
182182
->arg(6, service(AdminRouteGenerator::class))
183183
->arg(7, '%kernel.build_dir%')
184-
->arg(8, service(CrudControllerRegistry::class))
184+
->arg(8, service(AdminControllerRegistry::class))
185185
->tag('kernel.event_subscriber')
186186

187187
->set(ControllerFactory::class)
@@ -196,11 +196,12 @@
196196
->arg(0, '%kernel.build_dir%')
197197
->arg(1, new Reference('security.token_storage', ContainerInterface::NULL_ON_INVALID_REFERENCE))
198198
->arg(2, new Reference(MenuFactory::class))
199-
->arg(3, new Reference(CrudControllerRegistry::class))
199+
->arg(3, new Reference(AdminControllerRegistry::class))
200200
->arg(4, new Reference(EntityFactory::class))
201201
->arg(5, service(AdminRouteGenerator::class))
202202
->arg(6, service(ActionFactory::class))
203203
->arg(7, service(EntityTranslationIdGeneratorInterface::class))
204+
->arg(8, new Reference(CrudControllerRegistry::class))
204205

205206
->set(AdminUrlGenerator::class)
206207
// I don't know if we truly need the share() method to get a new instance of the
@@ -209,7 +210,7 @@
209210
->share(false)
210211
->arg(0, service(AdminContextProvider::class))
211212
->arg(1, service('router'))
212-
->arg(2, service(DashboardControllerRegistry::class))
213+
->arg(2, service(AdminControllerRegistry::class))
213214
->arg(3, service(AdminRouteGenerator::class))
214215
->arg(4, service('cache.easyadmin'))
215216

@@ -221,6 +222,11 @@
221222
->parent('cache.system')
222223
->tag('cache.pool')
223224

225+
->set(AdminControllerRegistry::class)
226+
->arg(0, '%kernel.build_dir%')
227+
->arg(1, abstract_arg('CRUD controller FQCN to Entity FQCN map'))
228+
->arg(2, abstract_arg('Dashboard controller FQCNs'))
229+
224230
->set(AdminRouteGenerator::class)
225231
->arg(0, tagged_iterator(EasyAdminExtension::TAG_DASHBOARD_CONTROLLER))
226232
->arg(1, tagged_iterator(EasyAdminExtension::TAG_CRUD_CONTROLLER))

src/Context/AdminContext.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use EasyCorp\Bundle\EasyAdminBundle\Dto\MainMenuDto;
1313
use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
1414
use EasyCorp\Bundle\EasyAdminBundle\Dto\UserMenuDto;
15+
use EasyCorp\Bundle\EasyAdminBundle\Registry\AdminControllerRegistry;
1516
use EasyCorp\Bundle\EasyAdminBundle\Registry\CrudControllerRegistry;
1617
use Symfony\Component\HttpFoundation\Request;
1718
use Symfony\Component\Security\Core\User\UserInterface;
@@ -85,8 +86,23 @@ public function getSearch(): ?SearchDto
8586
return $this->crudContext->getSearch();
8687
}
8788

89+
public function getAdminControllers(): AdminControllerRegistry
90+
{
91+
return $this->crudContext->getAdminControllers();
92+
}
93+
94+
/**
95+
* @deprecated since 4.28.1, use getAdminControllers() instead
96+
*/
8897
public function getCrudControllers(): CrudControllerRegistry
8998
{
99+
trigger_deprecation(
100+
'easycorp/easyadmin-bundle',
101+
'4.28.1',
102+
'The "%s()" method is deprecated. Use "getAdminControllers()" instead.',
103+
__METHOD__
104+
);
105+
90106
return $this->crudContext->getCrudControllers();
91107
}
92108

@@ -202,7 +218,7 @@ public function withEntity(EntityDto $entityDto): self
202218
$this->crudContext->getCrud(),
203219
$entityDto,
204220
$this->crudContext->getSearch(),
205-
$this->crudContext->getCrudControllers()
221+
$this->crudContext->getAdminControllers(),
206222
),
207223
$this->dashboardContext,
208224
$this->i18nContext,

src/Context/CrudContext.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use EasyCorp\Bundle\EasyAdminBundle\Dto\CrudDto;
66
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
77
use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
8+
use EasyCorp\Bundle\EasyAdminBundle\Registry\AdminControllerRegistry;
89
use EasyCorp\Bundle\EasyAdminBundle\Registry\CrudControllerRegistry;
910

1011
/**
@@ -19,7 +20,8 @@ public function __construct(
1920
private readonly ?CrudDto $crudDto,
2021
private readonly ?EntityDto $entityDto,
2122
private readonly ?SearchDto $searchDto,
22-
private readonly CrudControllerRegistry $crudControllers,
23+
private readonly AdminControllerRegistry $adminControllers,
24+
private readonly ?CrudControllerRegistry $crudControllers = null,
2325
) {
2426
}
2527

@@ -40,23 +42,36 @@ public function getSearch(): ?SearchDto
4042

4143
public function getCrudControllers(): CrudControllerRegistry
4244
{
45+
if (null === $this->crudControllers) {
46+
throw new \LogicException('The CrudControllerRegistry is not available. This method requires the registry to be injected in the constructor.');
47+
}
48+
4349
return $this->crudControllers;
4450
}
4551

52+
public function getAdminControllers(): AdminControllerRegistry
53+
{
54+
return $this->adminControllers;
55+
}
56+
4657
/**
4758
* Creates a CrudContext instance suitable for testing.
4859
*/
4960
public static function forTesting(
5061
?CrudDto $crudDto = null,
5162
?EntityDto $entityDto = null,
5263
?SearchDto $searchDto = null,
64+
?AdminControllerRegistry $adminControllers = null,
5365
?CrudControllerRegistry $crudControllers = null,
5466
): self {
67+
$adminControllers ??= new AdminControllerRegistry('', [], []);
68+
5569
return new self(
5670
$crudDto ?? new CrudDto(),
5771
$entityDto,
5872
$searchDto,
59-
$crudControllers ?? new CrudControllerRegistry([], [], [], [])
73+
$adminControllers,
74+
$crudControllers ?? new CrudControllerRegistry([], [], [], []),
6075
);
6176
}
6277
}

src/Contracts/Context/AdminContextInterface.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use EasyCorp\Bundle\EasyAdminBundle\Dto\MainMenuDto;
1111
use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
1212
use EasyCorp\Bundle\EasyAdminBundle\Dto\UserMenuDto;
13+
use EasyCorp\Bundle\EasyAdminBundle\Registry\AdminControllerRegistry;
1314
use EasyCorp\Bundle\EasyAdminBundle\Registry\CrudControllerRegistry;
1415
use Symfony\Component\HttpFoundation\Request;
1516
use Symfony\Component\Security\Core\User\UserInterface;
@@ -28,6 +29,13 @@ public function getReferrer(): ?string;
2829

2930
public function getI18n(): I18nDto;
3031

32+
// this method will be introduced in 5.0.0, but the class that implements
33+
// this interface already implements it, so you can use it to smooth upgrade
34+
// public function getAdminControllers(): AdminControllerRegistry;
35+
36+
/**
37+
* @deprecated since 4.28.1, use getAdminControllers() instead
38+
*/
3139
public function getCrudControllers(): CrudControllerRegistry;
3240

3341
/**
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
namespace EasyCorp\Bundle\EasyAdminBundle\Contracts\Registry;
4+
5+
/**
6+
* Unified registry for Dashboard and CRUD controllers.
7+
* Replaces the deprecated DashboardControllerRegistry and CrudControllerRegistry.
8+
*
9+
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
10+
*/
11+
interface AdminControllerRegistryInterface
12+
{
13+
/**
14+
* Returns the route name for the given Dashboard controller FQCN.
15+
*
16+
* @param class-string $dashboardFqcn
17+
*/
18+
public function getDashboardRoute(string $dashboardFqcn): ?string;
19+
20+
/**
21+
* Returns the Dashboard controller FQCN for the given route name.
22+
*
23+
* @return class-string|null
24+
*/
25+
public function getDashboardByRoute(string $routeName): ?string;
26+
27+
/**
28+
* Returns the number of registered Dashboard controllers.
29+
*/
30+
public function getDashboardCount(): int;
31+
32+
/**
33+
* Returns the FQCN of the first registered Dashboard controller.
34+
*
35+
* @return class-string|null
36+
*/
37+
public function getFirstDashboard(): ?string;
38+
39+
/**
40+
* Returns the route name of the first registered Dashboard controller.
41+
*/
42+
public function getFirstDashboardRoute(): ?string;
43+
44+
/**
45+
* Returns all registered Dashboard controller FQCNs.
46+
*
47+
* @return array<class-string>
48+
*/
49+
public function getAllDashboards(): array;
50+
51+
/**
52+
* Returns the CRUD controller FQCN that manages the given entity.
53+
*
54+
* Note: If multiple CRUD controllers manage the same entity, only the
55+
* last one registered will be returned. Use explicit controller references
56+
* when multiple controllers manage the same entity.
57+
*
58+
* @param class-string $entityFqcn
59+
*
60+
* @return class-string|null
61+
*/
62+
public function findCrudControllerByEntity(string $entityFqcn): ?string;
63+
64+
/**
65+
* Returns the entity FQCN managed by the given CRUD controller.
66+
*
67+
* @param class-string $crudControllerFqcn
68+
*
69+
* @return class-string|null
70+
*/
71+
public function findEntityByCrudController(string $crudControllerFqcn): ?string;
72+
73+
/**
74+
* Returns all registered CRUD controller FQCNs.
75+
*
76+
* @return array<class-string>
77+
*/
78+
public function getAllCrudControllers(): array;
79+
}

src/DependencyInjection/CreateControllerRegistriesPass.php

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
namespace EasyCorp\Bundle\EasyAdminBundle\DependencyInjection;
44

5+
use EasyCorp\Bundle\EasyAdminBundle\Registry\AdminControllerRegistry;
56
use EasyCorp\Bundle\EasyAdminBundle\Registry\CrudControllerRegistry;
67
use EasyCorp\Bundle\EasyAdminBundle\Registry\DashboardControllerRegistry;
78
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
89
use Symfony\Component\DependencyInjection\ContainerBuilder;
910

1011
/**
11-
* Creates the services of the Dashboard and CRUD controller registries. They can't
12-
* be defined as normal services because they cause circular dependencies.
12+
* Creates the services of the controller registries. They can't be defined as
13+
* normal services because they cause circular dependencies.
1314
* See https://github.com/EasyCorp/EasyAdminBundle/issues/3541.
1415
*
1516
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
@@ -18,10 +19,31 @@ class CreateControllerRegistriesPass implements CompilerPassInterface
1819
{
1920
public function process(ContainerBuilder $container): void
2021
{
22+
$this->createAdminControllerRegistryService($container);
2123
$this->createDashboardControllerRegistryService($container);
2224
$this->createCrudControllerRegistryService($container);
2325
}
2426

27+
private function createAdminControllerRegistryService(ContainerBuilder $container): void
28+
{
29+
$dashboardControllersFqcn = array_keys($container->findTaggedServiceIds(EasyAdminExtension::TAG_DASHBOARD_CONTROLLER, true));
30+
$crudControllersFqcn = array_keys($container->findTaggedServiceIds(EasyAdminExtension::TAG_CRUD_CONTROLLER, true));
31+
32+
$crudFqcnToEntityFqcnMap = [];
33+
foreach ($crudControllersFqcn as $controllerFqcn) {
34+
$crudFqcnToEntityFqcnMap[$controllerFqcn] = $controllerFqcn::getEntityFqcn();
35+
}
36+
37+
// the service is defined with abstract_arg() placeholders:
38+
// here we only replace the dynamic arguments built from tagged services.
39+
$container->getDefinition(AdminControllerRegistry::class)
40+
->replaceArgument(1, $crudFqcnToEntityFqcnMap)
41+
->replaceArgument(2, $dashboardControllersFqcn);
42+
}
43+
44+
/**
45+
* @deprecated since 4.28.1, use AdminControllerRegistry instead
46+
*/
2547
private function createDashboardControllerRegistryService(ContainerBuilder $container): void
2648
{
2749
$dashboardControllersFqcn = array_keys($container->findTaggedServiceIds(EasyAdminExtension::TAG_DASHBOARD_CONTROLLER, true));
@@ -44,6 +66,9 @@ private function createDashboardControllerRegistryService(ContainerBuilder $cont
4466
]);
4567
}
4668

69+
/**
70+
* @deprecated since 4.28.1, use AdminControllerRegistry instead
71+
*/
4772
private function createCrudControllerRegistryService(ContainerBuilder $container): void
4873
{
4974
$crudControllersFqcn = array_keys($container->findTaggedServiceIds(EasyAdminExtension::TAG_CRUD_CONTROLLER, true));

src/EventListener/AdminRouterSubscriber.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Router\AdminRouteGeneratorInterface;
1010
use EasyCorp\Bundle\EasyAdminBundle\Factory\AdminContextFactory;
1111
use EasyCorp\Bundle\EasyAdminBundle\Factory\ControllerFactory;
12-
use EasyCorp\Bundle\EasyAdminBundle\Registry\CrudControllerRegistry;
12+
use EasyCorp\Bundle\EasyAdminBundle\Registry\AdminControllerRegistry;
1313
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminRouteGenerator;
1414
use Psr\Cache\CacheItemPoolInterface;
1515
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -47,7 +47,7 @@ public function __construct(
4747
private readonly CacheItemPoolInterface $cache,
4848
private readonly AdminRouteGeneratorInterface $adminRouteGenerator,
4949
private readonly string $buildDir,
50-
private readonly CrudControllerRegistry $crudControllerRegistry,
50+
private readonly AdminControllerRegistry $adminControllers,
5151
) {
5252
}
5353

@@ -158,7 +158,7 @@ public function onKernelRequest(RequestEvent $event): void
158158
if (is_subclass_of($entityFqcnOrCrudControllerFqcn, CrudControllerInterface::class)) {
159159
$crudControllerFqcn = $entityFqcnOrCrudControllerFqcn;
160160
} else {
161-
$crudControllerFqcn = $this->crudControllerRegistry->findCrudFqcnByEntityFqcn($entityFqcnOrCrudControllerFqcn);
161+
$crudControllerFqcn = $this->adminControllers->findCrudControllerByEntity($entityFqcnOrCrudControllerFqcn);
162162
}
163163

164164
$prettyUrlRoute = $this->adminRouteGenerator->findRouteName($dashboardControllerFqcn, $crudControllerFqcn, $request->query->get(EA::CRUD_ACTION, ''));

0 commit comments

Comments
 (0)