0

编辑

在深入研究了 symfony 代码,尤其是 ControllerResolver 之后,似乎我试图做的事情实际上是不可能的,除非我自己继承/实现 ControllerResolverInterface。

这是以下代码,它实例化从路由传递的控制器:

protected function createController($controller)
{
    if (false === strpos($controller, '::')) {
        throw new \InvalidArgumentException(sprintf('Unable to find controller "%s".', $controller));
    }

    list($class, $method) = explode('::', $controller, 2);

    if (!class_exists($class)) {
        throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
    }

    return array(new $class(), $method);
}

正如你在最后一行看到的,它总是在没有传递参数的情况下被实例化,所以我必须重写这个方法才能以这种方式注入一些东西。感觉很hacky。


原始问题

我试图弄清楚如何使用 Symfony 组件(例如,不是完整的堆栈框架)将服务注入到在动态路由中定义的自定义控制器中。

请注意,我没有使用完整的堆栈框架,也没有使用他们的 DemoBundle src 代码。我有一个composer.json需要组件的文件,所以我有一个index.php与此处详述的文件大致相同的自定义文件:

http://fabien.potencier.org/article/55/create-your-own-framework-on-top-of-the-symfony2-components-part-12

我有以下内容:

$routes = new RouteCollection();
$routes->add(
    'some route name',
    new Route(
      'a route path', 
      array(
        '_controller' => 'App\MyBundle\Controller\MyController::handle'
      )
    )
); 

然后我有以下内容App/MyBundle/DependencyInjection/MyExtension.php

public function load(array $configs, ContainerBuilder $container) {
    $loader = new XmlFileLoader(
      $container, 
      new FileLocator(__DIR__.'/../Resource/config')
    );
    $loader->load('services.xml');
}

App/MyBundle/Resources/config/services.xml

<container xmlns="http://symfony.com/schema/dic/services"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://symfony.com/schema/dic/services 
                      http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
  <service id="templating" class="Symfony\Component\Templating\EngineInterface" />
  <service id="navigation" class="App\MyBundle\Controller\MyController">
    <argument type="service" id="templating" />
  </service> 
</services>
</container>

我基本上是在尝试将模板服务注入MyController构造函数,我的理解是MyExtension文件应该自动加载。我假设我没有使用完整的堆栈框架,这就是原因,但我怎样才能让它工作呢?

4

2 回答 2

2

重写 ControllerResolver 没有错。完整的堆栈框架也可以做到这一点。否则控制器不能是 ContainerAware。

我还使用了没有完整堆栈框架的 Symfony 组件,并且部分复制了完整堆栈框架,我最终这样做是为了将容器注入我的控制器中

class ControllerResolver extends SymfonyControllerResolver
{
    protected $container;

    public function __construct(ContainerInterface $container, LoggerInterface $logger = null)
    {
        $this->container = $container;

        parent::__construct($logger);
    }

    protected function createController($controller)
    {
        if (false === strpos($controller, '::')) {
           throw new \InvalidArgumentException(sprintf('Unable to find controller "%s".', $controller));
        }

        list($class, $method) = explode('::', $controller, 2);

        $class = "Namespace\\Controllers\\" . $class;

        if (!class_exists($class)) {
            throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
        }

        $controller = new $class();
        if ($controller instanceof ContainerAwareInterface) {
            $controller->setContainer($this->container);
        }

        return array($controller, $method);
    }
}

如果您想增加将控制器定义为服务的可能性,您可以替换

if (!class_exists($class)) {
        throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
    }

    $controller = new $class();
    if ($controller instanceof ContainerAwareInterface) {
        $controller->setContainer($this->container);
    }

有类似的东西

if (!class_exists($class)) {
    if (!$this->container->has($class)) {
        throw new \Exception( ... );
    }
    $controller = $this->container->get($class);

    return array($controller, $method);
}

$controller = new $class();
if ($controller instanceof ContainerAwareInterface) {
    $controller->setContainer($this->container);
}

return array($controller, $method);
于 2014-05-30T14:00:46.317 回答
0

嗯,一开始。您不必将服务注入您的控制器。一个普通的控制器将扩展Symfony\Bundle\FrameworkBundle\Controller\Controller,从而注入孔容器。这意味着您可以templating像这样访问服务:

public function myAction()
{
    $templating = $this->get('templating');
}

但是 Symfony2 也为您提供了将控制器创建为服务的可能性。这意味着您从默认值中删除扩展,Controller而不是只注入您需要的服务(通常是requestresponse)。更多信息可以在Richard Miller的这篇精彩文章中找到。
你也可以阅读Lukas Kahwe Smith 的这篇文章,他在其中谈到了为什么他认为服务是“最佳实践”(请注意,Symfony 项目的前身 Fabien 不同意这一点)。

于 2013-03-27T13:21:15.243 回答