Ответ
В 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.
Ответ 18+ 🔞
Ёпта, ну слушай, тут про автоматическое тегирование в Symfony Dependency Injection. Просто овердохуища способов, чтобы не париться и не прописывать каждый сервис руками, как будто на дворе 2002-й год.
1. Самый прикол — _instanceof в services.yaml:
Это, бля, базовая фишка, чтобы автоматом навешивать теги на все сервисы, которые реализуют какой-то интерфейс или класс.
services:
_instanceof:
AppServiceMessageHandlerInterface:
tags: ['app.message_handler']
Вот и всё, чувак. Любой сервис, который заявляет, что он MessageHandlerInterface, сразу получает тег app.message_handler. Никакой возни, мать его.
2. Атрибуты, с версии 6.1: Тут вообще красота — можно класс пометить атрибутом, и он сам себя протегирует. Выглядит как хитрая жопа, но работает.
use SymfonyComponentDependencyInjectionAttributeAutoconfigureTag;
#[AutoconfigureTag('app.message_handler')]
class EmailHandler implements MessageHandlerInterface
{
// ...
}
Написал и забыл, блядь. Компилятор сам всё подхватит.
3. Кастомный Compiler Pass для особо извращённых случаев:
Если логика тегирования сложная, на уровне "а если у него есть метод handle() и имя класса заканчивается на 'Listener'", то тут без своего Compiler Pass'а — никуда.
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');
}
}
}
}
Этот пасс, понятное дело, нужно ещё в ядре зарегистрировать в методе build(). Но это уже детали, ебать.
4. Как потом эти тегированные сервисы получить: Ну а дальше-то что? А дальше их нужно где-то использовать.
// Внутри другого CompilerPass'а
$taggedServices = $container->findTaggedServiceIds('app.message_handler');
// Или прямо в коде, если контейнер под рукой
$handlers = $this->container->get('service_container')->findTaggedServiceIds('app.message_handler');
А зачем это всё, спрашивается? А затем, чувак, чтобы не быть распиздяем. Вместо того чтобы в конфиге руками прописывать сотню хендлеров, ты один раз написал правило — и система сама всё расставила. Масштабируется на раз-два, соответствует принципу "меньше конфигурации — больше договорённостей". В общем, ебушки-воробушки, удобная штука.