Create a base controller class that implements ContainerAwareInterface
I followed Fabien Potiencier's guide on how to create my own Framework on top of Symfony components . Now I need a way. And I want to inject a container of dependencies to all of my controllers without defining each individual controller as a service.
In the original Symfony2 framework, all controllers extend a class Controller
located in Symfony\Bundle\FrameworkBundle\Controller\Controller.php
:
namespace Symfony\Bundle\FrameworkBundle\Controller;
class Controller extends ContainerAware
{
// ...
}
The class Controller
extends the class ControllerAware
, so you can do something like this in your controller:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class MyController extends Controller
{
public function someAction()
{
$this->container->get('dependencie_xyz);
}
}
So my question is: How can I do the same in my Framework?
source to share
It took me a while, but I finally figured out how the Symfony2 Framework works. The SymfonyFrameworkBundle uses a custom ControllerResolver that calls the setContainer method on the resolved controller. The controller must be an instance of ContainerAwareInterface.
Simplified version:
class ContainerAwareControllerResolver extends ControllerResolver
{
private $container;
public __construct(ContainerInterface $container)
{
$this->container = $container;
parent::__construct();
}
public function getController(Request $request)
{
$controller = parent::getController($request);
if($controller instanceof ContainerAware ){
$controller->setContainer($this->container);
}
}
}
Source:
source to share
It's too easy. The following code will help you
namespace Symfony\Bundle\FrameworkBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
use Symfony\Component\DependencyInjection\ContainerAware as ContainerAware;
class TestService extends ContainerAware
{
public function __construct(Container $container) {
// in your example from official doc 'dependencie_xyz' is a name of service
$this->setContainer($container); // call parent setContainer() method, for identifying container variable, from now you can access to ServiceContainer using $this->container variable
$test_param = $this->container->getParameter('test_param'); // get test_param from config.yml
}
}
in service.yml
write smthing like this
services:
test_service:
class: Symfony\Bundle\FrameworkBundle\TestService
arguments: ['@service_container']
and the service container as an argument
source to share
If you don't implement any interface on the controller, you can add this way and it will work. This is a slight modification of the c4pone implementation.
/**
* Description of ContainerAwareControllerResolver
*
* @author sbc
*/
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
class ContainerAwareControllerResolver extends ControllerResolver {
private $container;
public function __construct(LoggerInterface $logger = null, ContainerInterface $container = null) {
parent::__construct($logger);
$this->container = $container;
}
protected function instantiateController($class) {
$new_class = new $class();
$new_class->setContainer($this->container);
return $new_class;
}
source to share
The controller class extends the ControllerAware class, so you can do something like this in your controller:
Well, that's not true. If we look at the class signatureContainerAware
, we can see that this added a method setContainer
so that we can set up the container, Symfony2 created a method Controller::get
to make life easier.
We can see how they do it in the source code :
/**
* Gets a service by id.
*
* @param string $id The service id
*
* @return object The service
*/
public function get($id)
{
return $this->container->get($id);
}
You can put this in your own class Controller
and let all of your controllers extend that controller class.
source to share