From a54ffed0090523c34f7ca3aa2401e8699e19341b Mon Sep 17 00:00:00 2001 From: Wouter J Date: Mon, 31 Mar 2014 20:47:36 +0200 Subject: [PATCH 1/2] Made code readable --- .../dependency_injection/parentservices.rst | 299 +++++++++--------- 1 file changed, 150 insertions(+), 149 deletions(-) diff --git a/components/dependency_injection/parentservices.rst b/components/dependency_injection/parentservices.rst index e5a44e2a30e..be10307b552 100644 --- a/components/dependency_injection/parentservices.rst +++ b/components/dependency_injection/parentservices.rst @@ -56,11 +56,14 @@ The service config for these classes would look something like this: # ... newsletter_manager.class: NewsletterManager greeting_card_manager.class: GreetingCardManager + services: my_mailer: # ... + my_email_formatter: # ... + newsletter_manager: class: "%newsletter_manager.class%" calls: @@ -82,12 +85,14 @@ The service config for these classes would look something like this: - + - + + + @@ -96,10 +101,12 @@ The service config for these classes would look something like this: + + @@ -108,29 +115,34 @@ The service config for these classes would look something like this: .. code-block:: php - use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; // ... $container->setParameter('newsletter_manager.class', 'NewsletterManager'); $container->setParameter('greeting_card_manager.class', 'GreetingCardManager'); - $container->setDefinition('my_mailer', ...); - $container->setDefinition('my_email_formatter', ...); - $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%' - ))->addMethodCall('setMailer', array( - new Reference('my_mailer') - ))->addMethodCall('setEmailFormatter', array( - new Reference('my_email_formatter') - )); - $container->setDefinition('greeting_card_manager', new Definition( - '%greeting_card_manager.class%' - ))->addMethodCall('setMailer', array( - new Reference('my_mailer') - ))->addMethodCall('setEmailFormatter', array( - new Reference('my_email_formatter') - )); + $container->register('my_mailer', ...); + $container->register('my_email_formatter', ...); + + $container + ->register('newsletter_manager', '%newsletter_manager.class%') + ->addMethodCall('setMailer', array( + new Reference('my_mailer'), + )) + ->addMethodCall('setEmailFormatter', array( + new Reference('my_email_formatter'), + )) + ; + + $container + ->register('greeting_card_manager', '%greeting_card_manager.class%') + ->addMethodCall('setMailer', array( + new Reference('my_mailer'), + )) + ->addMethodCall('setEmailFormatter', array( + new Reference('my_email_formatter'), + )) + ; There is a lot of repetition in both the classes and the configuration. This means that if you changed, for example, the ``Mailer`` of ``EmailFormatter`` @@ -180,15 +192,9 @@ a parent for a service. .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - greeting_card_manager.class: GreetingCardManager + # ... services: - my_mailer: - # ... - my_email_formatter: - # ... + # ... mail_manager: abstract: true calls: @@ -200,34 +206,33 @@ a parent for a service. parent: mail_manager greeting_card_manager: - class: "%greeting_card_manager.class%" + class: "%greeting_card_manager.class%" parent: mail_manager .. code-block:: xml - - - NewsletterManager - GreetingCardManager - - + - - - - - - + + - - + + + + .. code-block:: php @@ -237,29 +242,26 @@ a parent for a service. use Symfony\Component\DependencyInjection\Reference; // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('greeting_card_manager.class', 'GreetingCardManager'); - - $container->setDefinition('my_mailer', ...); - $container->setDefinition('my_email_formatter', ...); - $container->setDefinition('mail_manager', new Definition( - ))->setAbstract( - true - )->addMethodCall('setMailer', array( - new Reference('my_mailer') - ))->addMethodCall('setEmailFormatter', array( - new Reference('my_email_formatter') - )); - $container->setDefinition('newsletter_manager', new DefinitionDecorator( - 'mail_manager' - ))->setClass( - '%newsletter_manager.class%' - ); - $container->setDefinition('greeting_card_manager', new DefinitionDecorator( - 'mail_manager' - ))->setClass( - '%greeting_card_manager.class%' - ); + + $mailManager = new Definition(); + $mailManager + ->setAbstract(true); + ->addMethodCall('setMailer', array( + new Reference('my_mailer'), + )) + ->addMethodCall('setEmailFormatter', array( + new Reference('my_email_formatter'), + )) + ; + $container->setDefinition('mail_manager', $mailManager); + + $newsletterManager = new DefinitionDecorator('mail_manager'); + $newsletterManager->setClass('%newsletter_manager.class%'); + $container->setDefinition('newsletter_manager', $newsletterManager); + + $greetingCardManager = new DefinitionDecorator('mail_manager'); + $greetingCardManager->setClass('%greeting_card_manager.class%'); + $container->setDefinition('greeting_card_manager', $greetingCardManager); In this context, having a ``parent`` service implies that the arguments and method calls of the parent service should be used for the child services. @@ -303,19 +305,14 @@ to the ``NewsletterManager`` class, the config would look like this: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - greeting_card_manager.class: GreetingCardManager + # ... services: - my_mailer: - # ... + # ... my_alternative_mailer: # ... - my_email_formatter: - # ... + mail_manager: - abstract: true + abstract: true calls: - [setMailer, ["@my_mailer"]] - [setEmailFormatter, ["@my_email_formatter"]] @@ -332,36 +329,37 @@ to the ``NewsletterManager`` class, the config would look like this: .. code-block:: xml - - - NewsletterManager - GreetingCardManager - + - - - - - - - + + + + - + + - + + .. code-block:: php @@ -371,32 +369,31 @@ to the ``NewsletterManager`` class, the config would look like this: use Symfony\Component\DependencyInjection\Reference; // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('greeting_card_manager.class', 'GreetingCardManager'); - - $container->setDefinition('my_mailer', ...); $container->setDefinition('my_alternative_mailer', ...); - $container->setDefinition('my_email_formatter', ...); - $container->setDefinition('mail_manager', new Definition( - ))->setAbstract( - true - )->addMethodCall('setMailer', array( - new Reference('my_mailer') - ))->addMethodCall('setEmailFormatter', array( - new Reference('my_email_formatter') - )); - $container->setDefinition('newsletter_manager', new DefinitionDecorator( - 'mail_manager' - ))->setClass( - '%newsletter_manager.class%' - )->addMethodCall('setMailer', array( - new Reference('my_alternative_mailer') - )); - $container->setDefinition('greeting_card_manager', new DefinitionDecorator( - 'mail_manager' - ))->setClass( - '%greeting_card_manager.class%' - ); + + $mailManager = new Definition(); + $mailManager + ->setAbstract(true); + ->addMethodCall('setMailer', array( + new Reference('my_mailer'), + )) + ->addMethodCall('setEmailFormatter', array( + new Reference('my_email_formatter'), + )) + ; + $container->setDefinition('mail_manager', $mailManager); + + $newsletterManager = new DefinitionDecorator('mail_manager'); + $newsletterManager->setClass('%newsletter_manager.class%'); + ->addMethodCall('setMailer', array( + new Reference('my_alternative_mailer'), + )) + ; + $container->setDefinition('newsletter_manager', $newsletterManager); + + $greetingCardManager = new DefinitionDecorator('mail_manager'); + $greetingCardManager->setClass('%greeting_card_manager.class%'); + $container->setDefinition('greeting_card_manager', $greetingCardManager); The ``GreetingCardManager`` will receive the same dependencies as before, but the ``NewsletterManager`` will be passed the ``my_alternative_mailer`` @@ -419,7 +416,7 @@ class looks like this:: { protected $filters; - public function setFilter($filter) + public function addFilter($filter) { $this->filters[] = $filter; } @@ -433,46 +430,48 @@ If you had the following config: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager + # ... services: my_filter: # ... another_filter: # ... + mail_manager: - abstract: true + abstract: true calls: - - [setFilter, ["@my_filter"]] + - [addFilter, ["@my_filter"]] newsletter_manager: - class: "%newsletter_manager.class%" + class: "%newsletter_manager.class%" parent: mail_manager calls: - - [setFilter, ["@another_filter"]] + - [addFilter, ["@another_filter"]] .. code-block:: xml - - - NewsletterManager - + - + - + + + - + - - + + + @@ -485,31 +484,33 @@ If you had the following config: use Symfony\Component\DependencyInjection\Reference; // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('mail_manager.class', 'MailManager'); - $container->setDefinition('my_filter', ...); $container->setDefinition('another_filter', ...); - $container->setDefinition('mail_manager', new Definition( - ))->setAbstract( - true - )->addMethodCall('setFilter', array( - new Reference('my_filter') - )); - $container->setDefinition('newsletter_manager', new DefinitionDecorator( - 'mail_manager' - ))->setClass( - '%newsletter_manager.class%' - )->addMethodCall('setFilter', array( - new Reference('another_filter') - )); - -In this example, the ``setFilter`` of the ``newsletter_manager`` service + + $mailManager = new Definition(); + $mailManager + ->setAbstract(true) + ->addMethodCall('addFilter', array( + new Reference('my_filter'), + )) + ; + $container->setDefinition('mail_manager', $mailManager); + + $newsletterManager = new DefinitionDecorator('mail_manager'); + $newsletterManager + ->setClass('%newsletter_manager.class%') + ->addMethodCall('addFilter', array( + new Reference('another_filter'), + )) + ; + $container->setDefinition('newsletter_manager', $newsletterManager); + +In this example, the ``addFilter`` method of the ``newsletter_manager`` service will be called twice, resulting in the ``$filters`` array containing both ``my_filter`` and ``another_filter`` objects. This is great if you just want to add additional filters to the subclasses. If you want to replace the filters passed to the subclass, removing the parent setting from the config will -prevent the base class from calling ``setFilter``. +prevent the base class from calling ``addFilter``. .. tip:: From 0535e188a086821889ee67ff431a559e489b18b9 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Thu, 3 Apr 2014 22:09:02 +0200 Subject: [PATCH 2/2] Reworded article a bit --- .../dependency_injection/parentservices.rst | 134 ++---------------- 1 file changed, 15 insertions(+), 119 deletions(-) diff --git a/components/dependency_injection/parentservices.rst b/components/dependency_injection/parentservices.rst index be10307b552..530c078fe27 100644 --- a/components/dependency_injection/parentservices.rst +++ b/components/dependency_injection/parentservices.rst @@ -291,6 +291,13 @@ would cause an exception to be raised for a non-abstract service. In order for parent dependencies to resolve, the ``ContainerBuilder`` must first be compiled. See :doc:`/components/dependency_injection/compilation` for more details. + +.. tip:: + + In the examples shown, the classes sharing the same configuration also extend + from the same parent in PHP. This does not need to be the case though, you can + extract common parts of similar service definitions into a parent service without + also extending a parent class in PHP. Overriding Parent Dependencies ------------------------------ @@ -399,123 +406,12 @@ The ``GreetingCardManager`` will receive the same dependencies as before, but the ``NewsletterManager`` will be passed the ``my_alternative_mailer`` instead of the ``my_mailer`` service. -Collections of Dependencies ---------------------------- - -It should be noted that the overridden setter method in the previous example -is actually called twice - once per the parent definition and once per the -child definition. In the previous example, that was fine, since the second -``setMailer`` call replaces mailer object set by the first call. - -In some cases, however, this can be a problem. For example, if the overridden -method call involves adding something to a collection, then two objects will -be added to that collection. The following shows such a case, if the parent -class looks like this:: - - abstract class MailManager - { - protected $filters; - - public function addFilter($filter) - { - $this->filters[] = $filter; - } - - // ... - } - -If you had the following config: - -.. configuration-block:: - - .. code-block:: yaml - - # ... - services: - my_filter: - # ... - another_filter: - # ... - - mail_manager: - abstract: true - calls: - - [addFilter, ["@my_filter"]] - - newsletter_manager: - class: "%newsletter_manager.class%" - parent: mail_manager - calls: - - [addFilter, ["@another_filter"]] - - .. code-block:: xml - - - - - - - - - - - - - - - - - - - - - - - - - - .. code-block:: php - - use Symfony\Component\DependencyInjection\Definition; - use Symfony\Component\DependencyInjection\DefinitionDecorator; - use Symfony\Component\DependencyInjection\Reference; - - // ... - $container->setDefinition('my_filter', ...); - $container->setDefinition('another_filter', ...); - - $mailManager = new Definition(); - $mailManager - ->setAbstract(true) - ->addMethodCall('addFilter', array( - new Reference('my_filter'), - )) - ; - $container->setDefinition('mail_manager', $mailManager); - - $newsletterManager = new DefinitionDecorator('mail_manager'); - $newsletterManager - ->setClass('%newsletter_manager.class%') - ->addMethodCall('addFilter', array( - new Reference('another_filter'), - )) - ; - $container->setDefinition('newsletter_manager', $newsletterManager); - -In this example, the ``addFilter`` method of the ``newsletter_manager`` service -will be called twice, resulting in the ``$filters`` array containing both -``my_filter`` and ``another_filter`` objects. This is great if you just want -to add additional filters to the subclasses. If you want to replace the filters -passed to the subclass, removing the parent setting from the config will -prevent the base class from calling ``addFilter``. - -.. tip:: +.. caution:: - In the examples shown there is a similar relationship between the parent - and child services and the underlying parent and child classes. This does - not need to be the case though, you can extract common parts of similar - service definitions into a parent service without also inheriting a parent - class. + You can't override method calls. When you defined new method calls in the child + service, it'll be added to the current set of configured method calls. This means + it works perfectly when the setter overrides the current property, but it doesn't + work as expected when the setter appends it to the existing data (e.g. an + ``addFilters()`` method). + In those cases, the only solution is to *not* extend the parent service and configuring + the service just like you did before knowing this feature.