Правильно, через
services.yaml. Например, мы создали интерфейс, у коготорого есть 3 реализации. Массив реализаций нам нужно передать куда-то в конструктор. У нас есть autowiring, и мы хотим его использовать. Переходим в services.yaml:App\Service\SightingScorer:Где каждый из перечисленных классов реализует интерфейс
arguments:
$scoringFactors:
- '@App\Scoring\TitleFactor'
- '@App\Scoring\DescriptionFactor'
- '@App\Scoring\CoordinatesFactor'
ScoringFactorInterface.Со временем реализаций становится всё больше, и всё больше вероятность, что при очередной деплое мы забудем обновить конфигурацию и прибиндить очередной класс. Так вот, ребята с symfonycast описали очень крутой способ, как биндить такие реализации автоматом. Идём в
Kernel.php, там переопределяем метод build, и в нём добавляем одну строчку:protected function build(ContainerBuilder $container)Всё, после этого все реализации будут помечены тегом "scoring.factor", и в services.yaml нам достаточно один раз прописать:
{
parent::build($container);
$container->registerForAutoconfiguration(ScoringFactorInterface::class)
->addTag('scoring.factor');
}
App\Service\SightingScorer:Такой подход называется тегированный итератор, и о нём можно почитать более подробно: https://symfony.com/doc/current/service_container/tags.html
arguments:
$scoringFactors: !tagged_iterator scoring.factor
Главное не забыть о том, что для того, чтобы автовайринг сработал нам нужно передавать аргументом итератор, а не массив:
private iterable $scoringFactors;Очень крутая штука, как по мне. И, в теории, что-то подобное можно реализовать и для других языков и фреймворков.
public function __construct(iterable $scoringFactors)
{
$this->scoringFactors = $scoringFactors;
}