Skip to content

Commit 19eb951

Browse files
committed
Merge pull request #1398 from richardmiller/moving_tags_article
Moved and updated the tags article from cookbook to components section
2 parents 1b9b27e + 83636b7 commit 19eb951

File tree

6 files changed

+77
-86
lines changed

6 files changed

+77
-86
lines changed

components/dependency_injection/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
introduction
88
definitions
99
compilation
10+
tags
1011
factories
1112
parentservices
1213

Lines changed: 41 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
.. index::
1+
.. index::
22
single: Service Container; Tags
33

44
How to make your Services use Tags
55
==================================
66

7-
Several of Symfony2's core services depend on tags to recognize which services
8-
should be loaded, notified of events, or handled in some other special way.
9-
For example, Twig uses the tag ``twig.extension`` to load extra extensions.
7+
You can ask a container builder for any services that were tagged when registered
8+
with it. This is useful in compiler passes where you can find services which
9+
were registered in config files you do not have control over and make use
10+
of them. This is useful if your service handles a collection of some kind,
11+
or implements a "chain", in which several alternative strategies are tried
12+
until one of them is successful. You can then allow services to be registered
13+
with a tag and add these services to your collection or chain.
1014

11-
But you can also use tags in your own bundles. For example in case your service
12-
handles a collection of some kind, or implements a "chain", in which several alternative
13-
strategies are tried until one of them is successful. In this article I will use the example
15+
For example, if you are using Swift Mailer and want to implement a
1416
of a "transport chain", which is a collection of classes implementing ``\Swift_Transport``.
15-
Using the chain, the Swift mailer may try several ways of transport, until one succeeds.
16-
This post focuses mainly on the dependency injection part of the story.
17+
Using the chain, Swift Mailer can try several ways of transport, until one
18+
succeeds.
1719

1820
To begin with, define the ``TransportChain`` class::
1921

