- Service Container
- Fetching and using Services
- Creating/Configuring Services in the Container
- Injecting Services/Config into a Service
- Service Parameters
- Choose a Specific Service
- Binding Arguments by Name or Type
- The autowire Option
- The autoconfigure Option
- Public Versus Private Services
- Importing Many Services at once with resource
- Explicitly Configuring Services and Arguments
- Learn more
Service Container
Screencast
Do you prefer video tutorials? Check out the Symfony Fundamentals screencast series.
Your application is full of useful objects: a "Mailer" object might help yousend emails while another object might help you save things to the database.Almost everything that your app "does" is actually done by one of these objects.And each time you install a new bundle, you get access to even more!
In Symfony, these useful objects are called services and each service livesinside a very special object called the service container. The containerallows you to centralize the way objects are constructed. It makes your lifeeasier, promotes a strong architecture and is super fast!
Fetching and using Services
The moment you start a Symfony app, your container already contains many services.These are like tools: waiting for you to take advantage of them. In your controller,you can "ask" for a service from the container by type-hinting an argument with theservice's class or interface name. Want to log something? No problem:
- // src/Controller/ProductController.php
- // ...
- use Psr\Log\LoggerInterface;
- /**
- * @Route("/products")
- */
- public function list(LoggerInterface $logger)
- {
- $logger->info('Look! I just used a service');
- // ...
- }
What other services are available? Find out by running:
- $ php bin/console debug:autowiring
- # this is just a *small* sample of the output...
- Describes a logger instance.
- Psr\Log\LoggerInterface (monolog.logger)
- Request stack that controls the lifecycle of requests.
- Symfony\Component\HttpFoundation\RequestStack (request_stack)
- Interface for the session.
- Symfony\Component\HttpFoundation\Session\SessionInterface (session)
- RouterInterface is the interface that all Router classes must implement.
- Symfony\Component\Routing\RouterInterface (router.default)
- [...]
When you use these type-hints in your controller methods or inside yourown services, Symfony will automaticallypass you the service object matching that type.
Throughout the docs, you'll see how to use the many different services that livein the container.
Tip
There are actually many more services in the container, and each service hasa unique id in the container, like session
or router.default
. For a fulllist, you can run php bin/console debug:container
. But most of the time,you won't need to worry about this. See Choose a Specific Service.See How to Debug the Service Container & List Services.
Creating/Configuring Services in the Container
You can also organize your own code into services. For example, suppose you needto show your users a random, happy message. If you put this code in your controller,it can't be re-used. Instead, you decide to create a new class:
- // src/Service/MessageGenerator.php
- namespace App\Service;
- class MessageGenerator
- {
- public function getHappyMessage()
- {
- $messages = [
- 'You did it! You updated the system! Amazing!',
- 'That was one of the coolest updates I\'ve seen all day!',
- 'Great work! Keep going!',
- ];
- $index = array_rand($messages);
- return $messages[$index];
- }
- }
Congratulations! You've just created your first service class! You can use it immediatelyinside your controller:
- use App\Service\MessageGenerator;
- public function new(MessageGenerator $messageGenerator)
- {
- // thanks to the type-hint, the container will instantiate a
- // new MessageGenerator and pass it to you!
- // ...
- $message = $messageGenerator->getHappyMessage();
- $this->addFlash('success', $message);
- // ...
- }
When you ask for the MessageGenerator
service, the container constructs a newMessageGenerator
object and returns it (see sidebar below). But if you never askfor the service, it's never constructed: saving memory and speed. As a bonus, theMessageGenerator
service is only created once: the same instance is returnedeach time you ask for it.
Automatic Service Loading in services.yaml
The documentation assumes you're using the following service configuration,which is the default config for a new project:
- YAML
- # config/services.yaml
- services:
- # default configuration for services in *this* file
- _defaults:
- autowire: true # Automatically injects dependencies in your services.
- autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
- public: false # Allows optimizing the container by removing unused services; this also means
- # fetching services directly from the container via $container->get() won't work.
- # The best practice is to be explicit about your dependencies anyway.
- # makes classes in src/ available to be used as services
- # this creates a service per class whose id is the fully-qualified class name
- App\:
- resource: '../src/*'
- exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
- # ...
- XML
- <!-- config/services.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <services>
- <!-- Default configuration for services in *this* file -->
- <defaults autowire="true" autoconfigure="true" public="false"/>
- <prototype namespace="App\" resource="../src/*" exclude="../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}"/>
- </services>
- </container>
- PHP
- // config/services.php
- namespace Symfony\Component\DependencyInjection\Loader\Configurator;
- return function(ContainerConfigurator $configurator) {
- // default configuration for services in *this* file
- $services = $configurator->services()
- ->defaults()
- ->autowire() // Automatically injects dependencies in your services.
- ->autoconfigure() // Automatically registers your services as commands, event subscribers, etc.
- ;
- // makes classes in src/ available to be used as services
- // this creates a service per class whose id is the fully-qualified class name
- $services->load('App\\', '../src/*')
- ->exclude('../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}');
- };
Tip
The value of the resource
and exclude
options can be any validglob pattern). The value of the exclude
option can also be anarray of glob patterns.
Thanks to this configuration, you can automatically use any classes from thesrc/
directory as a service, without needing to manually configureit. Later, you'll learn more about this in Importing Many Services at once with resource.
If you'd prefer to manually wire your service, that's totally possible: seeExplicitly Configuring Services and Arguments.
Injecting Services/Config into a Service
What if you need to access the logger
service from within MessageGenerator
?No problem! Create a __construct()
method with a $logger
argument that hasthe LoggerInterface
type-hint. Set this on a new $logger
propertyand use it later:
- // src/Service/MessageGenerator.php
- // ...
- use Psr\Log\LoggerInterface;
- class MessageGenerator
- {
- private $logger;
- public function __construct(LoggerInterface $logger)
- {
- $this->logger = $logger;
- }
- public function getHappyMessage()
- {
- $this->logger->info('About to find a happy message!');
- // ...
- }
- }
That's it! The container will automatically know to pass the logger
servicewhen instantiating the MessageGenerator
. How does it know to do this?Autowiring. The key is the LoggerInterface
type-hint in your __construct()
method and the autowire: true
config inservices.yaml
. When you type-hint an argument, the container will automaticallyfind the matching service. If it can't, you'll see a clear exception with a helpfulsuggestion.
By the way, this method of adding dependencies to your _construct()
method iscalled _dependency injection. It's a scary term for a simple concept.
How should you know to use LoggerInterface
for the type-hint? You can eitherread the docs for whatever feature you're using, or get a list of autowireabletype-hints by running:
- $ php bin/console debug:autowiring
- # this is just a *small* sample of the output...
- Describes a logger instance.
- Psr\Log\LoggerInterface (monolog.logger)
- Request stack that controls the lifecycle of requests.
- Symfony\Component\HttpFoundation\RequestStack (request_stack)
- Interface for the session.
- Symfony\Component\HttpFoundation\Session\SessionInterface (session)
- RouterInterface is the interface that all Router classes must implement.
- Symfony\Component\Routing\RouterInterface (router.default)
- [...]
Handling Multiple Services
Suppose you also want to email a site administrator each time a site update ismade. To do that, you create a new class:
- // src/Updates/SiteUpdateManager.php
- namespace App\Updates;
- use App\Service\MessageGenerator;
- class SiteUpdateManager
- {
- private $messageGenerator;
- private $mailer;
- public function __construct(MessageGenerator $messageGenerator, \Swift_Mailer $mailer)
- {
- $this->messageGenerator = $messageGenerator;
- $this->mailer = $mailer;
- }
- public function notifyOfSiteUpdate()
- {
- $happyMessage = $this->messageGenerator->getHappyMessage();
- $message = (new \Swift_Message('Site update just happened!'))
- ->setFrom('[email protected]')
- ->setTo('[email protected]')
- ->addPart(
- 'Someone just updated the site. We told them: '.$happyMessage
- );
- return $this->mailer->send($message) > 0;
- }
- }
This needs the MessageGenerator
and the Swift_Mailer
service. That's noproblem! In fact, this new service is ready to be used. In a controller, for example,you can type-hint the new SiteUpdateManager
class and use it:
- // src/Controller/SiteController.php
- // ...
- use App\Updates\SiteUpdateManager;
- public function new(SiteUpdateManager $siteUpdateManager)
- {
- // ...
- if ($siteUpdateManager->notifyOfSiteUpdate()) {
- $this->addFlash('success', 'Notification mail was sent successfully.');
- }
- // ...
- }
Thanks to autowiring and your type-hints in __construct()
, the container createsthe SiteUpdateManager
object and passes it the correct argument. In most cases,this works perfectly.
Manually Wiring Arguments
But there are a few cases when an argument to a service cannot be autowired. Forexample, suppose you want to make the admin email configurable:
- // src/Updates/SiteUpdateManager.php
- // ...
- class SiteUpdateManager
- {
- // ...
- + private $adminEmail;
- - public function __construct(MessageGenerator $messageGenerator, \Swift_Mailer $mailer)
- + public function __construct(MessageGenerator $messageGenerator, \Swift_Mailer $mailer, $adminEmail)
- {
- // ...
- + $this->adminEmail = $adminEmail;
- }
- public function notifyOfSiteUpdate()
- {
- // ...
- $message = \Swift_Message::newInstance()
- // ...
- - ->setTo('[email protected]')
- + ->setTo($this->adminEmail)
- // ...
- ;
- // ...
- }
- }
If you make this change and refresh, you'll see an error:
Cannot autowire service "AppUpdatesSiteUpdateManager": argument "$adminEmail"of method "__construct()" must have a type-hint or be given a value explicitly.
That makes sense! There is no way that the container knows what value you want topass here. No problem! In your configuration, you can explicitly set this argument:
- YAML
- # config/services.yaml
- services:
- # ...
- # same as before
- App\:
- resource: '../src/*'
- exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
- # explicitly configure the service
- App\Updates\SiteUpdateManager:
- arguments:
- $adminEmail: '[email protected]'
- XML
- <!-- config/services.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <services>
- <!-- ... -->
- <!-- Same as before -->
- <prototype namespace="App\" resource="../src/*" exclude="../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}"/>
- <!-- Explicitly configure the service -->
- <service id="App\Updates\SiteUpdateManager">
- <argument key="$adminEmail">[email protected]</argument>
- </service>
- </services>
- </container>
- PHP
- // config/services.php
- namespace Symfony\Component\DependencyInjection\Loader\Configurator;
- use App\Updates\SiteUpdateManager;
- return function(ContainerConfigurator $configurator) {
- // ...
- // same as before
- $services->load('App\\', '../src/*')
- ->exclude('../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}');
- $services->set(SiteUpdateManager::class)
- ->arg('$adminEmail', '[email protected]')
- ;
- };
Thanks to this, the container will pass manager@example.com
to the $adminEmail
argument of __construct
when creating the SiteUpdateManager
service. Theother arguments will still be autowired.
But, isn't this fragile? Fortunately, no! If you rename the $adminEmail
argumentto something else - e.g. $mainEmail
- you will get a clear exception when youreload the next page (even if that page doesn't use this service).
Service Parameters
In addition to holding service objects, the container also holds configuration,called parameters. The main article about Symfony configuration explains theconfiguration parameters in detail and showsall their types (string, boolean, array, binary and PHP constant parameters).
However, there is another type of parameter related to services. In YAML config,any string which starts with @
is considered as the ID of a service, insteadof a regular string. In XML config, use the type="service"
type for theparameter and in PHP config use the Reference
class:
- YAML
- # config/services.yaml
- services:
- App\Service\MessageGenerator:
- # this is not a string, but a reference to a service called 'logger'
- arguments: ['@logger']
- # if the value of a string parameter starts with '@', you need to escape
- # it by adding another '@' so Symfony doesn't consider it a service
- # (this will be parsed as the string '@securepassword')
- mailer_password: '@@securepassword'
- XML
- <!-- config/services.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <services>
- <service id="App\Service\MessageGenerator">
- <argument type="service" id="logger"/>
- </service>
- </services>
- </container>
- PHP
- // config/services.php
- namespace Symfony\Component\DependencyInjection\Loader\Configurator;
- use App\Service\MessageGenerator;
- return function(ContainerConfigurator $configurator) {
- $services = $configurator->services();
- $services->set(MessageGenerator::class)
- ->args([ref('logger')])
- ;
- };
Working with container parameters is straightforward using the container'saccessor methods for parameters:
- // checks if a parameter is defined (parameter names are case-sensitive)
- $container->hasParameter('mailer.transport');
- // gets value of a parameter
- $container->getParameter('mailer.transport');
- // adds a new parameter
- $container->setParameter('mailer.transport', 'sendmail');
Caution
The used .
notation is aSymfony convention to make parameterseasier to read. Parameters are flat key-value elements, they can'tbe organized into a nested array
Note
You can only set a parameter before the container is compiled, not at run-time.To learn more about compiling the container seeCompiling the Container.
Choose a Specific Service
The MessageGenerator
service created earlier requires a LoggerInterface
argument:
- // src/Service/MessageGenerator.php
- // ...
- use Psr\Log\LoggerInterface;
- class MessageGenerator
- {
- private $logger;
- public function __construct(LoggerInterface $logger)
- {
- $this->logger = $logger;
- }
- // ...
- }
However, there are multiple services in the container that implement LoggerInterface
,such as logger
, monolog.logger.request
, monolog.logger.php
, etc. Howdoes the container know which one to use?
In these situations, the container is usually configured to automatically chooseone of the services - logger
in this case (read more about why in Using Aliases to Enable Autowiring).But, you can control this and pass in a different logger:
- YAML
- # config/services.yaml
- services:
- # ... same code as before
- # explicitly configure the service
- App\Service\MessageGenerator:
- arguments:
- # the '@' symbol is important: that's what tells the container
- # you want to pass the *service* whose id is 'monolog.logger.request',
- # and not just the *string* 'monolog.logger.request'
- $logger: '@monolog.logger.request'
- XML
- <!-- config/services.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <services>
- <!-- ... same code as before -->
- <!-- Explicitly configure the service -->
- <service id="App\Service\MessageGenerator">
- <argument key="$logger" type="service" id="monolog.logger.request"/>
- </service>
- </services>
- </container>
- PHP
- // config/services.php
- namespace Symfony\Component\DependencyInjection\Loader\Configurator;
- use App\Service\MessageGenerator;
- return function(ContainerConfigurator $configurator) {
- // ... same code as before
- // explicitly configure the service
- $services->set(SiteUpdateManager::class)
- ->arg('$logger', ref('monolog.logger.request'))
- ;
- };
This tells the container that the $logger
argument to __construct
should useservice whose id is monolog.logger.request
.
For a full list of all possible services in the container, run:
- $ php bin/console debug:container
Binding Arguments by Name or Type
You can also use the bind
keyword to bind specific arguments by name or type:
- YAML
- # config/services.yaml
- services:
- _defaults:
- bind:
- # pass this value to any $adminEmail argument for any service
- # that's defined in this file (including controller arguments)
- $adminEmail: '[email protected]'
- # pass this service to any $requestLogger argument for any
- # service that's defined in this file
- $requestLogger: '@monolog.logger.request'
- # pass this service for any LoggerInterface type-hint for any
- # service that's defined in this file
- Psr\Log\LoggerInterface: '@monolog.logger.request'
- # optionally you can define both the name and type of the argument to match
- string $adminEmail: '[email protected]'
- Psr\Log\LoggerInterface $requestLogger: '@monolog.logger.request'
- # ...
- XML
- <!-- config/services.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <services>
- <defaults autowire="true" autoconfigure="true" public="false">
- <bind key="$adminEmail">[email protected]</bind>
- <bind key="$requestLogger"
- type="service"
- id="monolog.logger.request"
- />
- <bind key="Psr\Log\LoggerInterface"
- type="service"
- id="monolog.logger.request"
- />
- <!-- optionally you can define both the name and type of the argument to match -->
- <bind key="string $adminEmail">[email protected]</bind>
- <bind key="Psr\Log\LoggerInterface $requestLogger"
- type="service"
- id="monolog.logger.request"
- />
- </defaults>
- <!-- ... -->
- </services>
- </container>
- PHP
- // config/services.php
- namespace Symfony\Component\DependencyInjection\Loader\Configurator;
- use App\Controller\LuckyController;
- use Psr\Log\LoggerInterface;
- use Symfony\Component\DependencyInjection\Reference;
- return function(ContainerConfigurator $configurator) {
- $services = $configurator->services()
- ->defaults()
- // pass this value to any $adminEmail argument for any service
- // that's defined in this file (including controller arguments)
- ->bind('$adminEmail', '[email protected]')
- // pass this service to any $requestLogger argument for any
- // service that's defined in this file
- ->bind('$requestLogger', ref('monolog.logger.request'))
- // pass this service for any LoggerInterface type-hint for any
- // service that's defined in this file
- ->bind(LoggerInterface::class, ref('monolog.logger.request'))
- // optionally you can define both the name and type of the argument to match
- ->bind('string $adminEmail', '[email protected]')
- ->bind(LoggerInterface::class.' $requestLogger', ref('monolog.logger.request'))
- ;
- // ...
- };
By putting the bind
key under defaults
, you can specify the value of _any_argument for _any service defined in this file! You can bind arguments by name(e.g. $adminEmail
), by type (e.g. Psr\Log\LoggerInterface
) or both(e.g. Psr\Log\LoggerInterface $requestLogger
).
The bind
config can also be applied to specific services or when loading manyservices at once (i.e. Importing Many Services at once with resource).
The autowire Option
Above, the services.yaml
file has autowire: true
in the _defaults
sectionso that it applies to all services defined in that file. With this setting, you'reable to type-hint arguments in the __construct()
method of your services andthe container will automatically pass you the correct arguments. This entire entryhas been written around autowiring.
For more details about autowiring, check out Defining Services Dependencies Automatically (Autowiring).
The autoconfigure Option
Above, the services.yaml
file has autoconfigure: true
in the defaults
section so that it applies to all services defined in that file. With this setting,the container will automatically apply certain configuration to your services, basedon your service's _class. This is mostly used to auto-tag your services.
For example, to create a Twig extension, you need to create a class, register itas a service, and tag it with twig.extension
.
But, with autoconfigure: true
, you don't need the tag. In fact, if you're usingthe default services.yaml config,you don't need to do anything: the service will be automatically loaded. Then,autoconfigure
will add the twig.extension
tag for you, because your classimplements Twig\Extension\ExtensionInterface
. And thanks to autowire
, you can even addconstructor arguments without any configuration.
Public Versus Private Services
Thanks to the _defaults
section in services.yaml
, every service defined inthis file is public: false
by default.
What does this mean? When a service is public, you can access it directlyfrom the container object, which is accessible from any controller that extendsController
:
- use App\Service\MessageGenerator;
- // ...
- public function new()
- {
- // there IS a public "logger" service in the container
- $logger = $this->container->get('logger');
- // this will NOT work: MessageGenerator is a private service
- $generator = $this->container->get(MessageGenerator::class);
- }
As a best practice, you should only create private services, which will happenautomatically. And also, you should not use the $container->get()
method tofetch public services.
But, if you do need to make a service public, override the public
setting:
- YAML
- # config/services.yaml
- services:
- # ... same code as before
- # explicitly configure the service
- App\Service\MessageGenerator:
- public: true
- XML
- <!-- config/services.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <services>
- <!-- ... same code as before -->
- <!-- Explicitly configure the service -->
- <service id="App\Service\MessageGenerator" public="true"></service>
- </services>
- </container>
- PHP
- // config/services.php
- namespace Symfony\Component\DependencyInjection\Loader\Configurator;
- use App\Service\MessageGenerator;
- return function(ContainerConfigurator $configurator) {
- // ... same as code before
- // explicitly configure the service
- $services->set(MessageGenerator::class)
- ->public()
- ;
- };
Importing Many Services at once with resource
You've already seen that you can import many services at once by using the resource
key. For example, the default Symfony configuration contains this:
- YAML
- # config/services.yaml
- services:
- # ...
- # makes classes in src/ available to be used as services
- # this creates a service per class whose id is the fully-qualified class name
- App\:
- resource: '../src/*'
- exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
- XML
- <!-- config/services.xml -->
- <?xml version="1.0" encoding="UTF-8" ?>
- <container xmlns="http://symfony.com/schema/dic/services"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
- <services>
- <!-- ... -->
- <prototype namespace="App\" resource="../src/*" exclude="../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}"/>
- </services>
- </container>
- PHP
- // config/services.php
- namespace Symfony\Component\DependencyInjection\Loader\Configurator;
- return function(ContainerConfigurator $configurator) {
- // ...
- // makes classes in src/ available to be used as services
- // this creates a service per class whose id is the fully-qualified class name
- $services->load('App\\', '../src/*')
- ->exclude('../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}');
- };
Tip
The value of the resource
and exclude
options can be any validglob pattern).
This can be used to quickly make many classes available as services and apply somedefault configuration. The id
of each service is its fully-qualified class name.You can override any service that's imported by using its id (class name) below(e.g. see Manually Wiring Arguments). If you override a service, none ofthe options (e.g. public
) are inherited from the import (but the overriddenservice does still inherit from _defaults
).
You can also exclude
certain paths. This is optional, but will slightly increaseperformance in the dev
environment: excluded paths are not tracked and so modifyingthem will not cause the container to be rebuilt.
Note
Wait, does this mean that every class in src/
is registered asa service? Even model classes? Actually, no. As long as you havepublic: false
under your defaults
key (or you can add it under thespecific import), all the imported services are _private. Thanks to this, allclasses in src/
that are not explicitly used as services areautomatically removed from the final container. In reality, the importmeans that all classes are "available to be used as services" without needingto be manually configured.
Multiple Service Definitions Using the Same Namespace
If you define services using the YAML config format, the PHP namespace is usedas the key of each configuration, so you can't define different service configsfor classes under the same namespace:
- # config/services.yaml
- services:
- App\Domain\:
- resource: '../src/Domain/*'
- # ...
In order to have multiple definitions, add the namespace
option and use anyunique string as the key of each service config:
- # config/services.yaml
- services:
- command_handlers:
- namespace: App\Domain\
- resource: '../src/Domain/*/CommandHandler'
- tags: [command_handler]
- event_subscribers:
- namespace: App\Domain\
- resource: '../src/Domain/*/EventSubscriber'
- tags: [event_subscriber]
Explicitly Configuring Services and Arguments
Prior to Symfony 3.3, all services and (typically) arguments were explicitly configured:it was not possible to load services automaticallyand autowiring was much less common.
Both of these features are optional. And even if you use them, there may be somecases where you want to manually wire a service. For example, suppose that you wantto register 2 services for the SiteUpdateManager
class - each with a differentadmin email. In this case, each needs to have a unique service id:
- YAML
- # config/services.yaml
- services:
- # ...
- # this is the service's id
- site_update_manager.superadmin:
- class: App\Updates\SiteUpdateManager
- # you CAN still use autowiring: we just want to show what it looks like without
- autowire: false
- # manually wire all arguments
- arguments:
- - '@App\Service\MessageGenerator'
- - '@mailer'
- - '[email protected]'
- site_update_manager.normal_users:
- class: App\Updates\SiteUpdateManager
- autowire: false
- arguments:
- - '@App\Service\MessageGenerator'
- - '@mailer'
- - '[email protected]'
- # Create an alias, so that - by default - if you type-hint SiteUpdateManager,
- # the site_update_manager.superadmin will be used
- App\Updates\SiteUpdateManager: '@site_update_manager.superadmin'
- XML
<!-- config/services.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <!-- ... --> <service id="site_update_manager.superadmin" class="App\Updates\SiteUpdateManager" autowire="false"> <argument type="service" id="App\Service\MessageGenerator"/> <argument type="service" id="mailer"/> <argument>[email protected]</argument> </service> <service id="site_update_manager.normal_users" class="App\Updates\SiteUpdateManager" autowire="false"> <argument type="service" id="App\Service\MessageGenerator"/> <argument type="service" id="mailer"/> <argument>[email protected]</argument> </service> <service id="App\Updates\SiteUpdateManager" alias="site_update_manager.superadmin"/> </services> </container>
- PHP
// config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; use App\Service\MessageGenerator; use App\Updates\SiteUpdateManager; return function(ContainerConfigurator $configurator) { // ... // site_update_manager.superadmin is the service's id $services->set('site_update_manager.superadmin', SiteUpdateManager::class) // you CAN still use autowiring: we just want to show what it looks like without ->autowire(false) // manually wire all arguments ->args([ ref(MessageGenerator::class), ref('mailer'), '[email protected]', ]); $services->set('site_update_manager.normal_users', SiteUpdateManager::class) ->autowire(false) ->args([ ref(MessageGenerator::class), ref('mailer'), '[email protected]', ]); // Create an alias, so that - by default - if you type-hint SiteUpdateManager, // the site_update_manager.superadmin will be used $services->alias(SiteUpdateManager::class, 'site_update_manager.superadmin'); };
In this case, two services are registered: site_update_manager.superadmin
and site_update_manager.normal_users
. Thanks to the alias, if you type-hintSiteUpdateManager
the first (site_update_manager.superadmin
) will be passed.If you want to pass the second, you'll need to manually wire the service.
Caution
If you do not create the alias and are loading all services from src/,then three services have been created (the automatic service + your two services)and the automatically loaded service will be passed - by default - when you type-hintSiteUpdateManager
. That's why creating the alias is a good idea.
Learn more
- The Symfony 3.3 DI Container Changes Explained (autowiring, _defaults, etc)
- How to Create Service Aliases and Mark Services as Private
- Defining Services Dependencies Automatically (Autowiring)
- Service Method Calls and Setter Injection
- How to Work with Compiler Passes
- How to Configure a Service with a Configurator
- How to Debug the Service Container & List Services
- How to work with Service Definition Objects
- How to Inject Values Based on Complex Expressions
- Using a Factory to Create Services
- How to Import Configuration Files/Resources
- Types of Injection
- Lazy Services
- How to Make Service Arguments/References Optional
- How to Manage Common Dependencies with Parent Services
- How to Retrieve the Request from the Service Container
- How to Decorate Services
- Service Subscribers & Locators
- How to Define Non Shared Services
- How to Inject Instances into the Container
- How to Work with Service Tags