跳到内容

如何创建自定义路由加载器

编辑此页

基本应用程序可以在单个配置文件中定义其所有路由 - 通常是 config/routes.yaml(请参阅 路由)。但是,在大多数应用程序中,从不同资源导入路由定义是很常见的:控制器文件中的 PHP 属性、YAML、XML 或存储在某些目录中的 PHP 文件等。

内置路由加载器

Symfony 为最常见的需求提供了多个路由加载器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# config/routes.yaml
app_file:
    # loads routes from the given routing file stored in some bundle
    resource: '@AcmeBundle/Resources/config/routing.yaml'

app_psr4:
    # loads routes from the PHP attributes of the controllers found in the given PSR-4 namespace root
    resource:
        path: '../src/Controller/'
        namespace: App\Controller
    type: attribute

app_attributes:
    # loads routes from the PHP attributes of the controllers found in that directory
    resource: '../src/Controller/'
    type:     attribute

app_class_attributes:
    # loads routes from the PHP attributes of the given class
    resource: App\Controller\MyController
    type:     attribute

app_directory:
    # loads routes from the YAML, XML or PHP files found in that directory
    resource: '../legacy/routing/'
    type:     directory

app_bundle:
    # loads routes from the YAML, XML or PHP files found in some bundle directory
    resource: '@AcmeOtherBundle/Resources/config/routing/'
    type:     directory

注意

导入资源时,键(例如 app_file)是集合的名称。只需确保它在每个文件中都是唯一的,这样其他行就不会覆盖它。

如果您的应用程序需求不同,您可以创建自己的自定义路由加载器,如下一节所述。

什么是自定义路由加载器

自定义路由加载器使您能够根据某些约定、模式或集成生成路由。此用例的一个示例是 OpenAPI-Symfony-Routing 库,其中路由是根据 OpenAPI/Swagger 属性生成的。另一个示例是 SonataAdminBundle,它根据 CRUD 约定创建路由。

加载路由

Symfony 应用程序中的路由由 DelegatingLoader 加载。此加载器使用多个其他加载器(委托)来加载不同类型的资源,例如 YAML 文件或控制器文件中的 #[Route] 属性。专门的加载器实现了 LoaderInterface,因此有两个重要的方法:supports()load()

routes.yaml 中获取以下行

1
2
3
4
# config/routes.yaml
controllers:
    resource: ../src/Controller/
    type: attribute

当主加载器解析此内容时,它会尝试所有已注册的委托加载器,并使用给定的资源 (../src/Controller/) 和类型 (attribute) 作为参数调用它们的 supports() 方法。当其中一个加载器返回 true 时,将调用其 load() 方法,该方法应返回一个包含 Route 对象的 RouteCollection

注意

以这种方式加载的路由将由路由器缓存,就像它们以默认格式之一(例如 XML、YAML、PHP 文件)定义时一样。

使用自定义服务加载路由

使用常规 Symfony 服务是以自定义方式加载路由的最简单方法。这比创建完整的自定义路由加载器容易得多,因此您应该始终首先考虑此选项。

为此,请定义 type: service 作为加载的路由资源的类型,并配置要调用的服务和方法

1
2
3
4
# config/routes.yaml
admin_routes:
    resource: 'admin_route_loader::loadRoutes'
    type: service

在此示例中,路由通过调用 ID 为 admin_route_loader 的服务的 loadRoutes() 方法加载。您的服务不必扩展或实现任何特殊类,但调用的方法必须返回一个 RouteCollection 对象。

如果您正在使用 autoconfigure,您的类应实现 RouteLoaderInterface 接口以自动标记。如果您不使用 autoconfigure,请使用 routing.route_loader 手动标记它。

注意

使用服务路由加载器定义的路由将由框架自动缓存。因此,每当您的服务应加载新路由时,请不要忘记清除缓存。

提示

如果您的服务是可调用的,则无需指定要使用的方法。

创建自定义加载器

