Decoration Of Services With Symfony

Honestly I feel awful and don’t want to do anything, included writing a blog post…

Anyway.. we will see how it will be.

let’s start!

***

Let say we have a component that we can access over dependency injection container and we want to add some new features to the component without touch to existing implementation. We can implement decorator desgin pattern also, because of the exact reason for existence of this pattern. But why don’t we do this with Symfony way ?

Symfony provides an easy way to decoration of services.

Let say we have a mail client in our project like the following.

# services.yml
services:
mailer:
class: AppBundle\Services\Mailler

And… I want to log all reciepents, sender, and subject informations when my application try to send a mail to anywhere. So I decided to decorate my existing mailer implementation.

# services.yml
services:
mailer:
class: AppBundle\Services\Mailler
    mailer:
class: AppBundle\Services\Mailer\LoggerDecorator

The last one will replace the first one. But sometimes we need to keep old one instead of replace like that way.

# services.yml
services:
mailer:
class: AppBundle\Services\Mailler

mailer_logger_decorator:
class: AppBundle\Services\Mailer\LoggerDecorator
decorates: mailer
arguments: ['@mailer_logger_decorator.inner']
public: false

The ‘decorates’ option help us to replace the ‘mailer’ service with ‘mailer_logger_decorator’ service. We marked the decorator service as private in above because of we don’t need to fetch the decorator service directly in anywhere.

Next example, I want to sending mails as asynchronous but still i want to keep my logging feature. Symfony allow to decorate the services with more than one decorator service.You can manage the priority with ‘decoration_priority’ option.

# services.yml
services:
mailer:
class: AppBundle\Services\Mailler

mailer_logger_decorator:
class: AppBundle\Services\Mailer\LoggerDecorator
decorates: mailer
arguments: ['@mailer_logger_decorator.inner']
public: false
decoration_priority: 1
    mailer_queue_decorator:
class: AppBundle\Services\Mailer\QueueDecorator
decorates: mailer
arguments: ['@mailer_queue_decorator.inner']
public: false
decoration_priority: 2

Your declarations will generate the following code.

use AppBundle\Services\Mailer\QueueDecorator;
use AppBundle\Services\Mailer\LoggerDecorator;
use AppBundle\Services\Mailler;
$this->services['mailer'] = new LoggerDecorator(new QueueDecorator(new Mailler()));

Enjoy!

References:
http://symfony.com/doc/current/service_container/service_decoration.html