@@ -564,3 +564,242 @@ application handlers.
564564 ->addTag('app.handler', ['priority' => 20]);
565565
566566 Note that any other custom attributes will be ignored by this feature.
567+
568+
569+ Tagged Services Collection with Index
570+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
571+
572+ What if you want to retrieve a specific service within the injected collection previously?
573+ The solution is to use the ``index_by `` and ``default_index_method `` options of the argument
574+ in combination with ``!tagged ``.
575+
576+ In the following example, all services tagged with ``app.handler `` are passed as
577+ first constructor argument to the ``App\Handler\HandlerCollection `` service, like previously,
578+ but we can now access to a specific injected service :
579+
580+ .. configuration-block ::
581+
582+ .. code-block :: yaml
583+
584+ # config/services.yaml
585+ services :
586+ App\Handler\One :
587+ tags :
588+ - { name: 'app.handler', index_attribute_name: 'handler_one' }
589+
590+ App\Handler\Two :
591+ tags :
592+ - { name: 'app.handler', index_attribute_name: 'handler_two' }
593+
594+ App\HandlerCollection :
595+ # inject all services tagged with app.handler as first argument
596+ arguments : [!tagged { tag: 'app.handler', index_by: 'index_attribute_name' }]
597+
598+ .. code-block :: xml
599+
600+ <!-- config/services.xml -->
601+ <?xml version =" 1.0" encoding =" UTF-8" ?>
602+ <container xmlns =" http://symfony.com/schema/dic/services"
603+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
604+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
605+ http://symfony.com/schema/dic/services/services-1.0.xsd" >
606+
607+ <services >
608+ <service id =" App\Handler\One" >
609+ <tag name =" app.handler" index_attribute_name =" handler_one" />
610+ </service >
611+
612+ <service id =" App\Handler\Two" >
613+ <tag name =" app.handler" index_attribute_name =" handler_two" />
614+ </service >
615+
616+ <service id =" App\HandlerCollection" >
617+ <!-- inject all services tagged with app.handler as first argument -->
618+ <argument type =" tagged" tag =" app.handler" index-by =" index_attribute_name" />
619+ </service >
620+ </services >
621+ </container >
622+
623+ .. code-block :: php
624+
625+ // config/services.php
626+ use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
627+
628+ $container->register(App\Handler\One::class)
629+ ->addTag('app.handler', ['index_attribute_name' => 'handler_one']);
630+
631+ $container->register(App\Handler\Two::class)
632+ ->addTag('app.handler', ['index_attribute_name' => 'handler_two']);
633+
634+ $container->register(App\Handler\HandlerCollection::class)
635+ // inject all services tagged with app.handler as first argument
636+ ->addArgument(new TaggedIteratorArgument('app.handler', 'index_attribute_name'));
637+
638+ After compilation the ``HandlerCollection `` service is able to iterate over your
639+ application handlers. But we want to retrieve a specific service by it's
640+ ``index_attribute_name ``. As it's an iterator, we can use ``iterator_to_array ``
641+ to get an array and then retrieve the ``handler_two `` handler :
642+
643+ .. code-block :: php
644+
645+ // src/Handler/HandlerCollection.php
646+ namespace App\Handler;
647+
648+ class HandlerCollection
649+ {
650+ public function __construct(iterable $handlers)
651+ {
652+ $handlers = iterator_to_array($handlers);
653+
654+ $handler_two = $handlers['handler_two']:
655+ $handler_one = $handlers['handler_one'];
656+ }
657+ }
658+
659+
660+ .. tip ::
661+
662+ You can omit the ``index_attribute_name `` attribute, by implementing a static
663+ method named like this : ``getDefaultIndexAttributeNameName ``
664+
665+ .. configuration-block ::
666+
667+ .. code-block :: yaml
668+
669+ # config/services.yaml
670+ services :
671+ App\Handler\One :
672+ tags :
673+ - { name: 'app.handler', priority: 20 }
674+
675+ App\Handler\Two :
676+ tags :
677+ - { name: 'app.handler', index: 'handler_two' }
678+
679+ App\HandlerCollection :
680+ # inject all services tagged with app.handler as first argument
681+ arguments : [!tagged { tag: 'app.handler', index_by: 'index' }]
682+
683+ .. code-block :: xml
684+
685+ <!-- config/services.xml -->
686+ <?xml version =" 1.0" encoding =" UTF-8" ?>
687+ <container xmlns =" http://symfony.com/schema/dic/services"
688+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
689+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
690+ http://symfony.com/schema/dic/services/services-1.0.xsd" >
691+
692+ <services >
693+ <service id =" App\Handler\One" >
694+ <tag name =" app.handler" priority =" 20" />
695+ </service >
696+
697+ <service id =" App\Handler\Two" >
698+ <tag name =" app.handler" index =" handler_two" />
699+ </service >
700+
701+ <service id =" App\HandlerCollection" >
702+ <!-- inject all services tagged with app.handler as first argument -->
703+ <argument type =" tagged" tag =" app.handler" index-by =" index" />
704+ </service >
705+ </services >
706+ </container >
707+
708+ .. code-block :: php
709+
710+ // config/services.php
711+ $container->register(App\Handler\One::class)
712+ ->addTag('app.handler', ['priority' => 20]);
713+
714+ $container->register(App\Handler\One::class, ['index' => 'handler_two']);
715+
716+ $container->register(App\HandlerCollection::class)
717+ // inject all services tagged with app.handler as first argument
718+ ->addArgument(new TaggedIteratorArgument('app.handler', 'index'));
719+
720+ And here is how your ``App\Handler\One `` should now be :
721+
722+ .. code-block :: php
723+
724+ // src/Handler/One.php
725+ namespace App\Handler;
726+
727+ class One
728+ {
729+ public static function getDefaultIndexName() {
730+ return 'handler_one';
731+ }
732+ }
733+
734+ You also can define the name of the static method to implement on each service with the ``default_index_method `` attribute on the argument.
735+
736+ .. configuration-block ::
737+
738+ .. code-block :: yaml
739+
740+ # config/services.yaml
741+ services :
742+ App\Handler\One :
743+ tags :
744+ - { name: 'app.handler', priority: 20 }
745+
746+ App\Handler\Two :
747+ tags :
748+ - { name: 'app.handler', index: 'handler_two' }
749+
750+ App\HandlerCollection :
751+ # inject all services tagged with app.handler as first argument
752+ arguments : [!tagged { tag: 'app.handler', index_by: 'index', default_index_method: 'someFunctionName' }]
753+
754+ .. code-block :: xml
755+
756+ <!-- config/services.xml -->
757+ <?xml version =" 1.0" encoding =" UTF-8" ?>
758+ <container xmlns =" http://symfony.com/schema/dic/services"
759+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
760+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
761+ http://symfony.com/schema/dic/services/services-1.0.xsd" >
762+
763+ <services >
764+ <service id =" App\Handler\One" >
765+ <tag name =" app.handler" priority =" 20" />
766+ </service >
767+
768+ <service id =" App\Handler\Two" >
769+ <tag name =" app.handler" index =" handler_two" />
770+ </service >
771+
772+ <service id =" App\HandlerCollection" >
773+ <!-- inject all services tagged with app.handler as first argument -->
774+ <argument type =" tagged" tag =" app.handler" index-by =" index" default-index-method =" someFunctionName" />
775+ </service >
776+ </services >
777+ </container >
778+
779+ .. code-block :: php
780+
781+ // config/services.php
782+ $container->register(App\Handler\One::class)
783+ ->addTag('app.handler', ['priority' => 20]);
784+
785+ $container->register(App\Handler\One::class, ['index' => 'handler_two']);
786+
787+ $container->register(App\HandlerCollection::class)
788+ // inject all services tagged with app.handler as first argument
789+ ->addArgument(new TaggedIteratorArgument('app.handler', 'index', 'someFunctionName'));
790+
791+ And here is how your ``App\Handler\One `` should look like :
792+
793+ .. code-block :: php
794+
795+ // src/Handler/One.php
796+ namespace App\Handler;
797+
798+ class One
799+ {
800+ public static function someFunctionName()
801+ {
802+ return 'handler_one';
803+ }
804+ }
805+
0 commit comments