среда, 26 июня 2013 г.

EventDispatcher Drupal 8 Challenging

Эта статья основана на на статье описанной ещё в июле 2012 и проведённого Kyiv Code Sprint в июне 2013. Материал статьи остаётся актульным. Здесь опишем как работает система Event Dispatcher в Drupal 8. Будьте внимательны так как диспетчер в Д8 всеволишь один. Хотя логичнее сделать(для меня) для каждого события свой диспетчер. Наверное это будет сделано в Д9. Так как Д8 производительностью не блестает и подбираеться дата релиза, а работы не початый край. Так же это только начало познаний сложных структур Symfony 2 в Drupal 8.


index.php содержит:

$kernel = drupal_container()->get('httpkernel');
$response = $kernel->handle($request)->prepare($request)->send();
$kernel->terminate($request, $response);

Что такое drupal_container(). Это контейнер инъекций зависимостей, волшебный массив, который содержит объекты. Волшебство которого заключается в инициализации обьектов при запросе на получение. Dependency injection containe - DIC содержит описания классов, аргументы, зависимости. Особеностью также являеться, то что аргументы могут быть дугими элементами(это называеться зависимостями) DIC. Возможно регистрировать свои классы, или фабрики классов. Пример с ядра Drupal 8.

$this->register('resolver', 'Symfony\Component\HttpKernel\Controller\ControllerResolver');

Регистрируем объект, после получим.
$this->get('resolver')
Когда вызываеться get(), контейнер инициализирует новый класс и хранит его в контейнере. Потому при повторных вызовах мы получим тот же объект Symfony\Component\HttpKernel\Controller\ControllerResolver.

$this->register('dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher')
->addArgument(new Reference('service_container'))
->setFactoryClass('Drupal\Core\DependencyInjection\ContainerBuilder')
->setFactoryMethod('getKernelEventDispatcher');
Код регистрирует обьект, который можно получить
$this->get('dispatcher')
При вызове get(), сработает метод
Drupal\Core\DependencyInjection\ContainerBuilder::getKernelEventDispatcher
так как данный обьект зарегистрирован как фабрика и у нас указан аргумент, он будет передан метод getKernelEventDispatcher(). Так как объект передаёться “по ссылке” - это означает, что это что-то из вне DIC. В данном случае это сам DIC. DIC называеться контейнером сервисов, и всё что туд ложим называем сервисами по принципу Symfony.

Ядро
HTTP ядро использует события диспетчера при вызове handle():
- KernelEvents::REQUEST
- KernelEvents::CONTROLLER
- KernelEvents::VIEW
- KernelEvents::RESPONSE

События
События похожи на ловушки(hooks), но намного сложнее. Мы регистрируем класс с диспетчером, который вызовит метод класса getSubscribedEvents, который зарегистрирует методы/наблюдателей на определенные события. Пример класса ViewSubscriber:
static function getSubscribedEvents() {
  $events[KernelEvents::VIEW][] = array('onView');
  return $events;
}

Потому когда события KernelEvents::VIEW отправлено наблюдателям в HttpKernel:
$this->dispatcher->dispatch(KernelEvents::VIEW, $event);
Метод onView будет вызван с аргументом $event. Все подписчики получают единственный объект $event, который содержит только setters и getters.

К примеру в контейнере зависимостей иньекций можно просмотреть, как создаётся диспетчер getKernelEventDispatcher где создаётся объект EventDispatcher и присоеденяются подписчики событий.
Вот одни из многих.
$dispatcher->addSubscriber(new LegacyRequestSubscriber());

И другие.

2 комментария:

  1. Про EventDispatcher можете прочитать больше на рессурсе Symfony http://symfony.com/doc/current/components/event_dispatcher/introduction.html . Для меня данный паттерн является составным (Наблюдатель, стратегия, построитель). Хотя для использования компонент является гибким.

    ОтветитьУдалить
  2. Необходимо отметить, то что drupal_container заменен на Drupal::service
    https://api.drupal.org/api/drupal/core!includes!bootstrap.inc/function/drupal_container/8

    ОтветитьУдалить