Symfony2 service container
Introduction
A big PHP aplication should be OOP written and should have a lot of objects. You can split them by functionality depending on what they do. Some of them are used for interaction with database, some of them are used to save information in files.This chapter is about a special PHP object in Symfony2 that helps you instantiate, organize and retrieve the many objects of your application. This object, called a service container, will allow you to standardize and centralize the way objects are constructed in your application. It is highly recomended to create containers because it promotes reusable, decoupled and fast code.
Definition
A service is a PHP class that is used to perform some global operations. Each service is used throughout your application whenever you need the specific functionality it provides.
Often called dependency injection container, it is simply a PHP object that manages the instantiation of services (i.e. objects).
Dependency injection explained
Basically, instead of having your objects creating a dependency, you pass the needed dependencies in to the constructor or via property setters of another class. The following example is showing how a good part of developers think about good coding.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Author { public $firstname; public $lastname; public function __construct($firstname, $lastname) { $this->firstname = $firstname; $this->lastname = $lastname; } } class Post { private $author; private $body; public function __construct($firstname, $lastname, $body) { $this->author = new Author($firstname, $lastname); $this->body = $body; } |
You have two classes, one for Author and one for Post. This classes need somehow to be used together as you see. In the post class we need the Author object because we have to set later some information about that in the database perhaps. This is not that good code because the Author‘s information passed to the Post constructor has nothing to do inside Post‘s scope. This code will result in hard testing.
By using dependency injection pattern, you will avoid these problems by inserting the dependencies (objects of other class) directly into the controller of the other class. This will result in more maintainable code. Let’s see how we obtain this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Author { public $firstname; public $lastname; public function __construct($firstname, $lastname) { $this->firstname = $firstname; $this->lastname = $lastname; } } class Post { private $author; private $body; public function __construct(Author $author, $body) { $this->author = $author; $this->body = $body; } |
This will result in better code and better testing! Now, let’s get back to Symfony’s service container.
Creating services in the container
Let’s say we have to manage views for a certain post in our application. We choose to create a service that deals with that. We will start by adding in the “app/config/config.yml” the followings:
1 2 3 4 5 6 |
parameters: questions_viewer.class: Apn\ApnBundle\DependencyInjection\Views services: questions_viewer: class: "%questions_viewer.class%" arguments: [question] |
We specify the class with the namespace, the arguments and the service itself. Now we have to creat the physical 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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?php namespace Apn\ApnBundle\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\Loader; class Views extends Extension { protected $_what; public function __construct($what) { $this->_what = $what; } public function load(array $configs, ContainerBuilder $container) { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.yml'); } public function incrementViews($what, $id) { switch($what) { case 'question': $sql = "UPDATE questions SET questions.views = questions.views + 1 WHERE id = $id"; break; } } } |
We defined an “incrementViews” method that deals with the incrementations on the post views. Now inside the controller we can use this service like this:
1 2 |
$viewer= $this->get('questions_viewer'); $viewer->incrementViews('question'); |
Pretty simple, huh ?
More information about this can be found here.