Ответ
В Symfony Dependency Injection есть несколько способов автоматически назначать теги сервисам.
1. Использование _instanceof в конфигурации services.yaml:
Это основной метод для автоматической разметки всех сервисов, реализующих определенный интерфейс или наследующих класс.
services:
_instanceof:
AppServiceMessageHandlerInterface:
tags: ['app.message_handler']
Все сервисы, реализующие MessageHandlerInterface, получат тег app.message_handler.
2. Использование атрибутов (начиная с Symfony 6.1):
Можно помечать классы сервисов атрибутом #[AutoconfigureTag].
use SymfonyComponentDependencyInjectionAttributeAutoconfigureTag;
#[AutoconfigureTag('app.message_handler')]
class EmailHandler implements MessageHandlerInterface
{
// ...
}
3. Создание Compiler Pass для сложной логики:
Если нужна динамическая логика назначения тегов, создается кастомный CompilerPass.
use SymfonyComponentDependencyInjectionCompilerCompilerPassInterface;
use SymfonyComponentDependencyInjectionContainerBuilder;
class CustomTagCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
foreach ($container->findDefinitions() as $id => $definition) {
$class = $definition->getClass();
if ($class && is_subclass_of($class, SomeBaseClass::class)) {
$definition->addTag('custom.tag');
}
}
}
}
Этот CompilerPass нужно зарегистрировать в методе build() вашего Kernel.
4. Получение тегированных сервисов:
В коде или в другом CompilerPass можно получить все сервисы с определенным тегом.
// В CompilerPass
$taggedServices = $container->findTaggedServiceIds('app.message_handler');
// В контроллере или сервисе через инжектированный ContainerBuilder
$handlers = $this->container->get('service_container')->findTaggedServiceIds('app.message_handler');
Почему это важно: Автоматическое тегирование избавляет от ручной регистрации каждого сервиса в конфигурации, делает систему более масштабируемой и соответствует принципу Convention Over Configuration.