Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd33c53e

Browse files
committed
Add Usage and Subscribed services sections to Service subscribers
1 parent6c60e00 commitd33c53e

File tree

1 file changed

+264
-36
lines changed

1 file changed

+264
-36
lines changed

‎service_container/service_subscribers.rst‎

Lines changed: 264 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,21 @@ single: DependencyInjection; Service Subscribers
44
Service Subscribers
55
================
66

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.
7+
Symfony's:doc:`Service Locators</service_container/service_locators>` provide
8+
a powerful way of passing a subset of services from the service container to a
9+
service. Sometimes, you may want a more concrete way of defining the services
10+
contained within a service locator. By implementing
11+
:class:`Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface`
12+
you can specify the contents of the service locator from the object itself.
13+
This is useful when a set of services have the same parent class and similar
14+
dependencies.
1315

1416
Suppose you've got a mailing system with multiple ``Updater`` classes for
1517
sending out mails on different occasions. Since every updater has the same
1618
purpose, we start with a base updater::
1719

18-
// src/Updates/AbstractUpdater.php
19-
namespaceApp\Updates;
20+
// src/AppBundle/Updates/AbstractUpdater.php
21+
namespaceAppBundle\Updates;
2022

2123
use Twig\Environment;
2224

@@ -56,10 +58,10 @@ purpose, we start with a base updater::
5658

5759
Now that we have a base class, we can add updaters for different entities. Note
5860
that we have to inject the dependencies of ``AbstractUpdater`` to each
59-
updater.::
61+
updater::
6062

61-
// src/Updates/FooUpdater.php
62-
namespaceApp\Updates;
63+
// src/AppBundle/Updates/FooUpdater.php
64+
namespaceAppBundle\Updates;
6365

6466
use Doctrine\Common\Persistence\ManagerRegistry;
6567
use Twig\Environment;
@@ -84,9 +86,10 @@ updater.::
8486
}
8587
}
8688

87-
// src/Updates/BarUpdater.php
88-
namespaceApp\Updates;
89+
// src/AppBundle/Updates/BarUpdater.php
90+
namespaceAppBundle\Updates;
8991

92+
use AppBundle\BarManager;
9093
use Doctrine\Common\Persistence\ManagerRegistry;
9194
use Twig\Environment;
9295

@@ -95,7 +98,7 @@ updater.::
9598
private $doctrine;
9699
private $barManager;
97100

98-
public function __construct(\Swift_Mailer $mailer, Environment $twig, ManagerRegistry $doctrine, $barManager = null)
101+
public function __construct(\Swift_Mailer $mailer, Environment $twig, ManagerRegistry $doctrine,BarManager$barManager)
99102
{
100103
parent::__construct($mailer, $twig);
101104

@@ -112,10 +115,11 @@ updater.::
112115
}
113116
}
114117

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``.
118+
If you're using the:ref:`default services.yml configuration<service-container-services-load-example>`,
119+
no additional configuration is required for these services thanks to autowiring,
120+
but maintaining the list of dependencies through constructor injection will
121+
quickly become cumbersome, especially if you add a dependency in
122+
``AbstractUpdater``.
119123

120124
By creating a service subscriber of the base class, we can create a more
121125
practical solution for our dependencies.
@@ -126,50 +130,274 @@ Defining a Service Subscriber
126130
First, turn ``AbstractUpdater`` into an implementation of
127131
``ServiceSubscriberInterface`` by adding the static method
128132
``getSubscribedServices`` which maintains a list of subscribed services and
129-
replacing our dependencies with a service locator.::
133+
replacing our dependencies with a service locator::
134+
135+
// src/AppBundle/Updates/AbstractUpdater.php
136+
namespace AppBundle\Updates;
137+
138+
use Doctrine\Common\Persistence\ManagerRegistry;
139+
use Psr\Container\ContainerInterface;
140+
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
141+
use Twig\Environment;
142+
143+
abstract class AbstractUpdater implements ServiceSubscriberInterface
144+
{
145+
protected $locator;
146+
147+
public function __construct(ContainerInterface $locator)
148+
{
149+
$this->locator = $locator;
150+
}
151+
152+
public static function getSubscribedServices()
153+
{
154+
return [
155+
'doctrine' => '?'.ManagerRegistry::class,
156+
'mailer' => \Swift_Mailer::class,
157+
'twig' => Environment::class
158+
];
159+
}
160+
161+
162+
abstract public function update($entity);
163+
164+
/**
165+
* Renders a view with Twig
166+
*/
167+
public function render($template, $parameters)
168+
{
169+
return $this->locator->get('twig')->render($template, $parameters);
170+
}
171+
172+
/**
173+
* Sends an email with Swiftmailer
174+
*/
175+
public function send($recipient, $title, $body)
176+
{
177+
$message = new \Swift_Message();
178+
179+
// ...
180+
181+
$this->locator->get('mailer')->send($message);
182+
}
183+
}
130184

131-
<code>
132185

133186
With this newly created service subscriber, the updaters can be adapted to
134187
coincide with the service locator::
135188

136-
<code>
189+
// src/AppBundle/Updates/FooUpdater.php
190+
namespace AppBundle\Updates;
191+
192+
class FooUpdater extends AbstractUpdater
193+
{
194+
public function update($entity)
195+
{
196+
// ...
197+
198+
$body = $this->render('Emails/foo_update.html.twig', [/* ... */]);
199+
$this->send($entity->getRecipient(), 'Foo update', $body);
200+
}
201+
}
202+
203+
// src/AppBundle/Updates/BarUpdater.php
204+
namespace AppBundle\Updates;
205+
206+
use AppBundle\BarManager;
207+
use Psr\Container\ContainerInterface;
208+
209+
class BarUpdater extends AbstractUpdater
210+
{
211+
private $barManager;
212+
213+
public function __construct(ContainerInterface $locator, BarManager $barManager)
214+
{
215+
parent::__construct($locator);
216+
217+
$this->barManager = $barManager;
218+
}
219+
220+
public function update($entity)
221+
{
222+
// ...
223+
224+
$body = $this->render('Emails/bar_update.html.twig', [/* ... */]);
225+
$this->send($entity->getRecipient(), 'Bar update', $body);
226+
}
227+
}
137228

