Skip to content

Commit 6c60e00

Browse files
committed
Service subscribers draft
1 parent d8fcd1c commit 6c60e00

File tree

1 file changed

+175
-0
lines changed

1 file changed

+175
-0
lines changed
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
.. index::
2+
single: DependencyInjection; Service Subscribers
3+
4+
Service Subscribers
5+
================
6+
7+
Symfony's Service Locators provide a powerful way of passing a subset of
8+
services from the service container to a service. Sometimes, you may want a
9+
more concrete way of defining the services contained within a service locator.
10+
By implementing ``ServiceSubscriberInterface`` you can specify the contents of
11+
the service locator from the object itself. This is useful when a set of
12+
services have the same parent class and similar dependencies.
13+
14+
Suppose you've got a mailing system with multiple ``Updater`` classes for
15+
sending out mails on different occasions. Since every updater has the same
16+
purpose, we start with a base updater::
17+
18+
// src/Updates/AbstractUpdater.php
19+
namespace App\Updates;
20+
21+
use Twig\Environment;
22+
23+
abstract class AbstractUpdater
24+
{
25+
protected $mailer;
26+
protected $twig;
27+
28+
public function __construct(\Swift_Mailer $mailer, Environment $twig)
29+
{
30+
$this->mailer = $mailer;
31+
$this->twig = $twig;
32+
}
33+
34+
abstract public function update($entity);
35+
36+
/**
37+
* Renders a view with Twig
38+
*/
39+
public function render($template, $parameters)
40+
{
41+
return $this->twig->render($template, $parameters);
42+
}
43+
44+
/**
45+
* Sends an email with Swiftmailer
46+
*/
47+
public function send($recipient, $title, $body)
48+
{
49+
$message = new \Swift_Message();
50+
51+
// ...
52+
53+
$this->mailer->send($message);
54+
}
55+
}
56+
57+
Now that we have a base class, we can add updaters for different entities. Note
58+
that we have to inject the dependencies of ``AbstractUpdater`` to each
59+
updater.::
60+
61+
// src/Updates/FooUpdater.php
62+
namespace App\Updates;
63+
64+
use Doctrine\Common\Persistence\ManagerRegistry;
65+
use Twig\Environment;
66+
67+
class FooUpdater extends AbstractUpdater
68+
{
69+
private $doctrine;
70+
71+
public function __construct(\Swift_Mailer $mailer, Environment $twig, ManagerRegistry $doctrine)
72+
{
73+
parent::__construct($mailer, $twig);
74+
75+
$this->doctrine = $doctrine;
76+
}
77+
78+
public function update($entity)
79+
{
80+
// ...
81+
82+
$body = $this->render('Emails/foo_update.html.twig', [/* ... */]);
83+
$this->send($entity->getRecipient(), 'Foo update', $body);
84+
}
85+
}
86+
87+
// src/Updates/BarUpdater.php
88+
namespace App\Updates;
89+
90+
use Doctrine\Common\Persistence\ManagerRegistry;
91+
use Twig\Environment;
92+
93+
class BarUpdater extends AbstractUpdater
94+
{
95+
private $doctrine;
96+
private $barManager;
97+
98+
public function __construct(\Swift_Mailer $mailer, Environment $twig, ManagerRegistry $doctrine, $barManager = null)
99+
{
100+
parent::__construct($mailer, $twig);
101+
102+
$this->doctrine = $doctrine;
103+
$this->barManager = $barManager;
104+
}
105+
106+
public function update($entity)
107+
{
108+
// ...
109+
110+
$body = $this->render('Emails/bar_update.html.twig', [/* ... */]);
111+
$this->send($entity->getRecipient(), 'Bar update', $body);
112+
}
113+
}
114+
115+
If you're using the `default services.yaml configuration`_, no additional
116+
configuration is required for these services thanks to autowiring, but
117+
maintaining the list of dependencies through constructor injection will quickly
118+
become cumbersome, especially if you add a dependency in ``AbstractUpdater``.
119+
120+
By creating a service subscriber of the base class, we can create a more
121+
practical solution for our dependencies.
122+
123+
Defining a Service Subscriber
124+
-----------------------------
125+
126+
First, turn ``AbstractUpdater`` into an implementation of
127+
``ServiceSubscriberInterface`` by adding the static method
128+
``getSubscribedServices`` which maintains a list of subscribed services and
129+
replacing our dependencies with a service locator.::
130+
131+
<code>
132+
133+
With this newly created service subscriber, the updaters can be adapted to
134+
coincide with the service locator::
135+
136+
<code>
137+
138+
Optionally, you'll need to add the ``container.service_subscriber`` tag to
139+
configure the services to be recognized as service subscribers.
140+
141+
.. configuration-block::
142+
143+
.. code-block:: yaml
144+
145+
<code>
146+
147+
.. code-block:: xml
148+
149+
<code>
150+
151+
.. code-block:: php
152+
153+
<code>
154+
155+
Usage
156+
-----
157+
158+
Subscribed services
159+
-------------------
160+
161+
Returns an array of service types required by such instances, optionally keyed by the service names used internally.
162+
163+
For mandatory dependencies:
164+
165+
* array('logger' => 'Psr\Log\LoggerInterface') means the objects use the "logger" name
166+
internally to fetch a service which must implement Psr\Log\LoggerInterface.
167+
* array('Psr\Log\LoggerInterface') is a shortcut for
168+
* array('Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface')
169+
170+
otherwise:
171+
172+
* array('logger' => '?Psr\Log\LoggerInterface') denotes an optional dependency
173+
* array('?Psr\Log\LoggerInterface') is a shortcut for
174+
* array('Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface')
175+

0 commit comments

Comments
 (0)