Symfony:控制器工厂

Łuk*_*oda 11 controller routes factory-pattern symfony

我正在制作一个自定义用户包,允许定义多个用户类型,使用自己的存储库,管理器,提供商等.因此,我决定创建一个控制器工厂,而不是创建有限的控制器组,这将产生控制器基于定义的用户类型和配置.但这提出了一个重要的问题 - 这些工厂应该在哪里运营?

现在,请注意,在工厂中创建控制器是不够的,我们还必须在某处设置所有路径.

问题是 - 什么是最好的架构呢?

在选择我将放置代码的图层时,我正在考虑,其中包括:

  1. 在Extension的load方法中加载工厂定义,并在那里创建所有控制器.问题:路由器在那里不可用,因为它发生在容器构建之前,所以我无法在同一个地方创建路由.

  2. Sooo ...也许在编译器传递?但编译器传递无法访问配置...我的意思是......实际上它已经,如果我只是加载配置并手动处理它,但我仍然不确定这是否是一个好地方,但我我现在正倾向于这个解决方案.

在创建路线时:

  1. 我应该在控制器工厂中放置路由创建逻辑吗?但我正在创建控制器作为服务,而工厂无法访问创建的控制器的serviceId,并且创建路由需要serviceId,所以不应该.

  2. 在控制器本身?我的意思是,这就是注释路由的工作方式,因此它可能是可行的.控制器必须ControllerInterface使用该方法实现类似我自己的东西getRoutes,并且外部服务/编译器传递需要首先创建控制器作为服务,然后从所述控制器获取路由,修改它们,以便它们引用该控制器的serviceId并将它们添加到路由器......无论这看起来多么混乱.

  3. 还有其他选择吗?

关于这种特殊模式的信息非常缺乏 - 控制器工厂:).

Kév*_*las 5

API平台的第一个版本使用了类似的技术.

第一步是注册路线.路径使用_controller路径属性下定义的控制器映射URL模式.路由组件和HttpKernel组件是如何链接在一起的(这两个组件之间没有强耦合).可以通过创建以下内容来注册路由RouteLoader:http://symfony.com/doc/current/routing/custom_route_loader.html

这就是API平台,Sonata和Easy Admin的工作方式.

在运行时,_controller将执行属性下指定的可调用对象.它将在参数中接收HTTP请求,并应返回HTTP响应.如果需要,它可以访问其他服务(甚至是容器).

控制器可以是任何可调用的(方法,函数,可调用类......),但由于以下语法,它也可以是服务my_controller_service:myAction(请参阅http://symfony.com/doc/current/controller/service.html) .

DependencyInjection组件允许使用工厂构建服务:http://symfony.com/doc/current/service_container/factories.html.Factory方法可以接收其他服务或参数(config).

总结一下:

1 /使用工厂为您的控制器注册服务定义,如下所示:

# app/config/services.yml
services:
    # ...

    app.controller_factory:
        class: AppBundle\Controller\ControllerFactory
        arguments: ['@some_service', '%some_parameter%]

    app.my_controller:
        class:     AppBundle\Controller\ControllerInterface
        factory:   'app.controller_factory:createController'
        arguments: ['@some_service', '%some_parameter%]
Run Code Online (Sandbox Code Playgroud)

当然,如果需要,可以在AppBundle\DependencyInjection\AppBundleExtension类中以编程方式创建控制器定义.您还可以使用abstract服务定义来避免代码重复(http://symfony.com/doc/current/service_container/parent_services.html).

2 /创建RouteLoader注册Route实例的服务.你可以看看这个例子:https://github.com/api-platform/core/blob/1.x/Routing/ApiLoader.php

然后,将此路由加载器注册为服务:

# app/config/services.yml
services:
    app.routing_loader:
        class: AppBundle\Routing\MyLoader
        arguments: ['@some_service', '%some_parameter%]
        tags:
            - { name: routing.loader }
Run Code Online (Sandbox Code Playgroud)

3 /告诉路由器执行此操作RouteLoader:

# app/config/routing.yml
app:
    resource: . # Omitted
    type: mytype # Should match the one defined in your loader's supports() method
Run Code Online (Sandbox Code Playgroud)

全部完成!

(我是Symfony核心团队成员,但也是API平台创建者,所以这是一个自以为是的答案.)