Skip to content

Commit 37b86cf

Browse files
committed
perf(laravel): memoize the Symfony RouteCollection in Router
Router::getRouteCollection() rebuilt the entire Symfony RouteCollection on every call via Illuminate\Routing\AbstractRouteCollection::toSymfonyRouteCollection(), converting the whole Laravel route table each time. generate() is called once per IRI during normalization, so a single response with several relations triggered the full conversion dozens of times (~65 for ~8 relations in profiling). Routes are static within a request and Router is a per-request singleton, so the built collection is cached on the instance. Also type SkolemIriConverter's constructor against Symfony\Component\Routing\RouterInterface instead of the final concrete Router, so consumers are no longer pinned to the concrete class. Closes #8337
1 parent 7bc11b2 commit 37b86cf

3 files changed

Lines changed: 52 additions & 2 deletions

File tree

src/Laravel/Routing/Router.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ final class Router implements RouterInterface, UrlGeneratorInterface
3737
];
3838

3939
private RequestContext $context;
40+
private ?RouteCollection $routeCollection = null;
4041

4142
public function __construct(private readonly BaseRouter $router, private readonly int $urlGenerationStrategy = UrlGeneratorInterface::ABS_PATH)
4243
{
@@ -63,10 +64,14 @@ public function getContext(): RequestContext
6364
*/
6465
public function getRouteCollection(): RouteCollection
6566
{
67+
if (null !== $this->routeCollection) {
68+
return $this->routeCollection;
69+
}
70+
6671
/** @var \Illuminate\Routing\RouteCollection $routes */
6772
$routes = $this->router->getRoutes();
6873

69-
return $routes->toSymfonyRouteCollection();
74+
return $this->routeCollection = $routes->toSymfonyRouteCollection();
7075
}
7176

7277
/**

src/Laravel/Routing/SkolemIriConverter.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use ApiPlatform\Metadata\IriConverterInterface;
1818
use ApiPlatform\Metadata\Operation;
1919
use ApiPlatform\Metadata\UrlGeneratorInterface;
20+
use Symfony\Component\Routing\RouterInterface;
2021

2122
/**
2223
* {@inheritdoc}
@@ -37,7 +38,7 @@ final class SkolemIriConverter implements IriConverterInterface
3738
*/
3839
private array $classHashMap = [];
3940

40-
public function __construct(private readonly Router $router)
41+
public function __construct(private readonly RouterInterface $router)
4142
{
4243
$this->objectHashMap = new \SplObjectStorage();
4344
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Laravel\Tests\Unit\Routing;
15+
16+
use ApiPlatform\Laravel\Routing\Router;
17+
use Illuminate\Routing\Router as BaseRouter;
18+
use Illuminate\Routing\RouteCollection as IlluminateRouteCollection;
19+
use PHPUnit\Framework\TestCase;
20+
use Symfony\Component\Routing\RouteCollection;
21+
22+
class RouterTest extends TestCase
23+
{
24+
public function testGetRouteCollectionIsMemoizedAcrossCalls(): void
25+
{
26+
$symfonyRoutes = new RouteCollection();
27+
28+
$illuminateRoutes = $this->createMock(IlluminateRouteCollection::class);
29+
$illuminateRoutes->expects($this->once())
30+
->method('toSymfonyRouteCollection')
31+
->willReturn($symfonyRoutes);
32+
33+
$baseRouter = $this->createStub(BaseRouter::class);
34+
$baseRouter->method('getRoutes')->willReturn($illuminateRoutes);
35+
36+
$router = new Router($baseRouter);
37+
38+
$first = $router->getRouteCollection();
39+
$second = $router->getRouteCollection();
40+
41+
$this->assertSame($symfonyRoutes, $first);
42+
$this->assertSame($first, $second);
43+
}
44+
}

0 commit comments

Comments
 (0)