Я хочу добавить службы в контейнер служб, которые я хочу использовать позже в своем контроллере или службе.
Итак, я создал две службы со своим настраиваемым тегом fbeen.admin
Вот они:
services:
app.test:
class: AppBundle\Admin\TestAdmin
tags:
- { name: fbeen.admin }
fbeen.admin.test:
class: Fbeen\AdminBundle\Admin\TestAdmin
tags:
- { name: fbeen.admin }
Теперь я хочу использовать все службы с тегом fbeen.admin в моем контроллере, но не знаю как.
Я следовал руководству Как работать с сервисными тегами, но застрял на этом правиле:
$definition->addMethodCall('addTransport', array(new Reference($id)));
Каким-то образом следует вызвать метод addTransport класса TransportChain, но кажется, что он не был вызван.
И даже если бы он был вызван, у меня все еще нет списка служб с тегом fbeen.admin в моем контроллере.
Я уверен, что что-то упускаю, но кто может мне объяснить, что это такое?
p.s. Я знаю, что compilerPass запускается во время сборки, но, например, администратор sonata знает все классы администратора, а twig знает все расширения twig. Как они узнали?
Спасибо за чтение этого :-)




Здесь следует отметить следующее: CompilerPass не запускает addTransport (или как вы его называете) в самом проходе компилятора - просто говорит: «Когда настало время - запустите класс $definition->addTransport(...) с этими данными». . Место, где это происходит, следует искать в каталоге кэша (grep -R TransportChain var/cache/), где он устанавливает $transportChain->addTransport(...).
Когда вы впервые используете эту службу, данные заполняются только тогда, когда класс создается из контейнера.
Вызовы addTransport() (или как вы хотите его назвать) будут происходить при создании экземпляра службы - когда вы используете ее через container-> get (...) или автоматически подключаете в конструкторе или службе контроллера. и поэтому данные будут доступны из любого места, где они были сохранены.
Контейнер компилируется один раз (чаще при отладке, а в продакшене только один раз). С addMethodCall... вы управляете тем, что как только вы запрашиваете сервис из контейнера, который вы храните в $definition (в данном случае это контроллер). Затем контейнер вызовет метод addMethodCall('method'.. во время инициализации вашей службы.
Как это будет выглядеть в контейнере:
// This is pseudo content of compiled container
$service = new MyController();
// This is what compiler pass addMethodCall will add, now its your
// responsibility to implement method addAdmin to store admins in for
// example class variable. This is as well way which sonata is using
$service->addAdmin(new AppBundle\Admin\TestAdmin());
$service->addAdmin(new AppBundle\Admin\TestAdmin());
return $service; // So you get fully initialized service
Что вы можете сделать:
// Your services.yaml
services:
App/MyController/WantToInjectSerivcesController:
arguments:
$admins: !tagged fbeen.admin
// Your controller
class WantToInjectSerivcesController {
public function __construct(iterable $admins) {
foreach ($admins as $admin) {
// you hot your services here
}
}
}
Бонусная автоматическая пометка ваших услуг. Допустим, все ваши контроллеры реализуют интерфейс AdminInterface.
// In your extension where you building container or your kernel build method
$container->registerForAutoconfiguration(AdminInterface::class)->addTag('fbeen.admin');
Это автоматически пометит все сервисы, реализующие ваш интерфейс, тегом. Таким образом, вам не нужно явно устанавливать тег.
Тег! Доступен в Symfony 3.4+.
Извините, пропустил вашу версию Symfony, добавлено объяснение для 3.3
Это сработало для меня:
расширите класс TransportChain с помощью метода getTransports:
public function getTransports()
{
return $this->transports;
}
и используйте службу TransportChain в моем контроллере:
use AppBundle\Mail\TransportChain;
$transportChain = $this->get(TransportChain::class);
$transports = $transportChain->getTransports();
// $transports is now an array with all the tagged services
Спасибо Алистер Булман за то, что подтолкнули меня вперед :-)
Спасибо, Алистер, а как теперь читать необходимые данные из кеша?