Symfony 2 Controller dependencies, extending ContainerAware

馋奶兔 提交于 2019-12-25 00:18:39

问题


Edit

after digging into the symfony code, particularly the ControllerResolver, it seems what im trying to do actually isnt possible unless i subclass/implement ControllerResolverInterface myself.

this is the following code which instantiates the controller passed from the route:

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);
}

as you can see on the last line, this is always instantiated with no arguments passed, so i will have to override this method to inject something in that way. feels very hacky.


Original Question

I'm trying to figure out how I can inject services into a custom controller defined in dynamic routes using Symfony components (e.g. not the full stack framework).

Please note, I am not using the full stack framework and am not using their DemoBundle src code. I have a composer.json file that requires components, so I have a custom index.php file which is more or less the same as that detailed here:

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

I have the following:

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

Then I have the following within 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>

I'm basically trying to get the templating service injected into the MyController constructor, and my understanding is the MyExtension file should be loaded automatically. I assume as I'm not using the full stack framework, this is the reason why, but how can I get this working?


回答1:


Nothing wrong with overriding ControllerResolver. The full stack framework does that too. Otherwise the Controllers couldn't be ContainerAware.

I also use Symfony Components without the full stack framework and, partly copying the full stack framework, I ended up with this in order to inject the container in my controllers

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 you wanted to add possibility to define controllers as services you can replace

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);
    }

With something like

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);



回答2:


Well, at first. You don't have to inject services into your controller. A normal controller will extend Symfony\Bundle\FrameworkBundle\Controller\Controller which gets the hole container injected. This means you can access the templating service like this:

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

But Symfony2 gives you also the possibility of creating controller as services. That means you remove the extend from the default Controller and instead of that only inject the services you need (usually request and response). More information can be found in this great post by Richard Miller.
You can also read this post by Lukas Kahwe Smith, in which he talks about why he thinks services are a 'best practise' (please note that Fabien, former of the Symfony project, disagrees with this).



来源:https://stackoverflow.com/questions/15658830/symfony-2-controller-dependencies-extending-containeraware

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!