Иногда одна единственная идея способна изменить все представление о программировании. Для меня одной из такой идей была идея сервис-контейнера. Это как золотой грааль). Вообще-то, идея не сугубо Symfony framework, это просто один из удачных паттернов проектирования приложений, часто используемый в Symfony.
В двух словах и на пальцах: сервис-ориентированая архитектура позволяет регистрировать класс (сервис), который можно будет достать с любого места приложения, зная идентификатор. То есть, говорим: "Контейнер, а дай нам такой-то сервис", и получаем пригодный для использования объект.
Но, имхо, гениальна не так сама идея сервис контейнера, как Dependency Injection(внедрение зависимости).
Попробуем объяснить, что это такое.
Представьте себе ситуацию, что у вас есть класс, объект которого не может работать самостоятельно - для его работы нужны другие объекты (если вы считаете такую ситуацию маловероятной или ненужной - прочитайте эту заметку немного позже =))
Dependency Injection позволяет разрешить эти зависимости. Другими словами, мы можем не просто сказать "Контейнер, а дай нам такой-то сервис", а "Контейнер, а дай нам такой-то сервис со всеми необходимыми зависимостями". Сервис будет зависим от других сервисов, но внешне мы об этом даже не будем догадываться.
Перейдем к практике.
Чтобы получить сервис с контроллера, достаточно вызвать:
class HelloController extends Controller { // ... public function sendEmailAction() { // ... $mailer = $this->get('my_mailer'); $mailer->send('ryan@foobar.net', ...); } }
В Symfony framework конфигурация сервиса может осуществляться в разных форматах (yml, xml, php), в примерах будем использовать yml:
# app/config/config.yml services: my_mailer: class: Acme\HelloBundle\Mailer arguments: [sendmail]Естественно, сервисы можно использовать не только в контроллере. Хорошим тоном будет не инжектить весь контейнер в сервис (речь идет о зависимостях), а только заинжектить необходимый сервис.
Например, если есть два сервисы, один может быть внедрен в другой:
# app/config/config.yml services: service_foo: class: Acme\HelloBundle\SomeFeature\Foo arguments: [@service_goo] service_goo: class: Acme\HelloBundle\SomeFeature\GooЕсли теперь вызвать в контроллере
$mailer = $this->get('service_foo');то получим экземпляр Foo с уже заинжектеным Goo.
arguments - то, что передадим в конструктор.
Пример классов:
<?php namespace Acme\HelloBundle\SomeFeature class Foo { private $goo; public function __construct(Goo $goo) { $this->goo = $goo } }
<?php namespace Acme\HelloBundle\SomeFeature class Goo { }
Осуществлять Dependency Injection можно не только через конструктор, но и с помощью сеттера. Модифицируем предыдущий пример:
# app/config/config.yml services: service_foo: class: Acme\HelloBundle\SomeFeature\Foo calls: - [setGoo, ["@service_goo"]] service_goo: class: Acme\HelloBundle\SomeFeature\Goo
<?php namespace Acme\HelloBundle\SomeFeature class Foo { private $goo; public function setGoo(Goo $goo) { $this->goo = $goo } }
Похожим образом можно инжектить и параметры, только вместо символа @ использовать "окружение" символом % с обеих сторон :
# app/config/config.yml parameters: my_mailer.class: Acme\HelloBundle\Mailer my_mailer.transport: sendmail services: my_mailer: class: "%my_mailer.class%" arguments: ["%my_mailer.transport%"]
На этом базовое знакомство закончилось =)
Ссылка на оригинал: http://folkprog.net/service-container-and-dependency-injection-in-symfony-framework/