Skip to content

Commit 9963374

Browse files
Align new route paths and add a bc layer to keep legacy paths (#1051)
| Q | A | --------------- | ----- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Related tickets | | License | MIT To allow both new and old routing systems working together, we defines new route conditions. **on legacy routes** ``` sylius_admin_product: resource: | # ... condition: "context.isSyliusRoutingBcLayerEnabled('admin_product')" type: sylius.resource ``` **on new routes** ```php return (new ResourceMetadata()) >withClass('%sylius.model.product.class%') // ... ->withRouteCondition("not context.isSyliusRoutingBcLayerEnabled('admin_product')") ; ``` But to make that works, we need to have two different route names. Otherwise, there is only one Symfony route with the same condition. Here is an example of what we need: - legacy one: `sylius_admin_product_index` (we keep the orginal name) - new one: `_sylius_admin_product_index` (we prefix the name). **bc-layer disabled** ![image](https://github.com/user-attachments/assets/b1dca027-fe04-4c8d-b675-6996733cc118) **bc-layer enabled** ![image](https://github.com/user-attachments/assets/e469f043-0bc9-4e0e-a135-7a4e3811fc7b) But we need the **both paths match exactly** (and http methods too). That's the reason of that current PR. **Bc-layer enabled** <img width="840" height="129" alt="image" src="https://github.com/user-attachments/assets/0cba7f37-8a81-4a25-becf-b4f653e0a090" /> **Bc-layer disabled** <img width="862" height="133" alt="image" src="https://github.com/user-attachments/assets/84639e9d-8d66-4d66-ad12-5e127e5f3aae" /> That seems to work on Sylius E-commerce. The two errors I have are solved in another PR. <img width="1510" height="1206" alt="image" src="https://github.com/user-attachments/assets/6b066851-2bcf-46e3-8a9f-fe63b1305064" />
2 parents cbc846c + 0599c55 commit 9963374

File tree

5 files changed

+236
-13
lines changed

5 files changed

+236
-13
lines changed

src/Bundle/DependencyInjection/Configuration.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ public function getConfigTreeBuilder(): TreeBuilder
5151
->defaultValue('sylius.resource_controller.authorization_checker.disabled')
5252
->cannotBeEmpty()
5353
->end()
54+
->booleanNode('routing_path_bc_layer')
55+
->defaultTrue()
56+
->end()
5457
->end()
5558
;
5659

src/Bundle/DependencyInjection/SyliusResourceExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public function load(array $configs, ContainerBuilder $container): void
6767

6868
$container->setParameter('sylius.resource.mapping', $config['mapping']);
6969
$container->setParameter('sylius.resource.settings', $config['settings']);
70+
$container->setParameter('sylius.routing_path_bc_layer', $config['routing_path_bc_layer']);
7071
$container->setAlias('sylius.resource_controller.authorization_checker', $config['authorization_checker']);
7172

7273
$this->registerMetadataConfiguration($container, $config);

src/Bundle/Resources/config/services/routing.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<service class="Sylius\Bundle\ResourceBundle\Routing\RouteFactory" />
2626
</argument>
2727
<argument>%kernel.environment%</argument>
28+
<argument>%sylius.routing_path_bc_layer%</argument>
2829
<tag name="routing.loader" />
2930
<deprecated package="sylius/resource" version="1.13">The "%service_id%" service is deprecated since sylius/resource-bundle 1.13 and will be removed in sylius/resource-bundle 2.0. Use "sylius.symfony.routing.loader.resource" instead.</deprecated>
3031
</service>

src/Bundle/Routing/ResourceLoader.php

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,11 @@
2727
*/
2828
final class ResourceLoader extends Loader
2929
{
30-
private RegistryInterface $resourceRegistry;
31-
32-
private RouteFactoryInterface $routeFactory;
33-
3430
public function __construct(
35-
RegistryInterface $resourceRegistry,
36-
RouteFactoryInterface $routeFactory,
31+
private RegistryInterface $resourceRegistry,
32+
private RouteFactoryInterface $routeFactory,
3733
?string $env = null,
34+
private ?bool $routingPathBcLayer = null,
3835
) {
3936
parent::__construct($env);
4037

@@ -69,36 +66,55 @@ public function load($resource, $type = null): RouteCollection
6966
$metadata = $this->resourceRegistry->get($configuration['alias']);
7067
$routes = $this->routeFactory->createRouteCollection();
7168

72-
$rootPath = sprintf('/%s/', $configuration['path'] ?? Urlizer::urlize($metadata->getPluralName()));
69+
$rootPath = sprintf('/%s', $configuration['path'] ?? Urlizer::urlize($metadata->getPluralName()));
7370
$identifier = sprintf('{%s}', $configuration['identifier']);
7471

72+
/** @var bool $bcLayerEnabled */
73+
$bcLayerEnabled = $this->routingPathBcLayer ?? true;
74+
$trailingSlash = $bcLayerEnabled ? '/' : '';
75+
7576
if (in_array('index', $routesToGenerate, true)) {
76-
$indexRoute = $this->createRoute($metadata, $configuration, $rootPath, 'index', ['GET'], $isApi);
77+
$indexRoute = $this->createRoute($metadata, $configuration, $rootPath . $trailingSlash, 'index', ['GET'], $isApi);
7778
$routes->add($this->getRouteName($metadata, $configuration, 'index'), $indexRoute);
7879
}
7980

8081
if (in_array('create', $routesToGenerate, true)) {
81-
$createRoute = $this->createRoute($metadata, $configuration, $isApi ? $rootPath : $rootPath . 'new', 'create', $isApi ? ['POST'] : ['GET', 'POST'], $isApi);
82+
$createRoute = $this->createRoute($metadata, $configuration, $isApi ? $rootPath . $trailingSlash : $rootPath . '/new', 'create', $isApi ? ['POST'] : ['GET', 'POST'], $isApi);
8283
$routes->add($this->getRouteName($metadata, $configuration, 'create'), $createRoute);
8384
}
8485

8586
if (in_array('update', $routesToGenerate, true)) {
86-
$updateRoute = $this->createRoute($metadata, $configuration, $isApi ? $rootPath . $identifier : $rootPath . $identifier . '/edit', 'update', $isApi ? ['PUT', 'PATCH'] : ['GET', 'PUT', 'PATCH'], $isApi);
87+
$httpMethods = ['GET', 'PUT', 'PATCH'];
88+
if (!$bcLayerEnabled) {
89+
$httpMethods[] = 'POST';
90+
}
91+
92+
$updateRoute = $this->createRoute($metadata, $configuration, $isApi ? $rootPath . '/' . $identifier : $rootPath . '/' . $identifier . '/edit', 'update', $isApi ? ['PUT', 'PATCH'] : $httpMethods, $isApi);
8793
$routes->add($this->getRouteName($metadata, $configuration, 'update'), $updateRoute);
8894
}
8995

9096
if (in_array('show', $routesToGenerate, true)) {
91-
$showRoute = $this->createRoute($metadata, $configuration, $rootPath . $identifier, 'show', ['GET'], $isApi);
97+
$showRoute = $this->createRoute($metadata, $configuration, $rootPath . '/' . $identifier, 'show', ['GET'], $isApi);
9298
$routes->add($this->getRouteName($metadata, $configuration, 'show'), $showRoute);
9399
}
94100

95101
if (!$isApi && in_array('bulkDelete', $routesToGenerate, true)) {
96-
$bulkDeleteRoute = $this->createRoute($metadata, $configuration, $rootPath . 'bulk-delete', 'bulkDelete', ['DELETE'], $isApi);
102+
$httpMethods = ['DELETE'];
103+
if (!$bcLayerEnabled) {
104+
$httpMethods[] = 'POST';
105+
}
106+
107+
$bulkDeleteRoute = $this->createRoute($metadata, $configuration, $rootPath . '/' . ($bcLayerEnabled ? 'bulk-delete' : 'bulk_delete'), 'bulkDelete', $httpMethods, $isApi);
97108
$routes->add($this->getRouteName($metadata, $configuration, 'bulk_delete'), $bulkDeleteRoute);
98109
}
99110

100111
if (in_array('delete', $routesToGenerate, true)) {
101-
$deleteRoute = $this->createRoute($metadata, $configuration, $rootPath . $identifier, 'delete', ['DELETE'], $isApi);
112+
$httpMethods = ['DELETE'];
113+
if (!$bcLayerEnabled) {
114+
$httpMethods[] = 'POST';
115+
}
116+
117+
$deleteRoute = $this->createRoute($metadata, $configuration, $isApi ? $rootPath . '/' . $identifier : $rootPath . '/' . $identifier . ($bcLayerEnabled ? '' : '/delete'), 'delete', $isApi ? ['DELETE'] : $httpMethods, $isApi);
102118
$routes->add($this->getRouteName($metadata, $configuration, 'delete'), $deleteRoute);
103119
}
104120

src/Bundle/spec/Routing/ResourceLoaderSpec.php

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,208 @@ function it_generates_routing_based_on_resource_configuration(
168168
$this->load($configuration, 'sylius.resource')->shouldReturn($routeCollection);
169169
}
170170

171+
function it_generates_new_routing_paths_if_bc_layer_is_disabled(
172+
RegistryInterface $resourceRegistry,
173+
MetadataInterface $metadata,
174+
RouteFactoryInterface $routeFactory,
175+
RouteCollection $routeCollection,
176+
Route $showRoute,
177+
Route $indexRoute,
178+
Route $createRoute,
179+
Route $updateRoute,
180+
Route $bulkDeleteRoute,
181+
Route $deleteRoute,
182+
): void {
183+
$this->beConstructedWith($resourceRegistry, $routeFactory, null, false);
184+
185+
$resourceRegistry->get('sylius.product')->willReturn($metadata);
186+
$metadata->getApplicationName()->willReturn('sylius');
187+
$metadata->getName()->willReturn('product');
188+
$metadata->getPluralName()->willReturn('products');
189+
$metadata->getServiceId('controller')->willReturn('sylius.controller.product');
190+
191+
$routeFactory->createRouteCollection()->willReturn($routeCollection);
192+
193+
$configuration =
194+
<<<EOT
195+
alias: sylius.product
196+
EOT;
197+
198+
$showDefaults = [
199+
'_controller' => 'sylius.controller.product::showAction',
200+
'_sylius' => [
201+
'permission' => false,
202+
],
203+
];
204+
$routeFactory
205+
->createRoute('/products/{id}', $showDefaults, [], [], '', [], ['GET'], '')
206+
->willReturn($showRoute)
207+
;
208+
$routeCollection->add('sylius_product_show', $showRoute)->shouldBeCalled();
209+
210+
$indexDefaults = [
211+
'_controller' => 'sylius.controller.product::indexAction',
212+
'_sylius' => [
213+
'permission' => false,
214+
],
215+
];
216+
$routeFactory
217+
->createRoute('/products', $indexDefaults, [], [], '', [], ['GET'], '')
218+
->willReturn($indexRoute)
219+
;
220+
$routeCollection->add('sylius_product_index', $indexRoute)->shouldBeCalled();
221+
222+
$createDefaults = [
223+
'_controller' => 'sylius.controller.product::createAction',
224+
'_sylius' => [
225+
'permission' => false,
226+
],
227+
];
228+
$routeFactory
229+
->createRoute('/products/new', $createDefaults, [], [], '', [], ['GET', 'POST'], '')
230+
->willReturn($createRoute)
231+
;
232+
$routeCollection->add('sylius_product_create', $createRoute)->shouldBeCalled();
233+
234+
$updateDefaults = [
235+
'_controller' => 'sylius.controller.product::updateAction',
236+
'_sylius' => [
237+
'permission' => false,
238+
],
239+
];
240+
$routeFactory
241+
->createRoute('/products/{id}/edit', $updateDefaults, [], [], '', [], ['GET', 'PUT', 'PATCH', 'POST'], '')
242+
->willReturn($updateRoute)
243+
;
244+
$routeCollection->add('sylius_product_update', $updateRoute)->shouldBeCalled();
245+
246+
$bulkDeleteDefaults = [
247+
'_controller' => 'sylius.controller.product::bulkDeleteAction',
248+
'_sylius' => [
249+
'permission' => false,
250+
'paginate' => false,
251+
'repository' => [
252+
'method' => 'findById',
253+
'arguments' => ['$ids'],
254+
],
255+
],
256+
];
257+
$routeFactory
258+
->createRoute('/products/bulk_delete', $bulkDeleteDefaults, [], [], '', [], ['DELETE', 'POST'], '')
259+
->willReturn($bulkDeleteRoute)
260+
;
261+
$routeCollection->add('sylius_product_bulk_delete', $bulkDeleteRoute)->shouldBeCalled();
262+
263+
$deleteDefaults = [
264+
'_controller' => 'sylius.controller.product::deleteAction',
265+
'_sylius' => [
266+
'permission' => false,
267+
],
268+
];
269+
$routeFactory
270+
->createRoute('/products/{id}/delete', $deleteDefaults, [], [], '', [], ['DELETE', 'POST'], '')
271+
->willReturn($deleteRoute)
272+
;
273+
$routeCollection->add('sylius_product_delete', $deleteRoute)->shouldBeCalled();
274+
275+
$this->load($configuration, 'sylius.resource')->shouldReturn($routeCollection);
276+
}
277+
278+
function it_generates_new_api_routing_paths_if_bc_layer_is_disabled(
279+
RegistryInterface $resourceRegistry,
280+
MetadataInterface $metadata,
281+
RouteFactoryInterface $routeFactory,
282+
RouteCollection $routeCollection,
283+
Route $showRoute,
284+
Route $indexRoute,
285+
Route $createRoute,
286+
Route $updateRoute,
287+
Route $bulkDeleteRoute,
288+
Route $deleteRoute,
289+
): void {
290+
$this->beConstructedWith($resourceRegistry, $routeFactory, null, false);
291+
292+
$resourceRegistry->get('sylius.product')->willReturn($metadata);
293+
$metadata->getApplicationName()->willReturn('sylius');
294+
$metadata->getName()->willReturn('product');
295+
$metadata->getPluralName()->willReturn('products');
296+
$metadata->getServiceId('controller')->willReturn('sylius.controller.product');
297+
298+
$routeFactory->createRouteCollection()->willReturn($routeCollection);
299+
300+
$configuration =
301+
<<<EOT
302+
alias: sylius.product
303+
EOT;
304+
305+
$showDefaults = [
306+
'_controller' => 'sylius.controller.product::showAction',
307+
'_sylius' => [
308+
'permission' => false,
309+
'serialization_groups' => ['Default', 'Detailed'],
310+
],
311+
];
312+
$routeFactory
313+
->createRoute('/products/{id}', $showDefaults, [], [], '', [], ['GET'], '')
314+
->willReturn($showRoute)
315+
;
316+
$routeCollection->add('sylius_product_show', $showRoute)->shouldBeCalled();
317+
318+
$indexDefaults = [
319+
'_controller' => 'sylius.controller.product::indexAction',
320+
'_sylius' => [
321+
'permission' => false,
322+
'serialization_groups' => ['Default'],
323+
],
324+
];
325+
$routeFactory
326+
->createRoute('/products', $indexDefaults, [], [], '', [], ['GET'], '')
327+
->willReturn($indexRoute)
328+
;
329+
$routeCollection->add('sylius_product_index', $indexRoute)->shouldBeCalled();
330+
331+
$createDefaults = [
332+
'_controller' => 'sylius.controller.product::createAction',
333+
'_sylius' => [
334+
'permission' => false,
335+
'serialization_groups' => ['Default', 'Detailed'],
336+
],
337+
];
338+
$routeFactory
339+
->createRoute('/products', $createDefaults, [], [], '', [], ['POST'], '')
340+
->willReturn($createRoute)
341+
;
342+
$routeCollection->add('sylius_product_create', $createRoute)->shouldBeCalled();
343+
344+
$updateDefaults = [
345+
'_controller' => 'sylius.controller.product::updateAction',
346+
'_sylius' => [
347+
'permission' => false,
348+
'serialization_groups' => ['Default', 'Detailed'],
349+
],
350+
];
351+
$routeFactory
352+
->createRoute('/products/{id}', $updateDefaults, [], [], '', [], ['PUT', 'PATCH'], '')
353+
->willReturn($updateRoute)
354+
;
355+
$routeCollection->add('sylius_product_update', $updateRoute)->shouldBeCalled();
356+
357+
$deleteDefaults = [
358+
'_controller' => 'sylius.controller.product::deleteAction',
359+
'_sylius' => [
360+
'permission' => false,
361+
'csrf_protection' => false,
362+
],
363+
];
364+
$routeFactory
365+
->createRoute('/products/{id}', $deleteDefaults, [], [], '', [], ['DELETE'], '')
366+
->willReturn($deleteRoute)
367+
;
368+
$routeCollection->add('sylius_product_delete', $deleteRoute)->shouldBeCalled();
369+
370+
$this->load($configuration, 'sylius.resource_api')->shouldReturn($routeCollection);
371+
}
372+
171373
function it_generates_urlized_paths_for_resources_with_multiple_words_in_name(
172374
RegistryInterface $resourceRegistry,
173375
MetadataInterface $metadata,

0 commit comments

Comments
 (0)