@@ -100,25 +100,36 @@ working with optional dependencies. It is also more difficult to use in
100100combination with class hierarchies: if a class uses constructor injection
101101then extending it and overriding the constructor becomes problematic.
102102
103- Setter Injection
104- ----------------
103+ Immutable-setter Injection
104+ -------------------
105105
106- Another possible injection point into a class is by adding a setter method
107- that accepts the dependency ::
106+ Another possible injection in to use a method which return `` static ``,
107+ this approach allow you to make a service immutable ::
108108
109109 // ...
110+
110111 class NewsletterManager
111112 {
112113 private $mailer;
113114
114- public function setMailer(MailerInterface $mailer)
115+ /**
116+ * @required
117+ * @return static
118+ */
119+ public function withMailer(MailerInterface $mailer)
115120 {
116- $this->mailer = $mailer;
121+ $new = clone $this;
122+ $new->mailer = $mailer;
123+
124+ return $new;
117125 }
118126
119127 // ...
120128 }
121129
130+ In order to use the full potential of this approach, you can define a third argument
131+ which allow the container to return the newly created service:
132+
122133.. configuration-block ::
123134
124135 .. code-block :: yaml
@@ -130,7 +141,7 @@ that accepts the dependency::
130141 app.newsletter_manager :
131142 class : App\Mail\NewsletterManager
132143 calls :
133- - [setMailer , ['@mailer']]
144+ - [withMailer , ['@mailer'], true ]
134145
135146 .. code-block :: xml
136147
@@ -145,7 +156,7 @@ that accepts the dependency::
145156 <!-- ... -->
146157
147158 <service id =" app.newsletter_manager" class =" App\Mail\NewsletterManager" >
148- <call method =" setMailer " >
159+ <call method =" withMailer " use-result =true >
149160 <argument type =" service" id =" mailer" />
150161 </call >
151162 </service >
@@ -160,36 +171,55 @@ that accepts the dependency::
160171
161172 // ...
162173 $container->register('app.newsletter_manager', NewsletterManager::class)
163- ->addMethodCall('setMailer ', [new Reference('mailer')]);
174+ ->addMethodCall('withMailer ', [new Reference('mailer')], true );
164175
165- This time the advantages are :
176+ .. note : :
166177
167- * Setter injection works well with optional dependencies. If you do not
168- need the dependency, then do not call the setter .
178+ This type of injection requires that you add a `` @return static `` docblock in order
179+ for the container to be capable of registering the method .
169180
170- * You can call the setter multiple times. This is particularly useful if
171- the method adds the dependency to a collection. You can then have a variable
172- number of dependencies.
181+ This approach is useful if you need to configure your service according to your needs,
182+ so, what are the advantages?
173183
174- The disadvantages of setter injection are:
184+ * Your service becomes immutable, as the container will return a new object,
185+ the initial service stays clean and unchanged.
175186
176- * The setter can be called more than just at the time of construction so
177- you cannot be sure the dependency is not replaced during the lifetime
178- of the object (except by explicitly writing the setter method to check
179- if it has already been called).
187+ * You can easily change the injected service as long as it respect the interface/type
188+ asked by the initial service.
180189
181- * You cannot be sure the setter will be called and so you need to add checks
182- that any required dependencies are injected.
190+ * It allow you to get rid of factory usages which can lead a more complex code
183191
184- Property Injection
185- ------------------
192+ * As the dependency is optional, you can easily decide to receive the service
193+ without using the "with" method.
186194
187- Another possibility is setting public fields of the class directly::
195+ * As the method automatically receive and set the attribute value,
196+ you couldn't obtain the newly created service without this dependency.
197+
198+ The disadvantages are:
199+
200+ * As the ``@return static `` docblock is required by the container to
201+ understand that the method return a new object,
202+ you can found that adding docblock for a single method isn't adapted or
203+ link your code to the container.
204+
205+ * As this approach force the container to create a new object
206+ once the method is called, you can found hard to debug and test your code.
207+
208+ Setter Injection
209+ ----------------
210+
211+ Another possible injection point into a class is by adding a setter method
212+ that accepts the dependency::
188213
189214 // ...
190215 class NewsletterManager
191216 {
192- public $mailer;
217+ private $mailer;
218+
219+ public function setMailer(MailerInterface $mailer)
220+ {
221+ $this->mailer = $mailer;
222+ }
193223
194224 // ...
195225 }
@@ -204,8 +234,8 @@ Another possibility is setting public fields of the class directly::
204234
205235 app.newsletter_manager :
206236 class : App\Mail\NewsletterManager
207- properties :
208- mailer : ' @mailer'
237+ calls :
238+ - [setMailer, [ '@mailer']]
209239
210240 .. code-block :: xml
211241
@@ -220,7 +250,9 @@ Another possibility is setting public fields of the class directly::
220250 <!-- ... -->
221251
222252 <service id =" app.newsletter_manager" class =" App\Mail\NewsletterManager" >
223- <property name =" mailer" type =" service" id =" mailer" />
253+ <call method =" setMailer" >
254+ <argument type =" service" id =" mailer" />
255+ </call >
224256 </service >
225257 </services >
226258 </container >
@@ -232,53 +264,41 @@ Another possibility is setting public fields of the class directly::
232264 use Symfony\Component\DependencyInjection\Reference;
233265
234266 // ...
235- $container->register('newsletter_manager', NewsletterManager::class)
236- ->setProperty('mailer ', new Reference('mailer'));
267+ $container->register('app. newsletter_manager', NewsletterManager::class)
268+ ->addMethodCall('setMailer ', [ new Reference('mailer')] );
237269
238- There are mainly only disadvantages to using property injection, it is similar
239- to setter injection but with these additional important problems:
270+ This time the advantages are:
240271
241- * You cannot control when the dependency is set at all, it can be changed
242- at any point in the object's lifetime .
272+ * Setter injection works well with optional dependencies. If you do not
273+ need the dependency, then do not call the setter .
243274
244- * You cannot use type hinting so you cannot be sure what dependency is injected
245- except by writing into the class code to explicitly test the class instance
246- before using it .
275+ * You can call the setter multiple times. This is particularly useful if
276+ the method adds the dependency to a collection. You can then have a variable
277+ number of dependencies .
247278
248- But, it is useful to know that this can be done with the service container,
249- especially if you are working with code that is out of your control, such
250- as in a third party library, which uses public properties for its dependencies.
279+ The disadvantages of setter injection are:
251280
252- Immutable Injection
253- -------------------
281+ * The setter can be called more than just at the time of construction so
282+ you cannot be sure the dependency is not replaced during the lifetime
283+ of the object (except by explicitly writing the setter method to check
284+ if it has already been called).
254285
255- Another possible injection in to use a method which return `` static ``,
256- this approach allow you to make a service immutable::
286+ * You cannot be sure the setter will be called and so you need to add checks
287+ that any required dependencies are injected.
257288
258- // ...
289+ Property Injection
290+ ------------------
291+
292+ Another possibility is setting public fields of the class directly::
259293
294+ // ...
260295 class NewsletterManager
261296 {
262- private $mailer;
263-
264- /**
265- * @required
266- * @return static
267- */
268- public function withMailer(MailerInterface $mailer)
269- {
270- $new = clone $this;
271- $new->mailer = $mailer;
272-
273- return $new;
274- }
297+ public $mailer;
275298
276299 // ...
277300 }
278301
279- In order to use the full potential of this approach, you can define a third argument
280- which allow the container to return the newly created service:
281-
282302.. configuration-block ::
283303
284304 .. code-block :: yaml
@@ -289,8 +309,8 @@ which allow the container to return the newly created service:
289309
290310 app.newsletter_manager :
291311 class : App\Mail\NewsletterManager
292- calls :
293- - [withMailer, [ '@mailer'], true]
312+ properties :
313+ mailer : ' @mailer'
294314
295315 .. code-block :: xml
296316
@@ -305,9 +325,7 @@ which allow the container to return the newly created service:
305325 <!-- ... -->
306326
307327 <service id =" app.newsletter_manager" class =" App\Mail\NewsletterManager" >
308- <call method =" withMailer" use-result =true>
309- <argument type =" service" id =" mailer" />
310- </call >
328+ <property name =" mailer" type =" service" id =" mailer" />
311329 </service >
312330 </services >
313331 </container >
@@ -319,37 +337,19 @@ which allow the container to return the newly created service:
319337 use Symfony\Component\DependencyInjection\Reference;
320338
321339 // ...
322- $container->register('app.newsletter_manager', NewsletterManager::class)
323- ->addMethodCall('withMailer', [new Reference('mailer')], true);
324-
325- .. note ::
326-
327- This type of injection requires that you add a ``@return static `` docblock in order
328- for the container to be capable of registering the method.
329-
330- This approach is useful if you need to configure your service according to your needs,
331- so, what are the advantages?
332-
333- * Your service becomes immutable, as the container will return a new object,
334- the initial service stays clean and unchanged.
335-
336- * You can easily change the injected service as long as it respect the interface/type
337- asked by the initial service.
338-
339- * It allow you to get rid of factory usages which can lead a more complex code
340-
341- * As the dependency is optional, you can easily decide to receive the service
342- without using the "with" method.
340+ $container->register('newsletter_manager', NewsletterManager::class)
341+ ->setProperty('mailer', new Reference('mailer'));
343342
344- * As the method automatically receive and set the attribute value,
345- you couldn't obtain the newly created service without this dependency.
343+ There are mainly only disadvantages to using property injection, it is similar
344+ to setter injection but with these additional important problems:
346345
347- The disadvantages are:
346+ * You cannot control when the dependency is set at all, it can be changed
347+ at any point in the object's lifetime.
348348
349- * As the ``@return static `` docblock is required by the container to
350- understand that the method return a new object,
351- you can found that adding docblock for a single method isn't adapted or
352- link your code to the container.
349+ * You cannot use type hinting so you cannot be sure what dependency is injected
350+ except by writing into the class code to explicitly test the class instance
351+ before using it.
353352
354- * As this approach force the container to create a new object
355- once the method is called, you can found hard to debug and test your code.
353+ But, it is useful to know that this can be done with the service container,
354+ especially if you are working with code that is out of your control, such
355+ as in a third party library, which uses public properties for its dependencies.
0 commit comments