9.12. Event Dispatcher、特定のイベント用のカスタムコード
イベントシステムを使うと、特定のイベントに対してカスタムコードで機能を変更できる、より複雑なシステムを構築できます。Drupal 7 の多くのフックがイベントに置き換えられました。これにより、Drupal の多くの部分と追加の contrib モジュールの動作を統一できました。イベントシステム自体は Symfony から来ており、次の部分で構成されています:
Event Subscribers - 特定のイベントへの「サブスクライバー」は、特定のイベントで発火する関数やメソッドです。コードでは、次のクラスを実装するクラスです:
\Symfony\Component\EventDispatcher\EventSubscriberInterface
Event Registry - Event Subscribers の発火の順序に従って収集・ソートします。サブスクライバーのレジストリは、イベント名とイベントの優先度(順序)のキーと値の配列として Event Dispatcher オブジェクトに保存されます。イベントがサービスとして登録されると、そのイベントはグローバルに利用可能なディスパッチャーとして登録されます。
Event Dispatcher - イベントが発火するメカニズムで、適切なタイミングで Event Subscribers を呼び出せます。たいていの場合、少なくとも 1 つの Event Dispatcher インスタンスがサービスとして表されます。Event Dispatcher クラスは次を実装します:
\Symfony\Component\EventDispatcher\EventDispatcherInterface
Event Context - 多くのイベントは特定のデータセットを必要とし、それはイベントサブスクライバーにとって重要かもしれません。これは Event Subscriber に渡される単純な値かもしれませんし、必要なデータを含むクラスかもしれません。Event Context クラスは次のクラスを拡張します:
\Symfony\Component\EventDispatcher\Event
これがどう動作するかを明確にするために、イベントを扱う例を見てみましょう。
すべてのコードを github の drupalbook_examples モジュールに追加しました。モジュールをダウンロードして、あなたのウェブサイトに追加できます:
https://github.com/levmyshkin/drupalbook8
Drupal 8 にはもう hook_init() がありません:
https://www.drupal.org/node/2013014
今では、Event Subscriber を使ってページの読み込み時に必要なコードを実行できます:
modules/custom/drupalbook_examples/drupalbook_examples.services.yml
services:
drupalbook_examples.event_subscriber:
class: Drupal\drupalbook_examples\EventSubscriber\DrupalbookExamplesSubscriber
tags:
- {name: event_subscriber}
Event Subscriber を接続するには、サービスをモジュールファイル *.services.yml に追加する必要があります。ここで、Event subscriber がどのクラスになるかを記述し、tags に event_subscriber という名前を書きます。次に、event subscriber クラスを作成する必要があります:
modules/custom/drupalbook_examples/src/EventSubscriber/DrupalbookExamplesSubscriber.php
<?php
namespace Drupal\drupalbook_examples\EventSubscriber;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class DrupalbookExamplesSubscriber implements EventSubscriberInterface {
public function checkForRedirection(GetResponseEvent $event) {
if ($event->getRequest()->query->get('redirect-me')) {
$event->setResponse(new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE])));
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = array('checkForRedirection');
return $events;
}
}
このコードの主なポイントを見てみましょう。私たちの event subscriber のクラスは EventSubscriberInterface インターフェースを実装しており、このインターフェースの主なメソッドは getSubscribedEvents() です。これは、私たちのサブスクライバーがどのイベントに応答するか、そしてイベントが発生した場合にどのメソッドを呼び出すかを決定します。私たちの場合、リクエストの処理後、Drupal の動作のまさに最初にイベントが発生します。ここでは、イベントが発生すると checkForRedirection() メソッドを呼び出します。メソッド自体では、redirect-me パラメーターを取得した場合、ユーザーを example.com にリダイレクトします。
このコードは、ページを読み込むたびに、あるいはほぼ常に機能します。実は、ページがキャッシュされていると、キャッシュから提供されることがあり、Drupal はすべてのコード片を実行せず、キャッシュがすぐに機能します。
この場合、hook_boot() フックのアナログを使えます。これは hook_init() と同様に Drupal 8 から削除されました:
https://www.drupal.org/node/1909596
もう 1 つ redirectBeforeWithoutCache() メソッドを追加しましょう。
modules/custom/drupalbook_examples/src/EventSubscriber/DrupalbookExamplesSubscriber.php:
<?php
namespace Drupal\drupalbook_examples\EventSubscriber;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class DrupalbookExamplesSubscriber implements EventSubscriberInterface {
public function checkForRedirection(GetResponseEvent $event) {
if ($event->getRequest()->query->get('redirect-me')) {
$event->setResponse(new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE])));
}
}
public function redirectBeforeWithoutCache(GetResponseEvent $event) {
if ($event->getRequest()->query->get('redirect-me')) {
$event->setResponse(new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE])));
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = array('checkForRedirection');
$events[KernelEvents::REQUEST][] = array('redirectBeforeWithoutCache', 300);
return $events;
}
}
getSubscribedEvents() メソッドでは、同じ KernelEvents::REQUEST イベントへの応答を登録しました。ただしこの場合、優先度を 300 に設定しました。これにより、このメソッドは、このイベントで発火するが優先度がより低いメソッドよりも早く機能できます。これで、リダイレクトはキャッシュされたページでも機能します。
Drupal には Subscriber に追加できるイベントがかなり多くあります。公式ドキュメントでさらに多くの例を見つけられます: