По результатам опроса о чем написать, естественно большинство захотело известий с полей. На большую статью пока не замахиваюсь, но по мере появления интересных сниппетов постараюсь ими делиться.
Сегодняшний сниппет посвящается службам (сервисам), а именно – как получить доступ из пользовательской службы к другой службе в рамках приложения.
Положим у нас в нашем пакете есть служба Company\SuperBundle\Service\CoolService описываемая следующей конфигурацией:
# app/config/config.yml services: giveMeCool: class: Company\SuperBundle\Service\CoolService arguments: [%arg1%, %arg2%] |
Все идет хорошо, пока нам не требуется другая служба. Например мне понадобился logger
. Вообще говоря это есть в документации, но пришлось повозиться пока я это нашел: другую службу можно указать в аргументах своего сервиса через @svcname
:
# app/config/config.yml services: giveMeCool: class: Company\SuperBundle\Service\CoolService arguments: [%arg1%, %arg2%, @logger] |
Соответственно код конструктора службы надо немного модифицировать:
# src/Company/SuperBundle/Service/CoolService.php //... // services protected $logger = null; public function __construct( $arg1, $arg2, \Symfony\Bundle\ZendBundle\Logger\Logger $logger) { $this->logger = $logger; //... |
Тип сервиса можно указать явно (как в примере), тогда мы сможем как минимум пользоваться подсказками нашей IDE (что в Symfony2 получается не часто).
Update
Читатель Костег в комментариях предложил более общее решение для данного кейса: можно передавать в пользовательскую службу не отдельные службы, а весь service container разом (мало ли когда и какая из служб вам потребуется):
# app/config/config.yml services: giveMeCool: class: Company\SuperBundle\Service\CoolService arguments: [%arg1%, %arg2%, @service_container] |
Теперь в классе вашей службы вы сможете получить доступ к любой из служб, определенных в рамках вашего приложения: templating, logger, request, routing etc…
# src/Company/SuperBundle/Service/CoolService.php //... // services protected $serviceContainer = null; public function __construct( $arg1, $arg2, \Symfony\Component\DependencyInjection\Container $container) { $this->serviceContainer = $container; $this->serviceContainer->get('routing'); //... |
По-моему это уже выглядит достаточно удобно – во всяком случае к основным критическим данным служба сможет получить доступ без лишних извращений над кодом 🙂
смотря что там за сервисы и сколько их там нужно. Возможно стоит вообще передавать весь ContainerInterface?)
Расскрой плизз тему Controllers as a services & Interface injection. А то я что-то не до конца понимаю что там как и зачем
Меня и перечисление атрибутов в таком виде не устраивает, не говоря уж о службах. Вот представь, 5 параметров для инициализации facebook, плюс логгер мне понадобился… уже 6. И это я еще не разошелся )))
Но весь контейнер я не знаю как туда передать разумным образом.
хы-хы, а я задавался подобным вопросом и Фабьен ответил http://groups.google.com/group/symfony-users/browse_thread/thread/25ccca4be6d55f84 .
Зы: так а чего не передавать сразу заинициализированый фейсбук?
Что-то не понял, если я определяю класс как сервис, то service container там всегда доступен (примерно $this->get(‘service_container’))?
Что касается facebook то я имел в виду следующее:
Аргументов много. В контроллере же $this->get(‘facebook’) – это удобно.
Не, там он не будет доступен через $this->get(), этого метода вообще может и не быть в сервисе. Смотри конструктор в классе Container. Через конфигурацию инъектить можно
# app/config/config.yml
services:
giveMeCool:
class: Company\SuperBundle\Service\CoolService
arguments: [@service_container]
Facebook – да, я это и имел в виду.
Нет, фейсбук мне кажется как раз в виде сервиса и хорош. Я с тем же успехом new Facebook() мог делать в контроллере каждый раз – но это ж не айс )
За хинт спасибо ) В принципе это общий случай @сервис. Интересно, а список всех служб есть?
P.S. полезно вести блог, что-то новое узнаешь… ))))
Костя, спасибо, написал update к посту. Если дашь ссылку на тебя, прилинкую к твоему нику в тексте.
Да незачто. Я без сайта, линковать нечего).
Вот только похоже трабл есть:
Насколько я знаю, Response удалили из ServiceContainer.
зы: неплохо было б превью к комменариям добвить
Да, ты прав, response нет. Это я раньше из статьи про контроллер почерпнул. Надо бы погуглить насчет актуального списка служб.
@service_container – можно подробнее про эту конструкцию, как фрэйм понимает что и где искать?
Посмотри конструктор класса Container, там он указывается
\Symfony\Component\DependencyInjection\Container если уж быть совсем точным )
Это один из core concepts Symfony2, Dependency Injection. Если интересно – надо туда копать )
Это я знаю, вопрос был немного в другом. Мы в конфиге можем ссылаться на сервисы которые уже объявляли , а тут этот сервис уже объявлен где то в др. месте, наверно в конфеге бандла к которому он относится. Если я правильно понимаю, то конфиги бандлов на этапе инициализации приложения собираются и тогда мы получаем доступ к сервису любого бандла.
P.S. Кстати было бы не плохо написать статью по архитектуре симфони и процессу инициализации/запуска приложения, диспетчеризации и т.д.
Вообще говоря я свои сервисы определял в общем конфиге (app/config) – как в примерах описано. Возможно чтобы инициировалась служба, определенная в пакете, его конфигурацию нужно импортировать, как к примеру конфигурацию маршрутизатора. Надо попробовать.
@service_container – в xml конфиге как передать?
Согласно последней документации http://symfony.com/doc/current/book/service_container.html#creating-configuring-services-in-the-container можно попробовать
Попробую это вариант, книгу еще до этого момента не дочитал.
Решить задачу получилось
<argument type="service" id="service_container"></argument>
Игорь, у меня получается вставить ) Пришли код на мыло, я вставлю в твой коммент
Message sent to email 🙂