Квест → Как хакнуть форму
Прошли: 77
Я хочу поговорить о пространствах имен для сервисов в Symfony, специфичное для Symfony3.
Это захватывающие времена, Symfony 4 уже на подходе — выходит 30-го ноября — так что этот пост может стать неактуальным в ближайшее время! Тем не менее концепт всё тот же, так что давайте взглянем на него!
В последнее время, общаясь со своей командой в SlowCode, мы определили общий способ создания сервисов.
Любой сервис, который обеспечивает логику приложения, будет находиться в папке App\Service
. Таким образом, все аккуратно, и все разработчики в команде знают, где их найти.
Следующий слой — это доменное имя. Это опять же, чтобы обеспечить порядок. Возможно, вы так не думаете, но когда вы в конечном итоге работаете с 8-ю доменными именами, в каждом из которых по 2-3 сервиса, то всё может стать запутано, если оно не аккуратно :)
Например, если у нас есть сервис, который имеет отношение к остаткам (Stock
) под названием StockAvailability
, то её название вместе с пространством имен будет AppBundle\Service\Stock\StockAvailability
.
Идентификатор сервиса должен быть разделён точкой ('.') по названию папок и подчёркиванием ('_'), если в названии — папки или сервиса — больше, чем одно слово.
Так, в предыдущем примере, мы задали бы все это дело вот так:
services: app.service.stock.stock_availability: class: AppBundle\Service\Stock\StockAvailability arguments: - '@doctrine.orm.entity_manager' ...
В последнее время в Symfony 3.3, был принят новый способ определения сервиса.
Теперь лучше задать идентификатор сервиса через FQCN (полное название класса с пространством имён). Итак, вместо того, как задавали мы его раньше, мы можем это сделать так:
services: AppBundle\Service\Stock\StockAvailability: public: true arguments: - '@doctrine.orm.entity_manager' ...
Задав его таким способом, вы по-прежнему можете достать сервис из сервис-контейнера (с новым идентификатором, конечно, – с полным имением класса):
use AppBundle\Service\Stock\StockAvailability public function fooAction(Request $request) { // до Symfony 3.3 вы получали бы её так // $stockService = $this->get('app.service.stock.stock_availability'); // В Symfony 3.3 вы можете получить сервис так // (Это возможно, только если вы объявлили свой сервис как публичный) $stockService = $this->get(StockAvailability::class); }
Как следует из официальной страницы Symfony, хорошей практикой является объявление ваших сервисов закрытыми (приватными), а не публичными, а затем внедрять (инджектить) любые сервисы, которые могут понадобиться внутри контроллера, а не получать их из сервис-контейнера (аналогично инъекции зависимостей внутри сервисов), например:
use AppBundle\Service\Stock\StockAvailability public function fooAction(Request $request, StockAvailability $stockService) { // now we have it injected into our variable $stockService // so we don't need to get it from the container }
Итак, я думаю, что идентификатор с полным именем класса, вместо изобретенной номенклатуры — это хорошо. По крайней мере, больше не будет путаницы среди различных разработчиков из команды.
О приватных/публичных сервисах. Я понимаю, куда Symfony "клонит", и я думаю, что ограничение использовать ‘инъекции’ вместо ‘получения’ делает код более надежным, и, вероятно, более читаемым, в конце концов. Однако, я все же думаю, есть и плюсы у подхода, использовавшегося до 3.3 версии. Получение сервисов из контейнера является очень полезным, и обеспечивает гибкость и скорость.
Я думаю, что, поскольку по-прежнему можно объявить сервис публичным, это я и буду делать... что вы будете делать?
Подробнее здесь:
Symfony service container
Symfony 3.3 best practices
Symfony class for service id