Skip to content

Commit acc37d6

Browse files
committed
Added documentation for service configurator feature.
1 parent 95582d6 commit acc37d6

File tree

3 files changed

+227
-0
lines changed

3 files changed

+227
-0
lines changed
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
.. index::
2+
single: Dependency Injection; Service configurators
3+
4+
Configuring Services with a Service Configurator
5+
================================================
6+
7+
The Service Configurator is a feature of the Dependency Injection Container that
8+
allows you to use a callable to configure a service after its instantiation.
9+
10+
You can specify a method in another service, a PHP function or a static method
11+
in a class. The service instance is passed to the callable, allowing the
12+
configurator to do whatever it needs to configure the service after its
13+
creation.
14+
15+
A Service Configurator can be used, for example, when you a have a service that
16+
requires complex setup based on configuration settings coming from different
17+
sources/services. Using an external configurator, you can maintain the service
18+
implementation clean and decoupled from the other objects that provide the
19+
configuration for the service.
20+
21+
Another interesting use case is when you have multiple objects that share a
22+
common configuration or that should be configured in a similar way at runtime.
23+
24+
For example, suppose you have an application where you send different types of
25+
emails to users. Emails are passed through different formatters that could be
26+
enabled or not depending on some dynamic application settings. You start
27+
defining a ``NewsletterManager`` class like this::
28+
29+
class NewsletterManager implements EmailFormatterAwareInterface
30+
{
31+
protected $mailer;
32+
protected $enabledFormatters;
33+
34+
public function setMailer(Mailer $mailer)
35+
{
36+
$this->mailer = $mailer;
37+
}
38+
39+
public function setEnabledFormatters(array $enabledFormatters)
40+
{
41+
$this->enabledFormatters = $enabledFormatters;
42+
}
43+
44+
// ...
45+
}
46+
47+
48+
and also a ``GreetingCardManager`` class::
49+
50+
class GreetingCardManager implements EmailFormatterAwareInterface
51+
{
52+
protected $mailer;
53+
protected $enabledFormatters;
54+
55+
public function setMailer(Mailer $mailer)
56+
{
57+
$this->mailer = $mailer;
58+
}
59+
60+
public function setEnabledFormatters(array $enabledFormatters)
61+
{
62+
$this->enabledFormatters = $enabledFormatters;
63+
}
64+
65+
// ...
66+
}
67+
68+
69+
As commented before, the formatters should be set at runtime depending on
70+
application settings, so we also have a ``EmailFormatterManager`` class which is
71+
responsible for loading and validating formatters enabled in the application::
72+
73+
class EmailFormatterManager
74+
{
75+
protected $enabledFormatters;
76+
77+
public function loadFormatters()
78+
{
79+
// ...
80+
}
81+
82+
public function getEnabledFormatters()
83+
{
84+
return $this->enabledFormatters;
85+
}
86+
87+
// ...
88+
}
89+
90+
If your goal is to avoid having to couple ``NewsletterManager`` and
91+
``GreetingCardManager`` with ``EmailFormatterManager``, then you might want to
92+
create a configurator class to configure these instances::
93+
94+
class EmailConfigurator
95+
{
96+
private $formatterManager;
97+
98+
public function __construct(EmailFormatterManager $formatterManager)
99+
{
100+
$this->formatterManager = $formatterManager;
101+
}
102+
103+
public function configure(EmailFormatterAwareInterface $emailManager)
104+
{
105+
$emailManager->setEnabledFormatters(
106+
$this->formatterManager->getEnabledFormatters()
107+
);
108+
}
109+
110+
// ...
111+
}
112+
113+
``EmailConfigurator`` task is to inject enabled filters to ``NewsletterManager``
114+
and ``GreetingCardManager`` because they are not aware of where the enabled
115+
filters come from. In the other hand, the ``EmailFormatterManager`` holds the
116+
knowledge about the enabled formatters and how to load them, keeping the single
117+
responsibility principle.
118+
119+
Configurator Service Config
120+
---------------------------
121+
122+
The service config for the above classes would look something like this:
123+
124+
.. configuration-block::
125+
126+
.. code-block:: yaml
127+
128+
parameters:
129+
# ...
130+
newsletter_manager.class: NewsletterManager
131+
greeting_card_manager.class: GreetingCardManager
132+
email_formatter_manager.class: EmailFormatterManager
133+
email_configurator.class: EmailConfigurator
134+
135+
services:
136+
my_mailer:
137+
# ...
138+
139+
email_formatter_manager:
140+
class: "%email_formatter_manager.class%"
141+
# ...
142+
143+
email_configurator:
144+
class: "%email_configurator.class%"
145+
arguments: [@email_formatter_manager]
146+
# ...
147+
148+
newsletter_manager:
149+
class: "%newsletter_manager.class%"
150+
calls:
151+
- [setMailer, [@my_mailer]]
152+
configurator: [@email_configurator, configure]
153+
154+
greeting_card_manager:
155+
class: "%greeting_card_manager.class%"
156+
calls:
157+
- [setMailer, [@my_mailer]]
158+
configurator: [@email_configurator, configure]
159+
160+
161+
.. code-block:: xml
162+
163+
<parameters>
164+
<!-- ... -->
165+
<parameter key="newsletter_manager.class">NewsletterManager</parameter>
166+
<parameter key="greeting_card_manager.class">GreetingCardManager</parameter>
167+
<parameter key="email_formatter_manager.class">EmailFormatterManager</parameter>
168+
<parameter key="email_configurator.class">EmailConfigurator</parameter>
169+
</parameters>
170+
171+
<services>
172+
<service id="my_mailer" ...>
173+
<!-- ... -->
174+
</service>
175+
<service id="email_formatter_manager" class="%email_formatter_manager.class%">
176+
<!-- ... -->
177+
</service>
178+
<service id="email_configurator" class="%email_configurator.class%">
179+
<argument type="service" id="email_formatter_manager" />
180+
<!-- ... -->
181+
</service>
182+
<service id="newsletter_manager" class="%newsletter_manager.class%">
183+
<call method="setMailer">
184+
<argument type="service" id="my_mailer" />
185+
</call>
186+
<configurator service="email_configurator" method="configure" />
187+
</service>
188+
<service id="greeting_card_manager" class="%greeting_card_manager.class%">
189+
<call method="setMailer">
190+
<argument type="service" id="my_mailer" />
191+
</call>
192+
<configurator service="email_configurator" method="configure" />
193+
</service>
194+
</services>
195+
196+
.. code-block:: php
197+
198+
use Symfony\Component\DependencyInjection\Definition;
199+
use Symfony\Component\DependencyInjection\Reference;
200+
201+
// ...
202+
$container->setParameter('newsletter_manager.class', 'NewsletterManager');
203+
$container->setParameter('greeting_card_manager.class', 'GreetingCardManager');
204+
$container->setParameter('email_formatter_manager.class', 'EmailFormatterManager');
205+
$container->setParameter('email_configurator.class', 'EmailConfigurator');
206+
207+
$container->setDefinition('my_mailer', ...);
208+
$container->setDefinition('email_formatter_manager', ...);
209+
$container->setDefinition('email_configurator', ...);
210+
$container->setDefinition('newsletter_manager', new Definition(
211+
'%newsletter_manager.class%'
212+
))->addMethodCall('setMailer', array(
213+
new Reference('my_mailer'),
214+
))->setConfigurator(array(
215+
new Reference('email_configurator'),
216+
'configure',
217+
)));
218+
$container->setDefinition('greeting_card_manager', new Definition(
219+
'%greeting_card_manager.class%'
220+
))->addMethodCall('setMailer', array(
221+
new Reference('my_mailer'),
222+
))->setConfigurator(array(
223+
new Reference('email_configurator'),
224+
'configure',
225+
)));

components/dependency_injection/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
compilation
1111
tags
1212
factories
13+
configurators
1314
parentservices
1415
advanced
1516
workflow

components/map.rst.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* :doc:`/components/dependency_injection/compilation`
3131
* :doc:`/components/dependency_injection/tags`
3232
* :doc:`/components/dependency_injection/factories`
33+
* :doc:`/components/dependency_injection/configurators`
3334
* :doc:`/components/dependency_injection/parentservices`
3435
* :doc:`/components/dependency_injection/advanced`
3536
* :doc:`/components/dependency_injection/workflow`

0 commit comments

Comments
 (0)