@@ -6,7 +6,7 @@ Service Container
6
6
=================
7
7
8
8
Your application is *full * of useful objects: one "Mailer" object might help you
9
- deliver email messages while another object might help you save things to the database.
9
+ send email messages while another object might help you save things to the database.
10
10
Almost *everything * that your app "does" is actually done by one of these objects.
11
11
And each time you install a new bundle, you get access to even more!
12
12
@@ -17,21 +17,13 @@ then you can fetch a service by using that service's id::
17
17
$logger = $container->get('logger');
18
18
$entityManager = $container->get('doctrine.entity_manager');
19
19
20
- The container is the *heart * of Symfony: it allows you to standardize and centralize
21
- the way objects are constructed. It makes your life easier, is super fast, and emphasizes
22
- an architecture that promotes reusable and decoupled code. It's also a big reason
23
- that Symfony is so fast and extensible!
24
-
25
- Finally, configuring and using the service container is easy. By the end
26
- of this article, you'll be comfortable creating your own objects via the
27
- container and customizing objects from any third-party bundle. You'll begin
28
- writing code that is more reusable, testable and decoupled, simply because
29
- the service container makes writing good code so easy.
20
+ The container allows you to centralize the way objects are constructed. It makes
21
+ your life easier, promotes a strong architecture and is super fast!
30
22
31
23
Fetching and using Services
32
24
---------------------------
33
25
34
- The moment you start a Symfony app, the container *already * contains many services.
26
+ The moment you start a Symfony app, your container *already * contains many services.
35
27
These are like *tools *, waiting for you to take advantage of them. In your controller,
36
28
you have access to the container via ``$this->container ``. Want to :doc: `log </logging >`
37
29
something? No problem::
@@ -86,11 +78,10 @@ in the container.
86
78
87
79
.. sidebar :: Container: Lazy-loaded for speed
88
80
89
- If the container holds so many useful objects (services), does that mean those
90
- objects are instantiated on *every * request? No! The container is lazy: it doesn't
91
- instantiate a service until (and unless) you ask for it. For example, if you
92
- never use the ``validator `` service during a request, the container will never
93
- instantiate it.
81
+ Wait! Are all the services (objects) instantiated on *every * request? No! The
82
+ container is lazy: it doesn't instantiate a service until (and unless) you ask
83
+ for it. For example, if you never use the ``validator `` service during a request,
84
+ the container will never instantiate it.
94
85
95
86
.. index ::
96
87
single: Service Container; Configuring services
@@ -100,10 +91,9 @@ in the container.
100
91
Creating/Configuring Services in the Container
101
92
----------------------------------------------
102
93
103
- You can also leverage the container to organize your *own * code into services. For
104
- example, suppose you want to show your users a random, happy message every time
105
- they do something. If you put this code in your controller, it can't be re-used.
106
- Instead, you decide to create a new class::
94
+ You can also organize your *own * code into services. For example, suppose you need
95
+ to show your users a random, happy message. If you put this code in your controller,
96
+ it can't be re-used. Instead, you decide to create a new class::
107
97
108
98
// src/AppBundle/Service/MessageGenerator.php
109
99
namespace AppBundle\Service;
@@ -133,14 +123,13 @@ the service container *how* to instantiate it:
133
123
134
124
# app/config/services.yml
135
125
services :
136
- # configures defaults for all services in this file
137
126
_defaults :
138
127
autowire : true
139
128
autoconfigure : true
140
129
141
- # registers all classes in the dir(s) as services
130
+ # load services from whatever directories you want (you can update this!)
142
131
AppBundle\ :
143
- resource : ' ../../src/AppBundle/{Service}'
132
+ resource : ' ../../src/AppBundle/{Service,EventDispatcher,Twig,Form }'
144
133
145
134
.. code-block :: xml
146
135
@@ -153,9 +142,10 @@ the service container *how* to instantiate it:
153
142
TODO
154
143
155
144
That's it! Thanks to the ``AppBundle\ `` line and ``resource `` key below it, all
156
- classes in the ``src/AppBundle/Service `` directory will automatically be added to
157
- the container. Each service's "key" is simply its class name. You can use it immediately
158
- inside your controller::
145
+ classes in the ``src/AppBundle/Service `` directory (and a few other directories)
146
+ will automatically be added to the container.
147
+
148
+ Each service's "key" is its class name. You can use it immediately inside your controller::
159
149
160
150
use AppBundle\Service\MessageGenerator;
161
151
@@ -175,10 +165,8 @@ inside your controller::
175
165
}
176
166
177
167
When you ask for the ``MessageGenerator::class `` service, the container constructs
178
- a new ``MessageGenerator `` object and returns it. If you never ask for the
179
- ``MessageGenerator::class `` service during a request, it's *never * constructed, saving
180
- you memory and increasing the speed of your app. This also means that there's almost
181
- no performance overhead for defining a lot of services.
168
+ a new ``MessageGenerator `` object and returns it. But if you never ask for the service,
169
+ it's *never * constructed: saving memory and speed.
182
170
183
171
As a bonus, the ``MessageGenerator::class `` service is only created *once *: the same
184
172
instance is returned each time you ask for it.
@@ -221,25 +209,26 @@ and set it on a ``$logger`` property::
221
209
}
222
210
223
211
That's it! The container will *automatically * know to pass the ``logger `` service
224
- when instantiating the ``MessageGenerator ``? How does it know to do this? The key
225
- is the ``LoggerInterface `` type-hint in your ``__construct() `` method and the
226
- ``autowire: true `` config in ``services.yml ``. When you type-hint an argument, the
227
- container will automatically find the matching service. If it can't or there is any
228
- ambuiguity, you'll see a clear exception suggesting how to fix it.
212
+ when instantiating the ``MessageGenerator ``? How does it know to do this?
213
+ :doc: `Autowiring </service_container/autowiring >`. The key is the ``LoggerInterface ``
214
+ type-hint in your ``__construct() `` method and the ``autowire: true `` config in
215
+ ``services.yml ``. When you type-hint an argument, the container will automatically
216
+ find the matching service. If it can't or there is any ambuiguity, you'll see a clear
217
+ exception suggesting how to fix it.
229
218
230
219
Be sure to read more about :doc: `autowiring </service_container/autowiring >`.
231
220
232
221
.. tip ::
233
222
234
- How do you know to use ``LoggerInterface `` for the type-hint? The best way to
235
- know this is by reading the docs for whatever service you're using. You can
236
- also use the ``php bin/console debug:container `` console command to get a hint
223
+ How should you know to use ``LoggerInterface `` for the type-hint? The best way
224
+ is by reading the docs for whatever feature you're using. You can also use the
225
+ ``php bin/console debug:container `` console command to get a hint
237
226
to the class name for a service.
238
227
239
228
Handling Multiple Services
240
229
--------------------------
241
230
242
- Suppose you also want to email some site administrator each time a site update is
231
+ Suppose you also want to email a site administrator each time a site update is
243
232
made. To do that, you create a new class::
244
233
245
234
// src/AppBundle/Updates/SiteUpdateManager.php
@@ -289,7 +278,7 @@ the new ``Updates`` sub-directory:
289
278
290
279
# registers all classes in Services & Updates directories
291
280
AppBundle\ :
292
- resource : ' ../../src/AppBundle/{Service,Updates}'
281
+ resource : ' ../../src/AppBundle/{Service,Updates,EventDispatcher,Twig,Form }'
293
282
294
283
.. code-block :: xml
295
284
@@ -316,16 +305,15 @@ Now, you can use the service immediately::
316
305
// ...
317
306
}
318
307
319
- Just like before, when you ask for the ``SiteUpdateManager `` service, the container
320
- will automatically instantiate it for you. By reading the type-hints on the ``__construct() ``
321
- method in that class, it takes care of passing the correct services as arguments.
322
- All of that is taken care of for you.
308
+ Thanks to autowiring and your type-hints in ``__construct() ``, the container creates
309
+ the ``SiteUpdateManager `` object and passes it the correct arguments. In most cases,
310
+ this works perfectly.
323
311
324
312
Manually Wiring Arguments
325
313
-------------------------
326
314
327
- There are a few cases when an argument to a service cannot be autowired. For example,
328
- suppose you want to make the admin email configurable:
315
+ But there are a few cases when an argument to a service cannot be autowired. For
316
+ example, suppose you want to make the admin email configurable:
329
317
330
318
.. code-block :: diff
331
319
@@ -394,7 +382,8 @@ pass here. No problem! In your configuration, you can explicitly set this argume
394
382
TODO
395
383
396
384
Thanks to this, the container will pass ``manager@example.com `` as the third argument
397
- to ``__construct `` when creating the ``SiteUpdateManager `` service.
385
+ to ``__construct `` when creating the ``SiteUpdateManager `` service. The other arguments
386
+ will still be autowired.
398
387
399
388
.. _service-container-parameters :
400
389
@@ -460,6 +449,67 @@ You can also fetch parameters directly from the container::
460
449
461
450
For more info about parameters, see :doc: `/service_container/parameters `.
462
451
452
+ Choose a Specific Service
453
+ -------------------------
454
+
455
+ The ``MessageGenerator `` service created earlier requires a ``LoggerInterface `` argument::
456
+
457
+ // src/AppBundle/Service/MessageGenerator.php
458
+ // ...
459
+
460
+ use Psr\Log\LoggerInterface;
461
+
462
+ class MessageGenerator
463
+ {
464
+ private $logger;
465
+
466
+ public function __construct(LoggerInterface $logger)
467
+ {
468
+ $this->logger = $logger;
469
+ }
470
+ // ...
471
+ }
472
+
473
+ However, there are *multiple * services in the container that implement ``LoggerInterface ``,
474
+ such as ``logger ``, ``monolog.logger.request ``, ``monolog.logger.php ``, etc. How
475
+ does the container know which one to use?
476
+
477
+ In these situations, the container is usually configured to automatically choose
478
+ one of the services - ``logger `` in this case (read more about why in :ref: `service-autowiring-alias `).
479
+ But, you can control this and pass in a different logger:
480
+
481
+ .. configuration-block ::
482
+
483
+ .. code-block :: yaml
484
+
485
+ # app/config/services.yml
486
+ services :
487
+ # ... same code as before
488
+
489
+ # explicitly configure the service
490
+ AppBundle\Service\MessageGenerator :
491
+ arguments :
492
+ $logger : ' @monolog.logger.request'
493
+
494
+ .. code-block :: xml
495
+
496
+ <!-- app/config/services.xml -->
497
+ TODO
498
+
499
+ .. code-block :: php
500
+
501
+ // app/config/services.php
502
+ TODO
503
+
504
+ This tells the container that the ``$logger `` argument to ``_construct `` should use
505
+ service whose id is ``monolog.logger.request ``.
506
+
507
+ .. tip ::
508
+
509
+ The ``@ `` symbol is important: that's what tells the container you want to pass
510
+ the *service * whose id is ``monolog.logger.request ``, and not just the *string *
511
+ ``monolog.logger.request ``.
512
+
463
513
Learn more
464
514
----------
465
515
0 commit comments