20-
namespace Acme\MailerBundle;
21-
2222
class TransportChain
2323
{
2424
private $transports;
25-
25+
2626
public function __construct()
2727
{
2828
$this->transports = array();
2929
}
30-
30+
3131
public function addTransport(\Swift_Transport $transport)
3232
{
3333
$this->transports[] = $transport;
@@ -40,33 +40,29 @@ Then, define the chain as a service:
4040

4141
.. code-block:: yaml
4242
43-
# src/Acme/MailerBundle/Resources/config/services.yml
4443
parameters:
45-
acme_mailer.transport_chain.class: Acme\MailerBundle\TransportChain
46-
44+
acme_mailer.transport_chain.class: TransportChain
45+
4746
services:
4847
acme_mailer.transport_chain:
4948
class: %acme_mailer.transport_chain.class%
5049
5150
.. code-block:: xml
5251
53-
<!-- src/Acme/MailerBundle/Resources/config/services.xml -->
54-
5552
<parameters>
56-
<parameter key="acme_mailer.transport_chain.class">Acme\MailerBundle\TransportChain</parameter>
53+
<parameter key="acme_mailer.transport_chain.class">TransportChain</parameter>
5754
</parameters>
58-
55+
5956
<services>
6057
<service id="acme_mailer.transport_chain" class="%acme_mailer.transport_chain.class%" />
6158
</services>
62-
59+
6360
.. code-block:: php
64-
65-
// src/Acme/MailerBundle/Resources/config/services.php
61+
6662
use Symfony\Component\DependencyInjection\Definition;
67-
68-
$container->setParameter('acme_mailer.transport_chain.class', 'Acme\MailerBundle\TransportChain');
69-
63+
64+
$container->setParameter('acme_mailer.transport_chain.class', 'TransportChain');
65+
7066
$container->setDefinition('acme_mailer.transport_chain', new Definition('%acme_mailer.transport_chain.class%'));
7167
7268
Define Services with a Custom Tag
@@ -80,7 +76,6 @@ As an example we add the following transports as services:
8076

8177
.. code-block:: yaml
8278
83-
# src/Acme/MailerBundle/Resources/config/services.yml
8479
services:
8580
acme_mailer.transport.smtp:
8681
class: \Swift_SmtpTransport
@@ -92,77 +87,52 @@ As an example we add the following transports as services:
9287
class: \Swift_SendmailTransport
9388
tags:
9489
- { name: acme_mailer.transport }
95-
90+
9691
.. code-block:: xml
9792
98-
<!-- src/Acme/MailerBundle/Resources/config/services.xml -->
9993
<service id="acme_mailer.transport.smtp" class="\Swift_SmtpTransport">
10094
<argument>%mailer_host%</argument>
10195
<tag name="acme_mailer.transport" />
10296
</service>
103-
97+
10498
<service id="acme_mailer.transport.sendmail" class="\Swift_SendmailTransport">
10599
<tag name="acme_mailer.transport" />
106100
</service>
107-
101+
108102
.. code-block:: php
109-
110-
// src/Acme/MailerBundle/Resources/config/services.php
103+
111104
use Symfony\Component\DependencyInjection\Definition;
112-
105+
113106
$definitionSmtp = new Definition('\Swift_SmtpTransport', array('%mailer_host%'));
114107
$definitionSmtp->addTag('acme_mailer.transport');
115108
$container->setDefinition('acme_mailer.transport.smtp', $definitionSmtp);
116-
109+
117110
$definitionSendmail = new Definition('\Swift_SendmailTransport');
118111
$definitionSendmail->addTag('acme_mailer.transport');
119112
$container->setDefinition('acme_mailer.transport.sendmail', $definitionSendmail);
120113
121-
Notice the tags named "acme_mailer.transport". We want the bundle to recognize
122-
these transports and add them to the chain all by itself. In order to achieve
123-
this, we need to add a ``build()`` method to the ``AcmeMailerBundle`` class::
124-
125-
namespace Acme\MailerBundle;
126-
127-
use Symfony\Component\HttpKernel\Bundle\Bundle;
128-
use Symfony\Component\DependencyInjection\ContainerBuilder;
129-
130-
use Acme\MailerBundle\DependencyInjection\Compiler\TransportCompilerPass;
131-
132-
class AcmeMailerBundle extends Bundle
133-
{
134-
public function build(ContainerBuilder $container)
135-
{
136-
parent::build($container);
137-
138-
$container->addCompilerPass(new TransportCompilerPass());
139-
}
140-
}
114+
Notice the tags named "acme_mailer.transport". This is the custom tag to use ion your compiler pass::
141115

142116
Create a ``CompilerPass``
143117
-------------------------
144118

145-
You will have spotted a reference to the not yet existing ``TransportCompilerPass`` class.
146-
This class will make sure that all services with a tag ``acme_mailer.transport``
147-
will be added to the ``TransportChain`` class by calling the ``addTransport()``
148-
method. The ``TransportCompilerPass`` should look like this::
119+
Your compiler pass can then ask the container for any services with the
120+
custom tag::
149121

150-
namespace Acme\MailerBundle\DependencyInjection\Compiler;
151-
152122
use Symfony\Component\DependencyInjection\ContainerBuilder;
153123
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
154124
use Symfony\Component\DependencyInjection\Reference;
155-
125+
156126
class TransportCompilerPass implements CompilerPassInterface
157127
{
158128
public function process(ContainerBuilder $container)
159129
{
160130
if (false === $container->hasDefinition('acme_mailer.transport_chain')) {
161131
return;
162132
}
163-
133+
164134
$definition = $container->getDefinition('acme_mailer.transport_chain');
165-
135+
166136
foreach ($container->findTaggedServiceIds('acme_mailer.transport') as $id => $attributes) {
167137
$definition->addMethodCall('addTransport', array(new Reference($id)));
168138
}
@@ -176,26 +146,13 @@ to the definition of the ``acme_mailer.transport_chain`` service a call to
176146
The first argument of each of these calls will be the mailer transport service
177147
itself.
178148

179-
.. note::
180-
181-
By convention, tag names consist of the name of the bundle (lowercase,
182-
underscores as separators), followed by a dot, and finally the "real"
183-
name, so the tag "transport" in the AcmeMailerBundle should be: ``acme_mailer.transport``.
149+
Register the pass with the container
150+
------------------------------------
184151

185-
The Compiled Service Definition
186-
-------------------------------
152+
You also need to register the pass with the container, it will then be
153+
run when the container is compiled.
187154

188-
Adding the compiler pass will result in the automatic generation of the following
189-
lines of code in the compiled service container. In case you are working
190-
in the "dev" environment, open the file ``/cache/dev/appDevDebugProjectContainer.php``
191-
and look for the method ``getTransportChainService()``. It should look like this::
192-
193-
protected function getAcmeMailer_TransportChainService()
194-
{
195-
$this->services['acme_mailer.transport_chain'] = $instance = new \Acme\MailerBundle\TransportChain();
196-
197-
$instance->addTransport($this->get('acme_mailer.transport.smtp'));
198-
$instance->addTransport($this->get('acme_mailer.transport.sendmail'));
155+
use Symfony\Component\DependencyInjection\ContainerBuilder;
199156

200-
return $instance;
201-
}
157+
$container = new ContainerBuilder();
158+
$container->addCompilerPass(new TransportCompilerPass);

cookbook/map.rst.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292

9393
* :doc:`/cookbook/service_container/event_listener`
9494
* :doc:`/cookbook/service_container/scopes`
95-
* :doc:`/cookbook/service_container/tags`
95+
* :doc:`/cookbook/service_container/compiler_passes`
9696

9797
* :doc:`/cookbook/security/index`
9898

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
How to work with Compiler Passes in Bundles
2+
===========================================
3+
4+
Compiler passes give you an opportunity to manipulate other service
5+
definitions that have been registered with the service container. You
6+
can read about how to create them in the components section ":doc:`/components/dependency_injection/compilation`".
7+
To register a compiler pass from a bundle you need to add it to the build
8+
method of the bundle definition class::
9+
10+
namespace Acme\MailerBundle;
11+
12+
use Symfony\Component\HttpKernel\Bundle\Bundle;
13+
use Symfony\Component\DependencyInjection\ContainerBuilder;
14+
15+
use Acme\MailerBundle\DependencyInjection\Compiler\CustomCompilerPass;
16+
17+
class AcmeMailerBundle extends Bundle
18+
{
19+
public function build(ContainerBuilder $container)
20+
{
21+
parent::build($container);
22+
23+
$container->addCompilerPass(new CustomCompilerPass());
24+
}
25+
}
26+
27+
One of the common tasks for compiler passes is to work with tagged services,
28+
read more about this in the components section ":doc:`/components/dependency_injection/tags`".
29+
If you are using custom tags in a bundle then by convention, tag names consist
30+
of the name of the bundle (lowercase, underscores as separators), followed
31+
by a dot, and finally the "real" name, so the tag "transport" in the AcmeMailerBundle
32+
should be: ``acme_mailer.transport``.

cookbook/service_container/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ Service Container
66

77
event_listener
88
scopes
9-
tags
9+
compiler_passes

redirection_map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
/cookbook/tools/finder /components/finder
1010
/cookbook/service_container/parentservices /components/dependency_injection/parentservices
1111
/cookbook/service_container/factories /components/dependency_injection/factories
12+
/cookbook/service_container/tags /components/dependency_injection/tags
1213
/reference/configuration/mongodb /bundles/DoctrineMongoDBBundle/config
1314
/reference/YAML /components/yaml
1415
/components/dependency_injection /components/dependency_injection/introduction

0 commit comments

Comments
 (0)