0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Symfony ComponentAdvent Calendar 2022

Day 13

他のアプリケーションにメッセージを送信、"Messenger"

Last updated at Posted at 2022-12-12

Symfony Component Advent Calendar 2022の13日目の記事です。半分きた。

最初に

SymfonyはPHPのフレームワークのひとつです。しかし、公式サイトの説明文には

Symfony is a set of PHP Components, a Web Application framework, a Philosophy, and a Community — all working together in harmony.
(SymfonyはPHPコンポーネントのセットで、Webアプリケーションフレームワークで、哲学、そしてコミュニティです。それらがハーモニーを奏でながら動作しています。)

と書かれている通り、PHPコンポーネントのセットで、たくさんのコンポーネントを提供しており、それらを組み合わせてひとつのフレームワークとして動作しています。Symfonyのコンポーネントは、Symfony上だけで動作するのではなく、他のPHPフレームワークやアプリケーションでも動作している強力なものが揃っています。

今回はそれらの中から、役立ちそうなもの・お薦めしたいものを紹介していきたいと思います。

※記事内ではautoloadのインポートは省略します。

他のアプリケーションにメッセージを送信、"Messenger"

Messengerは、他のアプリケーションと連携するためにメッセージを送受信するためのコンポーネントです。EventDispatcherと似てますが、ちょっと違います。

インストール

composer require symfony/messenger

メッセージとは

ある処理を実行した後、その処理のコンテキスト外の処理を他のアプリケーションやサービスにお願いするときがあります。例えば、メール送信とか集計処理とか。そのような場合に『つづきの処理よろしく!』と、後をまかせる際に使うのがメッセージです。基本、本来の処理へ後処理の処理結果は影響しません。(メッセージを受け取った処理がどうなろうが、本来の処理は正常終了します)。ただし、メッセージの受け取り先が同じSymfonyプロジェクトですぐに処理を実行する場合は影響があります。

Symfonyの場合、メッセージ送信後すぐに処理を行うこともできますし、後でゆっくり実行する(いわゆる、キュー)ことも可能です。

メッセージの送り方

Symfonyでのメッセージの送り方は、MessageBus, Message, Envelopeを使います。

1. Messageクラスを作る

Messageクラスを作り、他のアプリケーションに渡すデータをセットします。 bin/soncole make:messageで簡単に生成できます。

SendMailMessage.php

use App\Entity\Item;

class SendMailMessage
{
    public function __construct(private readonly Item $item)
    {
    }
    
    public function getItem(): Item
    {
        return $this->item;
    }
}

2. Envelope(封筒)に入れる

MessageオブジェクトをEnvelopeオブジェクトに入れます。入れる際にStampを押すこともできます。(後述)

ItemController.php

use App\Entity\Item;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Stamp\DelayStamp;
 
class ItemController
{
    public function publish(): Response
    {
        // なにか処理
        $item = new Item();
        ...

        // Messageオブジェクト作成
        $message = new SendMailMessage($item);

        // Envelopeに入れる
        $envelope = new Envelope($message, [
            new DelayStamp(5000)
        ]);
    }
}

3. MessageBusで送る

MessageBusオブジェクトを使って、メッセージを送信します。dispatch()メソッドにEnvelopeオブジェクトを渡しますが、Stampが特にない場合は、直接Messageを渡すことも可能です。

ItemController.php

use App\Entity\Item;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\DelayStamp;
 
class ItemController
{
    public function publish(MessageBusInterface $bus): Response
    {
        // なにか処理
        $item = new Item();
        ...

        // Messageオブジェクト作成
        $message = new SendMailMessage($item);

        // Envelopeに入れる
        $envelope = new Envelope($message, [
            new DelayStamp(5000)
        ]);

        // 送信する
        $bus->dispatch($envelope);

        // 特にStampがなければ、そのままMessageを渡すことで、dispatch()ないでEnvelopeに入れてくれます
        $bus->dispatch($message);
    }
}

備考:Stamp

Stampは、メッセージをどのように処理するかであったり、現在のメッセージの状況を設定できます。
例えば、上記の例で挙げたDelayStampはメッセージを送るタイミングをずらすことができます。
また、状況を表すReceivedStamp, SentStampというものも存在します。
StampInterfeceを実装することで、独自のスタンプも作れます。押されたStampEnvelopのメソッドを利用することで取得できます。


$message = new SendMailMessage($item);
$envelope = new Envelope($message);
$envelope->last(); // 最後に押されたスタンプを取得
$envelope->all(); // 押されているスタンプを全て取得

メッセージを受け取るMessageHandler

Symfonyでメッセージを受け取るって処理をする場合、MessageHandlerクラスを作ってその中に処理を記述します。bin/console make:messageでメッセージを作ると同時にこのハンドラーも自動生成されます。ここもオートワイヤリング可能なので、必要なものをDIできます。

SendMessageHandler.php

use App\Message\SendMailMessage;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;

final class SendMailMessageHandler implements MessageHandlerInterface
{
    public function __construct(private readonly MailerInterface $mailer)
    {
    }

    public function __invoke(SendMailMessage $message)
    {
        // メッセージを受け取って行う処理
        $item = $message->getItem();
        ...
    }
}

非同期(あとで処理)にする

デフォルトはメッセージをすぐに送信しますが、非同期に設定することもできます。asyncオプションを指定し、どのサービスプロバイダを使ってキューを貯めるか指定します。

config/packages/messanger.yaml
framework:
    messenger:
        transports:
            async: "%env(MESSENGER_TRANSPORT_DSN)%"
.env
# AMQP
MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
# DB
MESSENGER_TRANSPORT_DSN=doctrine://default
# Redis
MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages

まとめ

今回はMessengerを紹介しました。実際に使う場合は非同期で本処理が終了した後で行うような処理を実行することが多いと思います。
MessengerEventDispatcherをうまく活用して、きれいにコンテキストを分けるときに便利です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?