diff --git a/components/dependency_injection/index.rst b/components/dependency_injection/index.rst index ac0e9d2a7da..20e26e0263e 100644 --- a/components/dependency_injection/index.rst +++ b/components/dependency_injection/index.rst @@ -7,6 +7,7 @@ introduction definitions compilation + tags factories parentservices diff --git a/cookbook/service_container/tags.rst b/components/dependency_injection/tags.rst similarity index 54% rename from cookbook/service_container/tags.rst rename to components/dependency_injection/tags.rst index 510728bc6a0..708757f0797 100644 --- a/cookbook/service_container/tags.rst +++ b/components/dependency_injection/tags.rst @@ -1,33 +1,33 @@ -.. index:: +.. index:: single: Service Container; Tags How to make your Services use Tags ================================== -Several of Symfony2's core services depend on tags to recognize which services -should be loaded, notified of events, or handled in some other special way. -For example, Twig uses the tag ``twig.extension`` to load extra extensions. +You can ask a container builder for any services that were tagged when registered +with it. This is useful in compiler passes where you can find services which +were registered in config files you do not have control over and make use +of them. This is useful if your service handles a collection of some kind, +or implements a "chain", in which several alternative strategies are tried +until one of them is successful. You can then allow services to be registered +with a tag and add these services to your collection or chain. -But you can also use tags in your own bundles. For example in case your service -handles a collection of some kind, or implements a "chain", in which several alternative -strategies are tried until one of them is successful. In this article I will use the example +For example, if you are using Swift Mailer and want to implement a of a "transport chain", which is a collection of classes implementing ``\Swift_Transport``. -Using the chain, the Swift mailer may try several ways of transport, until one succeeds. -This post focuses mainly on the dependency injection part of the story. +Using the chain, Swift Mailer can try several ways of transport, until one +succeeds. To begin with, define the ``TransportChain`` class:: - namespace Acme\MailerBundle; - class TransportChain { private $transports; - + public function __construct() { $this->transports = array(); } - + public function addTransport(\Swift_Transport $transport) { $this->transports[] = $transport; @@ -40,33 +40,29 @@ Then, define the chain as a service: .. code-block:: yaml - # src/Acme/MailerBundle/Resources/config/services.yml parameters: - acme_mailer.transport_chain.class: Acme\MailerBundle\TransportChain - + acme_mailer.transport_chain.class: TransportChain + services: acme_mailer.transport_chain: class: %acme_mailer.transport_chain.class% .. code-block:: xml - - - Acme\MailerBundle\TransportChain + TransportChain - + - + .. code-block:: php - - // src/Acme/MailerBundle/Resources/config/services.php + use Symfony\Component\DependencyInjection\Definition; - - $container->setParameter('acme_mailer.transport_chain.class', 'Acme\MailerBundle\TransportChain'); - + + $container->setParameter('acme_mailer.transport_chain.class', 'TransportChain'); + $container->setDefinition('acme_mailer.transport_chain', new Definition('%acme_mailer.transport_chain.class%')); Define Services with a Custom Tag @@ -80,7 +76,6 @@ As an example we add the following transports as services: .. code-block:: yaml - # src/Acme/MailerBundle/Resources/config/services.yml services: acme_mailer.transport.smtp: class: \Swift_SmtpTransport @@ -92,67 +87,42 @@ As an example we add the following transports as services: class: \Swift_SendmailTransport tags: - { name: acme_mailer.transport } - + .. code-block:: xml - %mailer_host% - + - + .. code-block:: php - - // src/Acme/MailerBundle/Resources/config/services.php + use Symfony\Component\DependencyInjection\Definition; - + $definitionSmtp = new Definition('\Swift_SmtpTransport', array('%mailer_host%')); $definitionSmtp->addTag('acme_mailer.transport'); $container->setDefinition('acme_mailer.transport.smtp', $definitionSmtp); - + $definitionSendmail = new Definition('\Swift_SendmailTransport'); $definitionSendmail->addTag('acme_mailer.transport'); $container->setDefinition('acme_mailer.transport.sendmail', $definitionSendmail); -Notice the tags named "acme_mailer.transport". We want the bundle to recognize -these transports and add them to the chain all by itself. In order to achieve -this, we need to add a ``build()`` method to the ``AcmeMailerBundle`` class:: - - namespace Acme\MailerBundle; - - use Symfony\Component\HttpKernel\Bundle\Bundle; - use Symfony\Component\DependencyInjection\ContainerBuilder; - - use Acme\MailerBundle\DependencyInjection\Compiler\TransportCompilerPass; - - class AcmeMailerBundle extends Bundle - { - public function build(ContainerBuilder $container) - { - parent::build($container); - - $container->addCompilerPass(new TransportCompilerPass()); - } - } +Notice the tags named "acme_mailer.transport". This is the custom tag to use ion your compiler pass:: Create a ``CompilerPass`` ------------------------- -You will have spotted a reference to the not yet existing ``TransportCompilerPass`` class. -This class will make sure that all services with a tag ``acme_mailer.transport`` -will be added to the ``TransportChain`` class by calling the ``addTransport()`` -method. The ``TransportCompilerPass`` should look like this:: +Your compiler pass can then ask the container for any services with the +custom tag:: - namespace Acme\MailerBundle\DependencyInjection\Compiler; - use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Reference; - + class TransportCompilerPass implements CompilerPassInterface { public function process(ContainerBuilder $container) @@ -160,9 +130,9 @@ method. The ``TransportCompilerPass`` should look like this:: if (false === $container->hasDefinition('acme_mailer.transport_chain')) { return; } - + $definition = $container->getDefinition('acme_mailer.transport_chain'); - + foreach ($container->findTaggedServiceIds('acme_mailer.transport') as $id => $attributes) { $definition->addMethodCall('addTransport', array(new Reference($id))); } @@ -176,26 +146,13 @@ to the definition of the ``acme_mailer.transport_chain`` service a call to The first argument of each of these calls will be the mailer transport service itself. -.. note:: - - By convention, tag names consist of the name of the bundle (lowercase, - underscores as separators), followed by a dot, and finally the "real" - name, so the tag "transport" in the AcmeMailerBundle should be: ``acme_mailer.transport``. +Register the pass with the container +------------------------------------ -The Compiled Service Definition -------------------------------- +You also need to register the pass with the container, it will then be +run when the container is compiled. -Adding the compiler pass will result in the automatic generation of the following -lines of code in the compiled service container. In case you are working -in the "dev" environment, open the file ``/cache/dev/appDevDebugProjectContainer.php`` -and look for the method ``getTransportChainService()``. It should look like this:: - - protected function getAcmeMailer_TransportChainService() - { - $this->services['acme_mailer.transport_chain'] = $instance = new \Acme\MailerBundle\TransportChain(); - - $instance->addTransport($this->get('acme_mailer.transport.smtp')); - $instance->addTransport($this->get('acme_mailer.transport.sendmail')); + use Symfony\Component\DependencyInjection\ContainerBuilder; - return $instance; - } + $container = new ContainerBuilder(); + $container->addCompilerPass(new TransportCompilerPass); diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 64c9d03358b..0bc2001bb88 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -92,7 +92,7 @@ * :doc:`/cookbook/service_container/event_listener` * :doc:`/cookbook/service_container/scopes` - * :doc:`/cookbook/service_container/tags` + * :doc:`/cookbook/service_container/compiler_passes` * :doc:`/cookbook/security/index` diff --git a/cookbook/service_container/compiler_passes.rst b/cookbook/service_container/compiler_passes.rst new file mode 100644 index 00000000000..7182d1953d9 --- /dev/null +++ b/cookbook/service_container/compiler_passes.rst @@ -0,0 +1,32 @@ +How to work with Compiler Passes in Bundles +=========================================== + +Compiler passes give you an opportunity to manipulate other service +definitions that have been registered with the service container. You +can read about how to create them in the components section ":doc:`/components/dependency_injection/compilation`". +To register a compiler pass from a bundle you need to add it to the build +method of the bundle definition class:: + + namespace Acme\MailerBundle; + + use Symfony\Component\HttpKernel\Bundle\Bundle; + use Symfony\Component\DependencyInjection\ContainerBuilder; + + use Acme\MailerBundle\DependencyInjection\Compiler\CustomCompilerPass; + + class AcmeMailerBundle extends Bundle + { + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new CustomCompilerPass()); + } + } + +One of the common tasks for compiler passes is to work with tagged services, +read more about this in the components section ":doc:`/components/dependency_injection/tags`". +If you are using custom tags in a bundle then by convention, tag names consist +of the name of the bundle (lowercase, underscores as separators), followed +by a dot, and finally the "real" name, so the tag "transport" in the AcmeMailerBundle +should be: ``acme_mailer.transport``. diff --git a/cookbook/service_container/index.rst b/cookbook/service_container/index.rst index 3b0a82ad06e..be8ad17868b 100644 --- a/cookbook/service_container/index.rst +++ b/cookbook/service_container/index.rst @@ -6,4 +6,4 @@ Service Container event_listener scopes - tags + compiler_passes diff --git a/redirection_map b/redirection_map index 814b30c3eca..3e979ddb236 100644 --- a/redirection_map +++ b/redirection_map @@ -9,6 +9,7 @@ /cookbook/tools/finder /components/finder /cookbook/service_container/parentservices /components/dependency_injection/parentservices /cookbook/service_container/factories /components/dependency_injection/factories +/cookbook/service_container/tags /components/dependency_injection/tags /reference/configuration/mongodb /bundles/DoctrineMongoDBBundle/config /reference/YAML /components/yaml /components/dependency_injection /components/dependency_injection/introduction