Skip to content

Commit b0d0491

Browse files
committed
fix(wording): fix on injection name & position
The injection is now named Immutable-setter injection. As this type of injection is ~better~ as the setter one, his position is now under the constructor one.
1 parent 5264bf8 commit b0d0491

File tree

1 file changed

+97
-97
lines changed

1 file changed

+97
-97
lines changed

service_container/injection_types.rst

Lines changed: 97 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -100,25 +100,36 @@ working with optional dependencies. It is also more difficult to use in
100100
combination with class hierarchies: if a class uses constructor injection
101101
then 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

Comments
 (0)