1
- .. index ::
1
+ .. index ::
2
2
single: Service Container; Tags
3
3
4
4
How to make your Services use Tags
5
5
==================================
6
6
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.
10
14
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
14
16
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 .
17
19
18
20
To begin with, define the ``TransportChain `` class::
19
21
20
- namespace Acme\MailerBundle;
21
-
22
22
class TransportChain
23
23
{
24
24
private $transports;
25
-
25
+
26
26
public function __construct()
27
27
{
28
28
$this->transports = array();
29
29
}
30
-
30
+
31
31
public function addTransport(\Swift_Transport $transport)
32
32
{
33
33
$this->transports[] = $transport;
@@ -40,33 +40,29 @@ Then, define the chain as a service:
40
40
41
41
.. code-block :: yaml
42
42
43
- # src/Acme/MailerBundle/Resources/config/services.yml
44
43
parameters :
45
- acme_mailer.transport_chain.class : Acme\MailerBundle\ TransportChain
46
-
44
+ acme_mailer.transport_chain.class : TransportChain
45
+
47
46
services :
48
47
acme_mailer.transport_chain :
49
48
class : %acme_mailer.transport_chain.class%
50
49
51
50
.. code-block :: xml
52
51
53
- <!-- src/Acme/MailerBundle/Resources/config/services.xml -->
54
-
55
52
<parameters >
56
- <parameter key =" acme_mailer.transport_chain.class" >Acme\MailerBundle\ TransportChain</parameter >
53
+ <parameter key =" acme_mailer.transport_chain.class" >TransportChain</parameter >
57
54
</parameters >
58
-
55
+
59
56
<services >
60
57
<service id =" acme_mailer.transport_chain" class =" %acme_mailer.transport_chain.class%" />
61
58
</services >
62
-
59
+
63
60
.. code-block :: php
64
-
65
- // src/Acme/MailerBundle/Resources/config/services.php
61
+
66
62
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
+
70
66
$container->setDefinition('acme_mailer.transport_chain', new Definition('%acme_mailer.transport_chain.class%'));
71
67
72
68
Define Services with a Custom Tag
@@ -80,7 +76,6 @@ As an example we add the following transports as services:
80
76
81
77
.. code-block :: yaml
82
78
83
- # src/Acme/MailerBundle/Resources/config/services.yml
84
79
services :
85
80
acme_mailer.transport.smtp :
86
81
class : \Swift_SmtpTransport
@@ -92,77 +87,52 @@ As an example we add the following transports as services:
92
87
class : \Swift_SendmailTransport
93
88
tags :
94
89
- { name: acme_mailer.transport }
95
-
90
+
96
91
.. code-block :: xml
97
92
98
- <!-- src/Acme/MailerBundle/Resources/config/services.xml -->
99
93
<service id =" acme_mailer.transport.smtp" class =" \Swift_SmtpTransport" >
100
94
<argument >%mailer_host%</argument >
101
95
<tag name =" acme_mailer.transport" />
102
96
</service >
103
-
97
+
104
98
<service id =" acme_mailer.transport.sendmail" class =" \Swift_SendmailTransport" >
105
99
<tag name =" acme_mailer.transport" />
106
100
</service >
107
-
101
+
108
102
.. code-block :: php
109
-
110
- // src/Acme/MailerBundle/Resources/config/services.php
103
+
111
104
use Symfony\Component\DependencyInjection\Definition;
112
-
105
+
113
106
$definitionSmtp = new Definition('\Swift_SmtpTransport', array('%mailer_host%'));
114
107
$definitionSmtp->addTag('acme_mailer.transport');
115
108
$container->setDefinition('acme_mailer.transport.smtp', $definitionSmtp);
116
-
109
+
117
110
$definitionSendmail = new Definition('\Swift_SendmailTransport');
118
111
$definitionSendmail->addTag('acme_mailer.transport');
119
112
$container->setDefinition('acme_mailer.transport.sendmail', $definitionSendmail);
120
113
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::
141
115
142
116
Create a ``CompilerPass ``
143
117
-------------------------
144
118
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::
149
121
150
- namespace Acme\MailerBundle\DependencyInjection\Compiler;
151
-
152
122
use Symfony\Component\DependencyInjection\ContainerBuilder;
153
123
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
154
124
use Symfony\Component\DependencyInjection\Reference;
155
-
125
+
156
126
class TransportCompilerPass implements CompilerPassInterface
157
127
{
158
128
public function process(ContainerBuilder $container)
159
129
{
160
130
if (false === $container->hasDefinition('acme_mailer.transport_chain')) {
161
131
return;
162
132
}
163
-
133
+
164
134
$definition = $container->getDefinition('acme_mailer.transport_chain');
165
-
135
+
166
136
foreach ($container->findTaggedServiceIds('acme_mailer.transport') as $id => $attributes) {
167
137
$definition->addMethodCall('addTransport', array(new Reference($id)));
168
138
}
@@ -176,26 +146,13 @@ to the definition of the ``acme_mailer.transport_chain`` service a call to
176
146
The first argument of each of these calls will be the mailer transport service
177
147
itself.
178
148
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
+ ------------------------------------
184
151
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.
187
154
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\C omponent\D ependencyInjection\C ontainerBuilder;
199
156
200
- return $instance ;
201
- }
157
+ $container = new ContainerBuilder() ;
158
+ $container->addCompilerPass(new TransportCompilerPass);
0 commit comments