Symfonyで独自イベント(カスタムイベント)を作成する方法をやさしく解説
生徒
「Symfonyって、決まったイベントだけ使えるんですか?」
先生
「いいえ、自分でイベントを作ることもできます。これを独自イベント、またはカスタムイベントと呼びます。」
生徒
「自分でイベントを作るって、難しそうです……」
先生
「仕組みを順番に見ていけば大丈夫です。今日はSymfonyで独自イベントを作る方法を一緒に見ていきましょう。」
1. Symfonyの独自イベント(カスタムイベント)とは?
Symfonyの独自イベント(カスタムイベント)とは、開発者が自由なタイミングで発生させられるイベントのことです。イベントとは「ある出来事が起きた」という合図のようなものです。例えば「会員登録が完了した」「商品が購入された」など、アプリケーション独自の出来事をイベントとして扱えます。
Symfonyではイベントとリスナーの仕組みが用意されており、独自イベントを使うことで処理を整理し、読みやすく保守しやすいコードを書くことができます。
2. 独自イベントを使うメリット
独自イベントを使う最大のメリットは、処理を分離できる点です。分離とは、役割ごとに処理を分けることです。例えば、注文処理の中にメール送信やログ保存の処理を直接書くと、コードが長くなり読みにくくなります。
イベントを使えば「注文完了イベント」を発生させ、メール送信やログ保存は別の場所で処理できます。これにより、プログラム全体が整理され、後から修正しやすくなります。
3. 独自イベント用のイベントクラスを作成する
まずはイベントそのものを表すイベントクラスを作成します。イベントクラスは「何が起きたか」と「必要な情報」をまとめた箱のようなものです。
namespace App\Event;
use Symfony\Contracts\EventDispatcher\Event;
class UserRegisteredEvent extends Event
{
public const NAME = 'user.registered';
private string $email;
public function __construct(string $email)
{
$this->email = $email;
}
public function getEmail(): string
{
return $this->email;
}
}
この例では「ユーザー登録が完了した」というイベントを表しています。メールアドレスをイベントに持たせることで、後からリスナーで使えるようになります。
4. イベントを発生(ディスパッチ)させる方法
次に、作成した独自イベントを発生させる処理を書きます。イベントを発生させることを「ディスパッチする」と言います。ディスパッチとは「知らせを送る」という意味です。
use App\Event\UserRegisteredEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class UserService
{
public function register(
string $email,
EventDispatcherInterface $dispatcher
): void {
$event = new UserRegisteredEvent($email);
$dispatcher->dispatch($event, UserRegisteredEvent::NAME);
}
}
このコードでは、ユーザー登録処理の中でイベントを作成し、EventDispatcherを使ってイベントを発生させています。
5. 独自イベントを受け取るリスナーを作成する
イベントが発生したら、それを受け取って処理を行うリスナーを作成します。リスナーは「イベントを聞いて行動する人」のような存在です。
namespace App\EventListener;
use App\Event\UserRegisteredEvent;
class SendWelcomeMailListener
{
public function onUserRegistered(UserRegisteredEvent $event): void
{
$email = $event->getEmail();
// ここでメール送信処理を行う
}
}
このリスナーは、ユーザー登録イベントを受け取り、登録されたメールアドレスを使って処理を行います。
6. services.yamlでリスナーを登録する
作成したリスナーをSymfonyに認識させるため、設定ファイルに登録します。ここではservices.yamlを使います。
services:
App\EventListener\SendWelcomeMailListener:
tags:
- { name: kernel.event_listener, event: 'user.registered', method: onUserRegistered }
この設定により、「user.registered」という独自イベントが発生したときに、このリスナーが呼び出されます。
7. 独自イベントの処理の流れを整理しよう
Symfonyの独自イベントは、次の流れで動きます。まずイベントクラスを作成し、次にイベントをディスパッチします。そして、登録されたリスナーがイベントを受け取り、それぞれの処理を実行します。
この流れを理解すると、処理の追加や変更がしやすくなり、大きなアプリケーションでも混乱しにくくなります。
8. 初心者がつまずきやすいポイント
初心者がつまずきやすいのは、イベント名の書き間違いや、リスナーが正しく登録されていないケースです。また、イベントクラスとリスナーの役割を混同してしまうこともあります。
イベントは「出来事」、リスナーは「それを受けて行う処理」と意識すると、理解しやすくなります。
まとめ
Symfonyにおける独自イベント(カスタムイベント)の作成と活用方法について、主要なポイントを詳しく振り返ってみましょう。PHPフレームワークの中でも、Symfonyはコンポーネントの独立性が高く、イベント駆動型設計(Event-Driven Design)を取り入れることで、非常に拡張性の高いシステムを構築できます。今回学んだ「イベントクラスの定義」「ディスパッチャーによる発行」「リスナーによる受信」という一連の流れは、中規模以上のプロジェクトでコードの肥大化を防ぐために欠かせない技術です。
カスタムイベントの実装ステップ再確認
実務で独自イベントを導入する際は、まず「どのようなビジネスロジックが完了したか」を明確に定義することが重要です。例えば、ECサイトであれば「注文確定(OrderPlaced)」、学習システムであれば「コース完了(CourseCompleted)」などが挙げられます。これらの出来事が発生した瞬間に、メインの処理から切り離して実行したいサブタスク(通知送信、ログ記録、外部API連携など)をリスナーに集約させるのが、Symfonyらしいスマートな設計です。
ここで、実際に複数のリスナーを登録した場合の動作を確認するための、具体的なサンプルプログラムを見てみましょう。
namespace App\Event;
/**
* 注文完了時に発行されるカスタムイベントクラス
*/
class OrderPlacedEvent
{
public const NAME = 'order.placed';
private int $orderId;
private string $customerEmail;
public function __construct(int $orderId, string $customerEmail)
{
$this->orderId = $orderId;
$this->customerEmail = $customerEmail;
}
public function getOrderId(): int
{
return $this->orderId;
}
public function getCustomerEmail(): string
{
return $this->customerEmail;
}
}
上記のイベントクラスがディスパッチされると、以下のようなリスナーがそれを受け取ります。
namespace App\EventListener;
use App\Event\OrderPlacedEvent;
/**
* 注文完了イベントを受けて在庫を減らす処理を行うリスナー
*/
class UpdateInventoryListener
{
public function onOrderPlaced(OrderPlacedEvent $event): void
{
$orderId = $event->getOrderId();
// ここにデータベースの在庫更新ロジックを記述
echo "注文ID:" . $orderId . "の在庫を更新しました。";
}
}
実行結果のイメージは以下のようになります。
注文ID:1024の在庫を更新しました。
注文完了メールを sample@example.com に送信しました。
このように、1つのイベントに対して複数のリスナー(在庫更新、メール送信など)を紐付けることが可能です。これにより、注文処理のコントローラーは「イベントを投げるだけ」という単純な責務に専念でき、テストのしやすさ(テスト容易性)も格段に向上します。
さらにステップアップするために
Symfonyのイベントシステムを使いこなすには、EventListener(リスナー)の他に、EventSubscriber(サブスクライバー)という仕組みも知っておくと便利です。サブスクライバーは自分自身がどのイベントを購読するかを知っているクラスで、services.yamlへの記述を最小限に抑えることができます。大規模な開発では、設定ファイルの記述を減らすためにサブスクライバーが好まれる傾向にあります。
また、イベントの優先順位(priority)を設定することで、複数のリスナーが実行される順番を制御することも可能です。例えば「在庫チェック」を「メール送信」より先に必ず実行したい場合に有効です。こうした細かい制御ができる点も、Symfonyがプロフェッショナルな現場で選ばれる理由の一つと言えるでしょう。
生徒
「先生、独自イベントの作り方がだいぶ分かってきました!イベントクラスを作って、ディスパッチャーで呼んで、リスナーで待ち構える。この3段構えですね。」
先生
「その通りです。素晴らしい理解ですね。なぜ直接コントローラーに全部の処理を書かずに、わざわざイベントを使うんだっけ?」
生徒
「ええと、処理をバラバラに切り離して、コードをスッキリさせるためですよね。もしメール送信の仕様が変わっても、注文処理のコードを触らなくて済むから安心です!」
先生
「正解です!それを疎結合(そけつごう)と呼びます。保守性が高まるので、チーム開発では特に重宝される考え方ですよ。」
生徒
「疎結合……かっこいい言葉ですね。でも、リスナーが増えすぎると、どこで何が起きているか分からなくなりませんか?」
先生
「良い視点ですね。Symfonyにはデバッグコマンドがあります。ターミナルで下記のコマンドを打つと、どのイベントにどのリスナーが紐付いているか一覧で見られるんですよ。」
php bin/console debug:event-dispatcher
生徒
「おぉ、これなら迷子にならずに済みそうです!さっそく自分のプロジェクトでも、ユーザーのアクションごとにカスタムイベントを作ってみます!」
先生
「ぜひ挑戦してみてください。最初は面倒に感じるかもしれませんが、後から機能を追加するときに『イベントにしておいて良かった!』と思う日が必ず来ますからね。」