要从某些自定义源(即,从属性、YAML 或 XML 文件以外的其他内容)加载路由,您需要创建自定义路由加载器。此加载器必须实现 LoaderInterface

在大多数情况下,从 Loader 扩展比自己实现 LoaderInterface 更容易。

下面的示例加载器支持加载类型为 extra 的路由资源。类型名称不应与其他可能支持相同资源类型的加载器冲突。 придумайте 任何特定于您所做的事情的名称。资源名称本身实际上没有在示例中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// src/Routing/ExtraLoader.php
namespace App\Routing;

use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

class ExtraLoader extends Loader
{
    private bool $isLoaded = false;

    public function load($resource, ?string $type = null): RouteCollection
    {
        if (true === $this->isLoaded) {
            throw new \RuntimeException('Do not add the "extra" loader twice');
        }

        $routes = new RouteCollection();

        // prepare a new route
        $path = '/extra/{parameter}';
        $defaults = [
            '_controller' => 'App\Controller\ExtraController::extra',
        ];
        $requirements = [
            'parameter' => '\d+',
        ];
        $route = new Route($path, $defaults, $requirements);

        // add the new route to the route collection
        $routeName = 'extraRoute';
        $routes->add($routeName, $route);

        $this->isLoaded = true;

        return $routes;
    }

    public function supports($resource, ?string $type = null): bool
    {
        return 'extra' === $type;
    }
}

确保您指定的控制器确实存在。在这种情况下,您必须在 ExtraController 中创建一个 extra() 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/Controller/ExtraController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class ExtraController extends AbstractController
{
    public function extra(mixed $parameter): Response
    {
        return new Response($parameter);
    }
}

现在为 ExtraLoader 定义一个服务

1
2
3
4
5
6
# config/services.yaml
services:
    # ...

    App\Routing\ExtraLoader:
        tags: [routing.loader]

请注意标签 routing.loader。所有带有此标签的服务都将被标记为潜在的路由加载器,并作为专门的路由加载器添加到 routing.loader服务中,该服务是 DelegatingLoader 的实例。

使用自定义加载器

如果您什么都不做,您的自定义路由加载器将不会被调用。剩下要做的就是在路由配置中添加几行

1
2
3
4
# config/routes.yaml
app_extra:
    resource: .
    type: extra

这里的重要部分是 type 键。它的值应该是 extra,因为这是 ExtraLoader 支持的类型,这将确保调用其 load() 方法。resource 键对于 ExtraLoader 来说是无关紧要的,因此它被设置为 . (一个点)。

注意

使用自定义路由加载器定义的路由将由框架自动缓存。因此,每当您在加载器类本身中更改某些内容时,请不要忘记清除缓存。

更高级的加载器

如果您的自定义路由加载器从 Loader 扩展,如上所示,您还可以利用提供的解析器,LoaderResolver 的实例,来加载辅助路由资源。

您仍然需要实现 supports()load()。每当您想要加载另一个资源(例如 YAML 路由配置文件)时,您可以调用 import() 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// src/Routing/AdvancedLoader.php
namespace App\Routing;

use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\RouteCollection;

class AdvancedLoader extends Loader
{
    public function load($resource, ?string $type = null): RouteCollection
    {
        $routes = new RouteCollection();

        $resource = '@ThirdPartyBundle/Resources/config/routes.yaml';
        $type = 'yaml';

        $importedRoutes = $this->import($resource, $type);

        $routes->addCollection($importedRoutes);

        return $routes;
    }

    public function supports($resource, ?string $type = null): bool
    {
        return 'advanced_extra' === $type;
    }
}

注意

导入的路由配置的资源名称和类型可以是路由配置加载器通常支持的任何内容(YAML、XML、PHP、attribute 等)。

注意

对于更高级的用途,请查看 Symfony CMF 项目提供的 ChainRouter。此路由器允许应用程序使用两个或多个路由器组合,例如在编写自定义路由器时继续使用默认的 Symfony 路由系统。

这项工作,包括代码示例,已获得 Creative Commons BY-SA 3.0 许可。
目录
    版本