@@ -392,7 +392,150 @@ will share identical locators amongst all the services referencing them::
392392 $myService->addArgument(ServiceLocatorTagPass::register($container, $locateableServices));
393393 }
394394
395- .. _`Command pattern` : https://en.wikipedia.org/wiki/Command_pattern
395+ Indexing the Collection of Services
396+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
397+
398+ Services passed to the service locator can define their own index using an
399+ arbitrary attribute whose name is defined as ``index_by `` in the service locator.
400+
401+ In the following example, the ``App\Handler\HandlerCollection `` locator receives
402+ all services tagged with ``app.handler `` and they are indexed using the value
403+ of the ``key `` tag attribute (as defined in the ``index_by `` locator option):
404+
405+ .. configuration-block ::
406+
407+ .. code-block :: yaml
408+
409+ # config/services.yaml
410+ services :
411+ App\Handler\One :
412+ tags :
413+ - { name: 'app.handler', key: 'handler_one' }
414+
415+ App\Handler\Two :
416+ tags :
417+ - { name: 'app.handler', key: 'handler_two' }
418+
419+ App\HandlerCollection :
420+ # inject all services tagged with app.handler as first argument
421+ arguments : [!tagged_locator { tag: 'app.handler', index_by: 'key' }]
422+
423+ .. code-block :: xml
424+
425+ <!-- config/services.xml -->
426+ <?xml version =" 1.0" encoding =" UTF-8" ?>
427+ <container xmlns =" http://symfony.com/schema/dic/services"
428+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
429+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
430+ http://symfony.com/schema/dic/services/services-1.0.xsd" >
431+
432+ <services >
433+ <service id =" App\Handler\One" >
434+ <tag name =" app.handler" key =" handler_one" />
435+ </service >
436+
437+ <service id =" App\Handler\Two" >
438+ <tag name =" app.handler" key =" handler_two" />
439+ </service >
440+
441+ <service id =" App\HandlerCollection" >
442+ <!-- inject all services tagged with app.handler as first argument -->
443+ <argument type =" tagged_locator" tag =" app.handler" index-by =" key" />
444+ </service >
445+ </services >
446+ </container >
447+
448+ .. code-block :: php
449+
450+ // config/services.php
451+ use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
452+ use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
453+
454+ $container->register(App\Handler\One::class)
455+ ->addTag('app.handler', ['key' => 'handler_one']);
456+
457+ $container->register(App\Handler\Two::class)
458+ ->addTag('app.handler', ['key' => 'handler_two']);
459+
460+ $container->register(App\Handler\HandlerCollection::class)
461+ // inject all services tagged with app.handler as first argument
462+ ->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('app.handler', 'key')));
463+
464+ Inside this locator you can retrieve services by index using the value of the
465+ ``key `` attribute. For example, to get the ``App\Handler\Two `` service::
466+
467+ // src/Handler/HandlerCollection.php
468+ namespace App\Handler;
469+
470+ use Symfony\Component\DependencyInjection\ServiceLocator;
471+
472+ class HandlerCollection
473+ {
474+ public function __construct(ServiceLocator $locator)
475+ {
476+ $handlerTwo = $locator->get('handler_two'):
477+ }
478+
479+ // ...
480+ }
481+
482+ Instead of defining the index in the service definition, you can return its
483+ value in a method called ``getDefaultIndexName() `` inside the class associated
484+ to the service::
485+
486+ // src/Handler/One.php
487+ namespace App\Handler;
488+
489+ class One
490+ {
491+ public static function getDefaultIndexName(): string
492+ {
493+ return 'handler_one';
494+ }
495+
496+ // ...
497+ }
498+
499+ If you prefer to use another method name, add a ``default_index_method ``
500+ attribute to the locator service defining the name of this custom method:
501+
502+ .. configuration-block ::
503+
504+ .. code-block :: yaml
505+
506+ # config/services.yaml
507+ services :
508+ # ...
509+
510+ App\HandlerCollection :
511+ arguments : [!tagged_locator { tag: 'app.handler', default_index_method: 'myOwnMethodName' }]
512+
513+ .. code-block :: xml
514+
515+ <!-- config/services.xml -->
516+ <?xml version =" 1.0" encoding =" UTF-8" ?>
517+ <container xmlns =" http://symfony.com/schema/dic/services"
518+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
519+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
520+ http://symfony.com/schema/dic/services/services-1.0.xsd" >
521+
522+ <services >
523+
524+ <!-- ... -->
525+
526+ <service id =" App\HandlerCollection" >
527+ <argument type =" tagged_locator" tag =" app.handler" default-index-method =" myOwnMethodName" />
528+ </service >
529+ </services >
530+ </container >
531+
532+ .. code-block :: php
533+
534+ // config/services.php
535+ // ...
536+
537+ $container->register(App\HandlerCollection::class)
538+ ->addArgument(new ServiceLocatorArgument(new TaggedIteratorArgument('app.handler', null, 'myOwnMethodName')));
396539
397540 Service Subscriber Trait
398541------------------------
@@ -484,3 +627,5 @@ and compose your services with them::
484627 When creating these helper traits, the service id cannot be ``__METHOD__ ``
485628 as this will include the trait name, not the class name. Instead, use
486629 ``__CLASS__.'::'.__FUNCTION__ `` as the service id.
630+
631+ .. _`Command pattern` : https://en.wikipedia.org/wiki/Command_pattern
0 commit comments