Symfony 2 Controller依赖项,扩展了ContainerAware

时间:2013-03-27 12:30:27

标签: dependency-injection symfony-components

修改

在深入研究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文件。我假设因为我没有使用完整的堆栈框架,这就是原因,但我怎么能让它工作呢?

2 个答案:

答案 0 :(得分: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);

答案 1 :(得分:0)

嗯,起初。您不必将服务注入控制器。一个普通的控制器将扩展Symfony\Bundle\FrameworkBundle\Controller\Controller,它会注入一个空洞容器。这意味着您可以像这样访问templating服务:

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

但Symfony2也为您提供了将控制器创建为服务的可能性。这意味着您从默认的Controller中删除了扩展,而不仅仅是注入您需要的服务(通常是requestresponse)。更多信息可以在Richard Miller的this great post中找到 您还可以阅读Lukas Kahwe Smith撰写的this post,其中他谈到了为什么他认为服务是最佳实践&#39; (请注意,Symfony项目的前Fabien不同意这一点。)