Skip to content

Commit

Permalink
feature #50084 [Routing] Add FQCN and FQCN::method aliases when appli…
Browse files Browse the repository at this point in the history
…cable (fancyweb)

This PR was merged into the 6.4 branch.

Discussion
----------

[Routing] Add FQCN and FQCN::method aliases when applicable

| Q             | A
| ------------- | ---
| Branch?       | 6.3
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | #49981
| License       | MIT
| Doc PR        | -

See #49981, I think it's a great idea 😃

* We add an FQCN alias only if the target class has an `__invoke` method that adds a route AND if the target class added 1 route exactly.
* We add a FQCN::method alias for every method that defines only one route.

Commits
-------

9fa5bae [Routing] Add FQCN and FQCN::method aliases when applicable
  • Loading branch information
fabpot committed Jun 20, 2023
2 parents d48b929 + 9fa5bae commit ffc47b9
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/Symfony/Component/Routing/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========

6.3
---

* Add FQCN and FQCN::method aliases for routes loaded from attributes/annotations when applicable

6.2
---

Expand Down
16 changes: 16 additions & 0 deletions src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php
Expand Up @@ -125,20 +125,36 @@ public function load(mixed $class, string $type = null): RouteCollection
return $collection;
}

$fqcnAlias = false;
foreach ($class->getMethods() as $method) {
$this->defaultRouteIndex = 0;
$routeNamesBefore = array_keys($collection->all());
foreach ($this->getAnnotations($method) as $annot) {
$this->addRoute($collection, $annot, $globals, $class, $method);
if ('__invoke' === $method->name) {
$fqcnAlias = true;
}
}

if (1 === $collection->count() - \count($routeNamesBefore)) {
$newRouteName = current(array_diff(array_keys($collection->all()), $routeNamesBefore));
$collection->addAlias(sprintf('%s::%s', $class->name, $method->name), $newRouteName);
}
}

if (0 === $collection->count() && $class->hasMethod('__invoke')) {
$globals = $this->resetGlobals();
foreach ($this->getAnnotations($class) as $annot) {
$this->addRoute($collection, $annot, $globals, $class, $class->getMethod('__invoke'));
$fqcnAlias = true;
}
}

if ($fqcnAlias && 1 === $collection->count()) {
$collection->addAlias($class->name, $invokeRouteName = key($collection->all()));
$collection->addAlias(sprintf('%s::__invoke', $class->name), $invokeRouteName);
}

return $collection;
}

Expand Down
@@ -0,0 +1,15 @@
<?php

namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;

use Symfony\Component\Routing\Annotation\Route;

class InvokableMethodController
{
/**
* @Route("/here", name="lol", methods={"GET", "POST"}, schemes={"https"})
*/
public function __invoke()
{
}
}
@@ -0,0 +1,13 @@
<?php

namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;

use Symfony\Component\Routing\Annotation\Route;

class InvokableMethodController
{
#[Route(path: '/here', name: 'lol', methods: ["GET", "POST"], schemes: ['https'])]
public function __invoke()
{
}
}
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Routing\Tests\Loader;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Routing\Alias;
use Symfony\Component\Routing\Loader\AnnotationClassLoader;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\AbstractClassController;

Expand Down Expand Up @@ -55,6 +56,7 @@ public function testSimplePathRoute()
$routes = $this->loader->load($this->getNamespace().'\ActionPathController');
$this->assertCount(1, $routes);
$this->assertEquals('/path', $routes->get('action')->getPath());
$this->assertEquals(new Alias('action'), $routes->getAlias($this->getNamespace().'\ActionPathController::action'));
}

public function testRequirementsWithoutPlaceholderName()
Expand All @@ -72,6 +74,19 @@ public function testInvokableControllerLoader()
$this->assertEquals('/here', $routes->get('lol')->getPath());
$this->assertEquals(['GET', 'POST'], $routes->get('lol')->getMethods());
$this->assertEquals(['https'], $routes->get('lol')->getSchemes());
$this->assertEquals(new Alias('lol'), $routes->getAlias($this->getNamespace().'\InvokableController'));
$this->assertEquals(new Alias('lol'), $routes->getAlias($this->getNamespace().'\InvokableController::__invoke'));
}

public function testInvokableMethodControllerLoader()
{
$routes = $this->loader->load($this->getNamespace().'\InvokableMethodController');
$this->assertCount(1, $routes);
$this->assertEquals('/here', $routes->get('lol')->getPath());
$this->assertEquals(['GET', 'POST'], $routes->get('lol')->getMethods());
$this->assertEquals(['https'], $routes->get('lol')->getSchemes());
$this->assertEquals(new Alias('lol'), $routes->getAlias($this->getNamespace().'\InvokableMethodController'));
$this->assertEquals(new Alias('lol'), $routes->getAlias($this->getNamespace().'\InvokableMethodController::__invoke'));
}

public function testInvokableLocalizedControllerLoading()
Expand Down Expand Up @@ -119,6 +134,8 @@ public function testMethodActionControllers()
$this->assertSame(['put', 'post'], array_keys($routes->all()));
$this->assertEquals('/the/path', $routes->get('put')->getPath());
$this->assertEquals('/the/path', $routes->get('post')->getPath());
$this->assertEquals(new Alias('post'), $routes->getAlias($this->getNamespace().'\MethodActionControllers::post'));
$this->assertEquals(new Alias('put'), $routes->getAlias($this->getNamespace().'\MethodActionControllers::put'));
}

public function testInvokableClassRouteLoadWithMethodAnnotation()
Expand Down

0 comments on commit ffc47b9

Please sign in to comment.