138229
Optionally, you'll need to add the ``container.service_subscriber`` tag to
139-
configurethe servicesto be recognized as service subscribers.
230+
the servicesdefinitions of the updaters to enable the service subscribers.
140231

141232
..configuration-block::
142233

143234
..code-block::yaml
144235
145-
<code>
236+
// app/config/services.yml
237+
services:
238+
AppBundle\Updates\:
239+
resource:'../src/Updates'
240+
tags:['container.service_subscriber']
241+
242+
// or
243+
244+
services:
245+
AppBundle\Updates\FooUpdater:
246+
tags:['container.service_subscriber']
247+
248+
AppBundle\Updates\BarUpdater:
249+
tags:['container.service_subscriber']
250+
arguments:
251+
-'@Psr\Container\ContainerInterface'
252+
-'@AppBundle\BarManager'
146253
147254
..code-block::xml
148255
149-
<code>
256+
<!-- app/config/services.xml-->
257+
<?xml version="1.0" encoding="UTF-8" ?>
258+
<containerxmlns="http://symfony.com/schema/dic/services"
259+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
260+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
261+
262+
<services>
263+
264+
<prototypenamespace="AppBundle\Updates\"resource="../src/Updates">
265+
<tagname="container.service_subscriber" />
266+
</prototype>
267+
268+
</services>
269+
</container>
150270
151271
..code-block::php
152272
153273
<code>
154274
275+
Subscribed services
276+
-------------------
277+
278+
Service subscribers rely on the ``getSubscribedServices()`` method to return a
279+
list of services types required to build a service locator for instances of
280+
that service subscriber::
281+
282+
use Psr\Log\LoggerInterface;
283+
284+
public static function getSubscribedServices()
285+
{
286+
return [
287+
'AppBundle\FooInterface',
288+
LoggerInterface::class
289+
];
290+
}
291+
292+
Service types can optionally be keyed by the service names used internally by
293+
the service subscriber::
294+
295+
use Psr\Log\LoggerInterface;
296+
297+
public static function getSubscribedServices()
298+
{
299+
return [
300+
'foo' => 'AppBundle\FooInterface',
301+
'logger' => LoggerInterface::class
302+
];
303+
}
304+
305+
Optional dependencies
306+
~~~~~~~~~~~~~~~~~~~~~
307+
308+
For optional dependencies, prepend the service type with a ``?`` to prevent
309+
errors if the service type has not been implemented in the service container::
310+
311+
use Psr\Log\LoggerInterface;
312+
313+
public static function getSubscribedServices()
314+
{
315+
return [
316+
'?AppBundle\FooInterface',
317+
'logger' => '?'.LoggerInterface::class
318+
];
319+
}
320+
321+
..note::
322+
323+
Make sure an optional service exists by calling ``has()`` on the service
324+
locator before calling the service itself.
325+
155326
Usage
156327
-----
157328

158-
Subscribed services
159-
-------------------
329+
Add the ``container.service_subscriber`` tag to a service definition to turn it
330+
into a service subscriber and add a ``Psr\Container\ContainerInterface``
331+
argument to pass the generated service locator to the service instance.
332+
333+
..configuration-block::
334+
335+
..code-block::yaml
336+
337+
// app/config/services.yml
338+
services:
339+
AppBundle\FooHandler:
340+
tags:['container.service_subscriber']
341+
arguments:
342+
-'@Psr\Container\ContainerInterface'
160343
161-
Returns an array of service types required by such instances, optionally keyed by the service names used internally.
344+
..code-block::xml
345+
346+
<!-- app/config/services.xml-->
347+
<?xml version="1.0" encoding="UTF-8" ?>
348+
<containerxmlns="http://symfony.com/schema/dic/services"
349+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
350+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
351+
352+
<services>
353+
354+
<serviceid="app.foo_handler"class="AppBundle\FooHandler">
355+
<argumenttype="service"id="Psr\Container\ContainerInterface" />
356+
<tagname="container.service_subscriber" />
357+
</service>
162358
163-
For mandatory dependencies:
359+
</services>
360+
</container>
361+
362+
..code-block::php
363+
364+
<code>
164365
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')
366+
An instance of this service would look like this::
169367

170-
otherwise:
368+
namespace AppBundle;
171369

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')
370+
use Psr\Container\ContainerInterface;
371+
use Psr\Log\LoggerInterface;
372+
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
373+
374+
class FooHandler implements ServiceSubscriberInterface
375+
{
376+
/**
377+
* @var ContainerInterface
378+
*/
379+
private $locator;
380+
381+
public function __construct(ContainerInterface $locator)
382+
{
383+
$this->locator = $locator;
384+
}
385+
386+
public static function getSubscribedServices()
387+
{
388+
return [
389+
'AppBundle\FooInterface',
390+
'logger' => '?'.LoggerInterface::class
391+
];
392+
}
393+
394+
public function handle()
395+
{
396+
$this->locator->get('AppBundle\FooInterface')->execute();
397+
398+
if ($this->locator->has('logger')) {
399+
$this->locator->get('logger')->info('Executed foo.');
400+
}
401+
}
402+
}
175403

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp