Skip to content

Commit 141e922

Browse files
committed
Moved Service Decoration to a topic article
1 parent 3918ee8 commit 141e922

File tree

2 files changed

+183
-108
lines changed

2 files changed

+183
-108
lines changed

components/dependency_injection/advanced.rst

+20-108
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ Marking Services as Public / Private
1010
------------------------------------
1111

1212
When defining services, you'll usually want to be able to access these definitions
13-
within your application code. These services are called ``public``. For
14-
example, the ``doctrine`` service registered with the container when using
15-
the DoctrineBundle is a public service. This means that you can fetch it
16-
from the container using the ``get()`` method::
13+
within your application code. These services are called *public*. For
14+
example, the ``doctrine`` service is a public service. This means that you can
15+
fetch it from the container using the ``get()`` method::
1716

18-
$doctrine = $container->get('doctrine');
17+
$doctrine = $container->get('doctrine');
1918

2019
In some cases, a service *only* exists to be injected into another service
2120
and is *not* intended to be fetched directly from the container as shown
@@ -73,7 +72,8 @@ However, if a service has been marked as private, you can still alias it
7372

7473
.. note::
7574

76-
Services are by default public.
75+
Services are by default public, but it's considered a good practice to mark
76+
as much services private as possible.
7777

7878
Aliasing
7979
--------
@@ -87,10 +87,11 @@ services.
8787
.. code-block:: yaml
8888
8989
services:
90-
foo:
91-
class: Example\Foo
92-
bar:
93-
alias: foo
90+
app.phpmailer:
91+
class: AppBundle\Mail\PhpMailer
92+
93+
app.mailer:
94+
alias: app.phpmailer
9495
9596
.. code-block:: xml
9697
@@ -100,24 +101,24 @@ services.
100101
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
101102
102103
<services>
103-
<service id="foo" class="Example\Foo" />
104+
<service id="app.phpmailer" class="AppBundle\PhpMailer" />
104105
105-
<service id="bar" alias="foo" />
106+
<service id="app.mailer" alias="app.phpmailer" />
106107
</services>
107108
</container>
108109
109110
.. code-block:: php
110111
111112
use Symfony\Component\DependencyInjection\Definition;
112113
113-
$container->setDefinition('foo', new Definition('Example\Foo'));
114+
$container->setDefinition('app.phpmailer', new Definition('AppBundle\PhpMailer'));
114115
115-
$containerBuilder->setAlias('bar', 'foo');
116+
$containerBuilder->setAlias('app.mailer', 'app.phpmailer');
116117
117-
This means that when using the container directly, you can access the ``foo``
118-
service by asking for the ``bar`` service like this::
118+
This means that when using the container directly, you can access the
119+
``app.phpmailer`` service by asking for the ``app.mailer`` service like this::
119120

120-
$container->get('bar'); // Would return the foo service
121+
$container->get('app.mailer'); // Would return a PhpMailer instance
121122

122123
.. tip::
123124

