Symfony2 and Dependency Injection
When I started using Symfony2
(autumn 2010) I realised, that some things about Dependency Injection design pattern are not really obvious. That's why I decided to write an article to make it at least a little bit more clear.
IMPORTANT NOTE
Since Symfony2 was released I strongly recommend you to read the official documentation for Service Container
and take this article just as a quick reference.
Dependency Injection
If you don't know completelly anything about dependency injection I recommend your these resources:
- Official Dependency Injection component documentation
(whole Symfony2 is built on this component) - Martin Fowlers's Inversion of Control Containers and the Dependency Injection
pattern article - Wikipedia article on Dependency Injection

- Talk by Tobias Schlitt on Advanced OO Patterns In PHP
. In 2/3 of the video he talks about Dependency Injection.
The first article is more like an example why is Dependency Injection useful, while the second article is more focused on theory about Inversion of Control and Dependency Injection design patterns.
The Wikipedia article is also worthy reading, especially the
paragraph.
Purpose of this article is not to explain Dependency Injection but to show how it's implemented in Symfony2 and how to use it. The most common situation where you meet Dependency Injection is when you work with services.
Probably you'll deal with one of these situations:
- I want to use a service in my controller

- I'm writing my own service and I want to use it in my controller

- I'm writing my own service which uses other services

Fortunately, it's always pretty simple.
How to access a service in a controller
This is probably the most common situation. Everytime you're accessing for example entity manager you're actually accessing a service in your service container.
1 2 3 | $em = $this->container->get('doctrine.orm.entity_manager'); // or you can use just $em = $this->get('doctrine.orm.entity_manager'); |
How to write my own service
At this point it's getting a little more complicated. When you're writing your own bundle Symfony2 allows you to set it up (load configuration, register services, ...) during the initialization process by creating so-called Extension class (for more information read Official Symfony2 Documentation - Semantic Configuration
).
In other words, if we want to register our own service, we have to create an Extension class.
In my previous article on How to make a Twig extension for Symfony2
I made HelloExtension and in this tutorial I'm going to write almost the same class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // Bundle/HelloBundle/DependencyInjection/HelloExtension.php namespace Bundle\HelloBundle\DependencyInjection; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; class HelloExtension extends Extension { public function load(array $configs, ContainerBuilder $container) { // here we'll define our service } /** * Was necessary in previous Symfony2 PR releases. * Symfony2 calls `load` method automatically now. * * public function getAlias() { * return 'my_bundle'; * } */ } |
This class defines my_bundle namespace which we can use in configuration files.
# app/config/config.yml my_bundle: ~
This triggers the `load` method in the `HelloExtension` class and passes all configuration defined for this namespace.
Now we'll modify load method to define our service.
Just to be clear, service can be any class, it doesn't has to extends any other classes.
Basically we have two options how to define services:
- hardcode definitions in PHP
- write definitions in XML or YAML (I strongly recommend)
The first way, hardcoding definitions in PHP, is good only for very limited amount of services. I used it in one of my previous posts How to make a Twig extension for Symfony2
because there was just one very simple service.
The second way means writing service definitions in a separate XML (or YAML) file and loading it in our load method. I recommend writing an XML because almost all Symfony2 built-in bundles uses XML files and if you had hard time figuring out how to define something you can easily look how it's done by Symfony2 programmers.
So, lets modify load method to load extensions from an XML file.
1 2 3 4 5 6 7 8 9 10 11 | // Bundle/HelloBundle/DependencyInjection/HelloExtension.php class HelloExtension extends Extension { public function load(array $config, ContainerBuilder $container) { // only load default services and parameters once $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.xml'); } // ... } |
write the XML definition:
1 2 3 4 5 6 7 8 9 10 11 | # Bundle/HelloBundle/Resources/config/services.xml <?xml version="1.0" ?> <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="my_service" class="Bundle\HelloBundle\Services\MyService"></service> </services> </container> |
A quick explanation here:
tag - our example is very simple, but service definition might also contain arguments or tags (see examples below)id- this parameter represents identifier for our service (in this casemy_service. Later when we'll access the service, we'll call$this->get('my_service'). This will return aMyServiceinstance.class- full service's class name (don't forget namespaces)
1 2 3 4 5 6 7 8 | // Bundle/HelloBundle/Services/MyService.php namespace Bundle\HelloBundle\Services; class MyService { public function sum($n1, $n2) { return $n1 + $n2; } } |
Basically that's all. Now we can use MyService in any controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // Bundle/HelloBundle/Controller/HelloController.php namespace Bundle\HelloBundle\Controller; class HelloController extends Controller { public function indexAction() { $number = $this->get('my_service')->sum(12, 37); // this returns 49 /* ... */ } } |
Accessing our service is like accessing any other service.
1
| $service = $this->get('my_service');
|
How to access other services inside my own service
Sometimes when you're writing your own service you realize, that you want to access database, request object, routes or whatever inside your service. The problem is, how?
In the XML service definition you can specify what arguments the Dependency Injection component will pass to your service constructor method. For example, lets say we want to write a service that accesses database and the routing loader object. To keep things simple we'll extend the MyService class used in the previous example and add a few lines to our XML service definition:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # Bundle/HelloBundle/Resources/config/services.xml <?xml version="1.0" ?> <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="my_service" class="Bundle\HelloBundle\Services\MyService"> <argument type="service" id="doctrine.orm.entity_manager" /> <argument type="service" id="routing.loader" /> </service> </services> </container> |
and check what arguments (or what objects) are passed to the MyService::__constructor().
1 2 3 4 5 6 | class MyService { public function __constructor() { print_r(func_get_args());exit; } // ... } |
as we could expect there are two arguments:
Array
(
[0] => Doctrine\ORM\EntityManager Object
[1] => Symfony\Bundle\FrameworkBundle\Routing\LazyLoader Object
)
That's great, so we can modify MyService class for the last time:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | use Doctrine\ORM\EntityManager; use Symfony\Bundle\FrameworkBundle\Routing\LazyLoader; class MyService { protected $em; protected $routeLoader; public function __construct(EntityManager $em, LazyLoader $routeLoader) { $this->em = $em; $this->routeLoader = $routeLoader; } public function doSomethingAmazing() { // $this->em - my entity manager // $this->routeLoader - my routing loader } // ... } |
In some situations you might want to pass to your service entire Service Container. It is possible but it's a very bad practise and you should always try to avoid it. This is very useful when your service needs to access a lot of other services.
1 2 3 4 | # Bundle/HelloBundle/Resources/config/services.xml <service id="my_service" class="Bundle\HelloBundle\Services\MyService"> <argument type="service" id="service_container" /> </service> |
A few words at the end
Most of these thing I figured out by examining Symfony2's source code because the official documentation is not complete yet. I don't want to claim that this is the best possible solution, If you found better leave me a comment.
Also this code snippets were written and tested on Symfony2 PR6 but should be the same for later Symfony2 releases Symfony2 PR7 PR9 PR12.