@@ -126,95 +127,6 @@ service by asking for the ``bar`` service like this::
126127
.. code-block:: yaml
127128
128129
services:
129-
foo:
130-
class: Example\Foo
131-
bar: '@foo'
132-
133-
Decorating Services
134-
-------------------
135-
136-
When overriding an existing definition, the old service is lost:
137-
138-
.. code-block:: php
139-
140-
$container->register('foo', 'FooService');
141-
142-
// this is going to replace the old definition with the new one
143-
// old definition is lost
144-
$container->register('foo', 'CustomFooService');
145-
146-
Most of the time, that's exactly what you want to do. But sometimes,
147-
you might want to decorate the old one instead. In this case, the
148-
old service should be kept around to be able to reference it in the
149-
new one. This configuration replaces ``foo`` with a new one, but keeps
150-
a reference of the old one as ``bar.inner``:
151-
152-
.. configuration-block::
153-
154-
.. code-block:: yaml
155-
156-
bar:
157-
public: false
158-
class: stdClass
159-
decorates: foo
160-
arguments: ["@bar.inner"]
161-
162-
.. code-block:: xml
163-
164-
<service id="bar" class="stdClass" decorates="foo" public="false">
165-
<argument type="service" id="bar.inner" />
166-
</service>
167-
168-
.. code-block:: php
169-
170-
use Symfony\Component\DependencyInjection\Reference;
171-
172-
$container->register('bar', 'stdClass')
173-
->addArgument(new Reference('bar.inner'))
174-
->setPublic(false)
175-
->setDecoratedService('foo');
176-
177-
Here is what's going on here: the ``setDecoratedService()`` method tells
178-
the container that the ``bar`` service should replace the ``foo`` service,
179-
renaming ``foo`` to ``bar.inner``.
180-
By convention, the old ``foo`` service is going to be renamed ``bar.inner``,
181-
so you can inject it into your new service.
182-
183-
.. note::
184-
The generated inner id is based on the id of the decorator service
185-
(``bar`` here), not of the decorated service (``foo`` here). This is
186-
mandatory to allow several decorators on the same service (they need to have
187-
different generated inner ids).
188-
189-
Most of the time, the decorator should be declared private, as you will not
190-
need to retrieve it as ``bar`` from the container. The visibility of the
191-
decorated ``foo`` service (which is an alias for ``bar``) will still be the
192-
same as the original ``foo`` visibility.
193-
194-
You can change the inner service name if you want to:
195-
196-
.. configuration-block::
197-
198-
.. code-block:: yaml
199-
200-
bar:
201-
class: stdClass
202-
public: false
203-
decorates: foo
204-
decoration_inner_name: bar.wooz
205-
arguments: ["@bar.wooz"]
206-
207-
.. code-block:: xml
208-
209-
<service id="bar" class="stdClass" decorates="foo" decoration-inner-name="bar.wooz" public="false">
210-
<argument type="service" id="bar.wooz" />
211-
</service>
212-
213-
.. code-block:: php
214-
215-
use Symfony\Component\DependencyInjection\Reference;
130+
# ...
216131
217-
$container->register('bar', 'stdClass')
218-
->addArgument(new Reference('bar.wooz'))
219-
->setPublic(false)
220-
->setDecoratedService('foo', 'bar.wooz');
132+
app.mailer: '@app.phpmailer'
+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
.. index::
2+
single: Service Container; Decoration
3+
4+
How to Decorating Services
5+
==========================
6+
7+
When overriding an existing definition (e.g. when applying the `Decorator pattern`_),
8+
the old service is lost:
9+
10+
.. configuration-block::
11+
12+
.. code-block:: yaml
13+
14+
services:
15+
app.mailer:
16+
class: AppBundle\Mailer
17+
18+
# this is going to replace the old app.mailer definition with the
19+
# new one, the old definition is lost
20+
app.mailer:
21+
class AppBundle\DecoratingMailer
22+
23+
.. code-block:: xml
24+
25+
<?xml version="1.0" encoding="UTF-8" ?>
26+
<container xmlns="http://symfony.com/schema/dic/services"
27+
xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance"
28+
xsd:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"
29+
>
30+
<services>
31+
32+
<service id="app.mailer" class="AppBundle\Mailer" />
33+
34+
<!-- this is going to replace the old app.mailer definition
35+
with the new one, the old definition is lost -->
36+
<service id="app.mailer" class="AppBundle\DecoratingMailer" />
37+
38+
</service>
39+
</container>
40+
41+
.. code-block:: php
42+
43+
$container->register('mailer', 'AppBundle\Mailer');
44+
45+
// this is going to replace the old app.mailer definition with the new
46+
// one, the old definition is lost
47+
$container->register('mailer', 'AppBundle\DecoratingMailer');
48+
49+
Most of the time, that's exactly what you want to do. But sometimes,
50+
you might want to decorate the old one instead. In this case, the
51+
old service should be kept around to be able to reference it in the
52+
new one. This configuration replaces ``app.mailer`` with a new one, but keeps
53+
a reference of the old one as ``app.decorating_mailer.inner``:
54+
55+
.. configuration-block::
56+
57+
.. code-block:: yaml
58+
59+
services:
60+
# ...
61+
62+
app.decorating_mailer:
63+
class: AppBundle\DecoratingMailer
64+
decorates: app.mailer
65+
arguments: ['@app.decorating_mailer.inner']
66+
public: false
67+
68+
.. code-block:: xml
69+
70+
<?xml version="1.0" encoding="UTF-8" ?>
71+
<container xmlns="http://symfony.com/schema/dic/services"
72+
xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance"
73+
xsd:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"
74+
>
75+
<services>
76+
<!-- ... -->
77+
78+
<service id="app.decorating_mailer"
79+
class="AppBundle\DecoratingMailer"
80+
decorates="app.mailer"
81+
public="false"
82+
>
83+
<argument type="service" id="app.decorating_mailer.inner" />
84+
</service>
85+
86+
</service>
87+
</container>
88+
89+
.. code-block:: php
90+
91+
use Symfony\Component\DependencyInjection\Reference;
92+
93+
// ...
94+
$container->register('app.decorating_mailer', 'AppBundle\DecoratingMailer')
95+
->setDecoratedService('app.mailer')
96+
->addArgument(new Reference('app.decorating_mailer.inner'))
97+
->setPublic(false)
98+
;
99+
100+
Here is what's going on here: the decorates option tells the container that the
101+
``app.decorating_mailer`` service should replace the ``app.mailer`` service. By
102+
convention, the old ``app.mailer`` service is renamed to
103+
``app.decorating_mailer.inner``, so you can inject it into your new service.
104+
105+
.. tip::
106+
107+
Most of the time, the decorator should be declared private, as you will not
108+
need to retrieve it as ``app.decorating_mailer`` from the container.
109+
110+
The visibility of the decorated ``app.mailer`` service (which is an alias
111+
for the new service) will still be the same as the original ``app.mailer``
112+
visibility.
113+
114+
.. note::
115+
116+
The generated inner id is based on the id of the decorator service
117+
(``app.decorating_mailer`` here), not of the decorated service (``app.mailer``
118+
here). This is mandatory to allow several decorators on the same service
119+
(they need to have different generated inner ids).
120+
121+
You can change the inner service name if you want to using the
122+
``decoration_inner_name`` option:
123+
124+
.. configuration-block::
125+
126+
.. code-block:: yaml
127+
128+
services:
129+
app.mailer:
130+
# ...
131+
decoration_inner_name: app.decorating_mailer.wooz
132+
arguments: ['@app.decorating_mailer.wooz']
133+
134+
.. code-block:: xml
135+
136+
<?xml version="1.0" encoding="UTF-8" ?>
137+
<container xmlns="http://symfony.com/schema/dic/services"
138+
xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance"
139+
xsd:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"
140+
>
141+
<services>
142+
<!-- ... -->
143+
144+
<service
145+
decoration-inner-name="app.decorating_mailer.wooz"
146+
>
147+
<argument type="service" id="app.decorating_mailer.wooz" />
148+
</service>
149+
150+
</service>
151+
</container>
152+
153+
.. code-block:: php
154+
155+
use Symfony\Component\DependencyInjection\Reference;
156+
157+
$container->register('app.decorating_mailer', 'AppBundle\DeocratingMailer')
158+
->setDecoratedService('foo', 'app.decorating_mailer.wooz')
159+
->addArgument(new Reference('app.decorating_mailer.wooz'))
160+
// ...
161+
;
162+
163+
.. _decorator pattern: https://en.wikipedia.org/wiki/Decorator_pattern

0 commit comments

Comments
 (0)