Pushing Data to Clients Using the Mercure Protocol

Being able to broadcast data in real-time from servers to clients is a requirement for many modern web and mobile applications.

サーバーからクライアントにリアルタイムでデータをブロードキャストできることは、多くの最新の Web およびモバイル アプリケーションの要件です。

Creating a UI reacting in live to changes made by other users (e.g. a user changes the data currently browsed by several other users, all UIs are instantly updated), notifying the user when an asynchronous job has been completed or creating chat applications are among the typical use cases requiring "push" capabilities.

他のユーザーが行った変更にライブで反応する UI を作成する (たとえば、ユーザーが他の複数のユーザーによって現在閲覧されているデータを変更すると、すべての UI が即座に更新される)、非同期ジョブが完了したときにユーザーに通知する、またはチャット アプリケーションを作成することが一般的です。 「プッシュ」機能を必要とするユースケース。

Symfony provides a straightforward component, built on top of the Mercure protocol, specifically designed for this class of use cases.

Symfony は、Mercure プロトコルの上に構築された、このクラスのユースケース用に特別に設計された単純なコンポーネントを提供します。

Mercure is an open protocol designed from the ground up to publish updates from server to clients. It is a modern and efficient alternative to timer-based polling and to WebSocket.

Mercure は、サーバーからクライアントに更新を公開するためにゼロから設計されたオープン プロトコルです。これは、タイマーベースのポーリングや WebSocket に代わる最新の効率的な代替手段です。

Because it is built on top Server-Sent Events (SSE), Mercure is supported out of the box in modern browsers (old versions of Edge and IE require a polyfill) and has high-level implementations in many programming languages.

Mercure は最上位の Server-Sent Events (SSE) 上に構築されているため、最新のブラウザー (古いバージョンの Edge と IE ではポリフィルが必要) ですぐにサポートされ、多くのプログラミング言語で高レベルの実装が行われています。

Mercure comes with an authorization mechanism, automatic reconnection in case of network issues with retrieving of lost updates, a presence API, "connection-less" push for smartphones and auto-discoverability (a supported client can automatically discover and subscribe to updates of a given resource thanks to a specific HTTP header).

Mercure には、承認メカニズム、失われた更新の取得によるネットワークの問題の場合の自動再接続、プレゼンス API、スマートフォン用の「コネクションレス」プッシュ、および自動検出機能 (サポートされているクライアントは、特定のリソースの更新を自動的に検出してサブスクライブできます。特定の HTTP ヘッダー)。

All these features are supported in the Symfony integration.

これらの機能はすべて Symfony 統合でサポートされています。

In this recording you can see how a Symfony web API leverages Mercure and API Platform to update in live a React app and a mobile app (React Native) generated using the API Platform client generator.

この記録では、Symfony Web API が Mercure と API プラットフォームを活用して、API プラットフォーム クライアント ジェネレーターを使用して生成された React アプリとモバイル アプリ (React Native) をライブで更新する方法を確認できます。

Installation

Installing the Symfony Bundle

Run this command to install the Mercure support:

次のコマンドを実行して、Mercure サポートをインストールします。
1
$ composer require mercure

To manage persistent connections, Mercure relies on a Hub: a dedicated server that handles persistent SSE connections with the clients. The Symfony app publishes the updates to the hub, that will broadcast them to clients.

永続的な接続を管理するために、Mercure はハブ (クライアントとの永続的な SSE 接続を処理する専用サーバー) に依存しています。Symfony アプリは更新をハブに公開し、クライアントにブロードキャストします。

Thanks to the Docker integration of Symfony, Flex proposes to install a Mercure hub. Run docker-compose up to start the hub if you have chosen this option.

Symfony の Docker 統合のおかげで、Flex は Mercure ハブのインストールを提案します。このオプションを選択した場合は、docker-compose up を実行してハブを起動します。

If you use the Symfony Local Web Server, you must start it with the --no-tls option.

Symfony ローカル Web サーバーを使用する場合は、 --no-tls オプションを指定して開始する必要があります。
1
$ symfony server:start --no-tls -d

Running a Mercure Hub

If you use the Docker integration, a hub is already up and running, and you can go straight to the next section.

Docker 統合を使用する場合、ハブは既に稼働中であり、次のセクションに直接進むことができます。

Otherwise, and in production, you have to install a hub by yourself. An official and open source (AGPL) Hub based on the Caddy web server can be downloaded as a static binary from Mercure.rocks. A Docker image, a Helm chart for Kubernetes and a managed, High Availability Hub are also provided.

それ以外の場合、実稼働環境では、自分でハブをインストールする必要があります。Caddy Web サーバーに基づく公式のオープン ソース (AGPL) ハブは、Mercure.rocks から静的バイナリとしてダウンロードできます。Docker イメージ、Kubernetes の Helm チャートおよび管理された高可用性ハブも提供されます。

Configuration

The preferred way to configure MercureBundle is using environment variables.

MercureBundle を構成するための推奨される方法は、環境変数を使用することです。

When MercureBundle has been installed, the .env file of your project has been updated by the Flex recipe to include the available env vars.

MercureBundle がインストールされると、プロジェクトの .env ファイルが Flex レシピによって更新され、利用可能な環境変数が含まれるようになります。

Also, if you are using the Docker integration with the Symfony Local Web Server, Symfony Docker or the API Platform distribution, the proper environment variables have been automatically set. Skip straight to the next section.

また、Symfony Local Web Server、Symfony Docker、または API Platform ディストリビューションとの Docker 統合を使用している場合は、適切な環境変数が自動的に設定されています。スキップして次のセクションに進んでください。

Otherwise, set the URL of your hub as the value of the MERCURE_URL and MERCURE_PUBLIC_URL env vars. Sometimes a different URL must be called by the Symfony app (usually to publish), and the JavaScript client (usually to subscribe). It's especially common when the Symfony app must use a local URL and the client-side JavaScript code a public one. In this case, MERCURE_URL must contain the local URL used by the Symfony app (e.g. https://mercure/.well-known/mercure), and MERCURE_PUBLIC_URL the publicly available URL (e.g. https://example.com/.well-known/mercure).

それ以外の場合は、ハブの URL を MERCURE_URL および MERCURE_PUBLIC_URL 環境変数の値として設定します。Symfony アプリ (通常は公開するため) と JavaScript クライアント (通常は購読するため) によって別の URL を呼び出す必要がある場合があります。これは、Symfony アプリがローカル URL を使用し、クライアント側の JavaScript コードがパブリック URL を使用する必要がある場合に特に一般的です。この場合、MERCURE_URL には、Symfony アプリで使用されるローカル URL を含める必要があります (例: https://mercure/.well-known/mercure )、および MERCURE_PUBLIC_URL 公開されている URL (例: https://example.com/.well-known/mercure)。

The clients must also bear a JSON Web Token (JWT) to the Mercure Hub to be authorized to publish updates and, sometimes, to subscribe.

クライアントは、Mercure Hub への JSON Web トークン (JWT) を保持して、更新の公開と、場合によっては購読を承認する必要もあります。

This token must be signed with the same secret key as the one used by the Hub to verify the JWT (!ChangeThisMercureHubJWTSecretKey! if you use the Docker integration). This secret key must be stored in the MERCURE_JWT_SECRET environment variable. MercureBundle will use it to automatically generate and sign the needed JWTs.

このトークンは、Hub が JWT を検証するために使用するものと同じ秘密鍵で署名する必要があります (!ChangeThisMercureHubJWTSecretKey! Docker 統合を使用する場合)。この秘密鍵は MERCURE_JWT_SECRET 環境変数に格納する必要があります。必要な JWT を自動的に生成して署名します。

In addition to these environment variables, MercureBundle provides a more advanced configuration:

これらの環境変数に加えて、MercureBundle はより高度な構成を提供します。
  • secret: the key to use to sign the JWT - A key of the same size as the hash output (for instance, 256 bits for "HS256") or larger MUST be used. (all other options, beside algorithm, subscribe, and publish will be ignored)
    secret: JWT の署名に使用する鍵 - ハッシュ出力と同じサイズ (たとえば、「HS256」の場合は 256 ビット) またはそれ以上の鍵を使用する必要があります。 (アルゴリズム、サブスクライブ、パブリッシュ以外のすべてのオプションは無視されます)
  • publish: a list of topics to allow publishing to when generating the JWT (only usable when secret, or factory are provided)
    publish: JWT の生成時にパブリッシュを許可するトピックのリスト (シークレットまたはファクトリが提供されている場合にのみ使用可能)
  • subscribe: a list of topics to allow subscribing to when generating the JWT (only usable when secret, or factory are provided)
    subscribe: JWT の生成時にサブスクライブできるトピックのリスト (secret または factory が提供されている場合にのみ使用可能)
  • algorithm: The algorithm to use to sign the JWT (only usable when secret is provided)
    algorithm: JWT の署名に使用するアルゴリズム (シークレットが提供されている場合にのみ使用可能)
  • provider: The ID of a service to call to provide the JWT (all other options will be ignored)
    provider: JWT を提供するために呼び出すサービスの ID (他のすべてのオプションは無視されます)
  • factory: The ID of a service to call to create the JWT (all other options, beside subscribe, and publish will be ignored)
    factory: JWT を作成するために呼び出すサービスの ID (subscribe と publish 以外のすべてのオプションは無視されます)
  • value: the raw JWT to use (all other options will be ignored)
    値: 使用する生の JWT (他のすべてのオプションは無視されます)
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
# config/packages/mercure.yaml
mercure:
    hubs:
        default:
            url: https://mercure-hub.example.com/.well-known/mercure
            jwt:
                secret: '!ChangeThisMercureHubJWTSecretKey!'
                publish: ['foo', 'https://example.com/foo']
                subscribe: ['bar', 'https://example.com/bar']
                algorithm: 'hmac.sha256'
                provider: 'My\Provider'
                factory: 'My\Factory'
                value: 'my.jwt'

Tip

ヒント

The JWT payload must contain at least the following structure for the client to be allowed to publish:

クライアントが公開できるようにするには、JWT ペイロードに少なくとも次の構造が含まれている必要があります。
1
2
3
4
5
{
    "mercure": {
        "publish": []
    }
}

Because the array is empty, the Symfony app will only be authorized to publish public updates (see the authorization section for further information).

配列が空であるため、Symfony アプリは公開更新の公開のみを承認されます (詳細については、承認セクションを参照してください)。

The jwt.io website is a convenient way to create and sign JWTs. Checkout this example JWT, that grants publishing rights for all topics (notice the star in the array). Don't forget to set your secret key properly in the bottom of the right panel of the form!

jwt.io Web サイトは、JWT を作成して署名するための便利な方法です。すべてのトピックの公開権を付与するこのサンプル JWT を確認してください (配列内の星に注意してください)。フォームの右パネル!

Basic Usage

Publishing

The Mercure Component provides an Update value object representing the update to publish. It also provides a Publisher service to dispatch updates to the Hub.

Mercure コンポーネントは、公開する更新を表す Update 値オブジェクトを提供します。また、更新をハブにディスパッチするパブリッシャー サービスも提供します。

The Publisher service can be injected using the autowiring in any other service, including controllers:

パブリッシャー サービスは、コントローラーを含む他のサービスでオートワイヤリングを使用して挿入できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/Controller/PublishController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;

class PublishController extends AbstractController
{
    public function publish(HubInterface $hub): Response
    {
        $update = new Update(
            'https://example.com/books/1',
            json_encode(['status' => 'OutOfStock'])
        );

        $hub->publish($update);

        return new Response('published!');
    }
}

The first parameter to pass to the Update constructor is the topic being updated. This topic should be an IRI (Internationalized Resource Identifier, RFC 3987): a unique identifier of the resource being dispatched.

Update コンストラクターに渡す最初のパラメーターは、更新されるトピックです。このトピックは、IRI (Internationalized Resource Identifier、RFC 3987) である必要があります。つまり、ディスパッチされるリソースの一意の識別子です。

Usually, this parameter contains the original URL of the resource transmitted to the client, but it can be any string or IRI, and it doesn't have to be a URL that exists (similarly to XML namespaces).

通常、このパラメーターには、クライアントに送信されるリソースの元の URL が含まれますが、任意の文字列または IRI にすることができ、存在する URL である必要はありません (XML 名前空間と同様)。

The second parameter of the constructor is the content of the update. It can be anything, stored in any format. However, serializing the resource in a hypermedia format such as JSON-LD, Atom, HTML or XML is recommended.

コンストラクターの 2 番目のパラメーターは、更新の内容です。これは、任意の形式で保存されたものであれば何でもかまいません。ただし、JSON-LD、Atom、HTML、または XML などのハイパーメディア形式でリソースをシリアル化することをお勧めします。

Subscribing

Subscribing to updates in JavaScript from a Twig template is straightforward:

Twig テンプレートから JavaScript で更新をサブスクライブするのは簡単です。
1
2
3
4
5
6
7
<script>
const eventSource = new EventSource("{{ mercure('https://example.com/books/1')|escape('js') }}");
eventSource.onmessage = event => {
    // Will be called every time an update is published by the server
    console.log(JSON.parse(event.data));
}
</script>

The mercure() Twig function generates the URL of the Mercure hub according to the configuration. The URL includes the topic query parameters corresponding to the topics passed as first argument.

mercure() Twig 関数は、構成に従って Mercure ハブの URL を生成します。 URL には、最初の引数として渡されたトピックに対応するトピック クエリ パラメータが含まれます。

If you want to access to this URL from an external JavaScript file, generate the URL in a dedicated HTML element:

外部 JavaScript ファイルからこの URL にアクセスする場合は、専用の HTML 要素で URL を生成します。
1
2
3
<script type="application/json" id="mercure-url">
{{ mercure('https://example.com/books/1')|json_encode(constant('JSON_UNESCAPED_SLASHES') b-or constant('JSON_HEX_TAG'))|raw }}
</script>

Then retrieve it from your JS file:

次に、JS ファイルから取得します。
1
2
3
const url = JSON.parse(document.getElementById("mercure-url").textContent);
const eventSource = new EventSource(url);
// ...

Mercure also allows subscribing to several topics, and to use URI Templates or the special value * (matched by all topics) as patterns:

Mercure では、複数のトピックをサブスクライブし、URI テンプレートまたは特別な値 * (すべてのトピックに一致) をパターンとして使用することもできます。
1
2
3
4
5
6
7
8
9
10
11
12
<script>
{# Subscribe to updates of several Book resources and to all Review resources matching the given pattern #}
const eventSource = new EventSource("{{ mercure([
    'https://example.com/books/1',
    'https://example.com/books/2',
    'https://example.com/reviews/{id}'
])|escape('js') }}");

eventSource.onmessage = event => {
    console.log(JSON.parse(event.data));
}
</script>

Tip

ヒント

Google Chrome DevTools natively integrate a practical UI displaying in live the received events:

Google Chrome DevTools は、受信したイベントをライブで表示する実用的な UI をネイティブに統合します。

To use it:

使用するには:
  • open the DevTools
    開発ツールを開く
  • select the "Network" tab
    「ネットワーク」タブを選択
  • click on the request to the Mercure hub
    Mercure ハブへのリクエストをクリックします
  • click on the "EventStream" sub-tab.
    「EventStream」サブタブをクリックします。

Tip

ヒント

Test if a URI Template match a URL using the online debugger

オンライン デバッガーを使用して URI テンプレートが URL と一致するかどうかをテストする

Discovery

The Mercure protocol comes with a discovery mechanism. To leverage it, the Symfony application must expose the URL of the Mercure Hub in a Link HTTP header.

Mercure プロトコルには検出メカニズムが付属しています。これを利用するには、Symfony アプリケーションは Mercure Hubin の URL を Link HTTP ヘッダーで公開する必要があります。

You can create Link headers with the Discovery helper class (under the hood, it uses the WebLink Component):

Discovery ヘルパー クラスを使用して Link ヘッダーを作成できます (内部では、WebLink コンポーネントを使用します)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// src/Controller/DiscoverController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mercure\Discovery;

class DiscoverController extends AbstractController
{
    public function discover(Request $request, Discovery $discovery): JsonResponse
    {
        // Link: <https://hub.example.com/.well-known/mercure>; rel="mercure"
        $discovery->addLink($request);

        return $this->json([
            '@id' => '/books/1',
            'availability' => 'https://schema.org/InStock',
        ]);
    }
}

Then, this header can be parsed client-side to find the URL of the Hub, and to subscribe to it:

次に、このヘッダーをクライアント側で解析して、ハブの URL を見つけ、それにサブスクライブできます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Fetch the original resource served by the Symfony web API
fetch('/books/1') // Has Link: <https://hub.example.com/.well-known/mercure>; rel="mercure"
    .then(response => {
        // Extract the hub URL from the Link header
        const hubUrl = response.headers.get('Link').match(/<([^>]+)>;\s+rel=(?:mercure|"[^"]*mercure[^"]*")/)[1];

        // Append the topic(s) to subscribe as query parameter
        const hub = new URL(hubUrl, window.origin);
        hub.searchParams.append('topic', 'https://example.com/books/{id}');

        // Subscribe to updates
        const eventSource = new EventSource(hub);
        eventSource.onmessage = event => console.log(event.data);
    });

Authorization

Mercure also allows dispatching updates only to authorized clients. To do so, mark the update as private by setting the third parameter of the Update constructor to true:

Mercure では、許可されたクライアントにのみ更新をディスパッチすることもできます。これを行うには、Update コンストラクターの 3 番目のパラメーターを true に設定して、更新をプライベートとしてマークします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// src/Controller/Publish.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\Update;

class PublishController extends AbstractController
{
    public function publish(HubInterface $hub): Response
    {
        $update = new Update(
            'https://example.com/books/1',
            json_encode(['status' => 'OutOfStock']),
            true // private
        );

        // Publisher's JWT must contain this topic, a URI template it matches or * in mercure.publish or you'll get a 401
        // Subscriber's JWT must contain this topic, a URI template it matches or * in mercure.subscribe to receive the update
        $hub->publish($update);

        return new Response('private update published!');
    }
}

To subscribe to private updates, subscribers must provide to the Hub a JWT containing a topic selector matching by the topic of the update.

非公開の更新をサブスクライブするには、サブスクライバーは、更新のトピックに一致するトピック セレクターを含む Huba JWT に提供する必要があります。

To provide this JWT, the subscriber can use a cookie, or an Authorization HTTP header.

この JWT を提供するために、サブスクライバーは Cookie または Authorization HTTP ヘッダーを使用できます。

Cookies can be set automatically by Symfony by passing the appropriate options to the mercure() Twig function. Cookies set by Symfony are automatically passed by the browsers to the Mercure hub if the withCredentials attribute of the EventSource class is set to true. Then, the Hub verifies the validity of the provided JWT, and extract the topic selectors from it.

Cookie は、適切なオプションを mercure() Twig 関数に渡すことで、Symfony によって自動的に設定されます。 EventSource クラスの withCredentials 属性が true に設定されている場合、Symfony によって設定された Cookie は、ブラウザーによって自動的に Mercure ハブに渡されます。次に、ハブは提供された JWT の有効性を検証し、そこからトピック セレクターを抽出します。
1
2
3
4
5
<script>
const eventSource = new EventSource("{{ mercure('https://example.com/books/1', { subscribe: 'https://example.com/books/1' })|escape('js') }}", {
    withCredentials: true
});
</script>

The supported options are:

サポートされているオプションは次のとおりです。
  • subscribe: the list of topic selectors to include in the mercure.subscribe claim of the JWT
    subscribe: JWT の mercure.subscribe クレームに含めるトピック セレクターのリスト
  • publish: the list of topic selectors to include in the mercure.publish claim of the JWT
    publish: JWT の mercure.publish クレームに含めるトピック セレクターのリスト
  • additionalClaims: extra claims to include in the JWT (expiration date, token ID...)
    additionalClaims: JWT に含める追加のクレーム (有効期限、トークン ID...)

Using cookies is the most secure and preferred way when the client is a web browser. If the client is not a web browser, then using an authorization header is the way to go.

クライアントが Web ブラウザの場合、Cookie を使用するのが最も安全で好ましい方法です。クライアントが Web ブラウザーでない場合は、認証ヘッダーを使用する方法があります。

Caution

注意

To use the cookie authentication method, the Symfony app and the Hub must be served from the same domain (can be different sub-domains).

Cookie 認証方式を使用するには、Symfony アプリと Hub が同じドメイン (異なるサブドメインであってもかまいません) から提供されている必要があります。

Tip

ヒント

The native implementation of EventSource doesn't allow specifying headers. For example, authorization using a Bearer token. In order to achieve that, use a polyfill

EventSource のネイティブ実装では、ヘッダーを指定できません。たとえば、ベアラー トークンを使用した承認です。それを実現するには、ポリフィルを使用します
1
2
3
4
5
6
7
<script>
const es = new EventSourcePolyfill("{{ mercure('https://example.com/books/1') }}", {
    headers: {
        'Authorization': 'Bearer ' + token,
    }
});
</script>

Sometimes, it can be convenient to set the authorization cookie from your code instead of using the Twig function. MercureBundle provides a convenient service, Authorization, to do so.

Twig 関数を使用する代わりに、コードから認証 Cookie を設定すると便利な場合があります。 MercureBundle は、これを行うための便利なサービス、Authorization を提供します。

In the following example controller, the added cookie contains a JWT, itself containing the appropriate topic selector.

次の例のコントローラーでは、追加された Cookie に JWT が含まれており、それ自体に適切なトピック セレクターが含まれています。

And here is the controller:

そして、ここにコントローラーがあります:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/Controller/DiscoverController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mercure\Authorization;
use Symfony\Component\Mercure\Discovery;

class DiscoverController extends AbstractController
{
    public function publish(Request $request, Discovery $discovery, Authorization $authorization): JsonResponse
    {
        $discovery->addLink($request);
        $authorization->setCookie($request, ['https://example.com/books/1']);

        return $this->json([
            '@id' => '/demo/books/1',
            'availability' => 'https://schema.org/InStock'
        ]);
    }
}

Tip

ヒント

You cannot use the mercure() helper and the setCookie() method at the same time (it would set the cookie twice on a single request). Choose either one method or the other.

mercure() ヘルパーと setCookie() メソッドを同時に使用することはできません (1 回のリクエストで Cookie が 2 回設定されます)。いずれかの方法を選択してください。

Programmatically Generating The JWT Used to Publish

Instead of directly storing a JWT in the configuration, you can create a token provider that will return the token used by the HubInterface object:

JWT を構成に直接格納する代わりに、HubInterface オブジェクトによって使用されるトークンを返すトークン プロバイダーを作成できます。
1
2
3
4
5
6
7
8
9
10
11
12
// src/Mercure/MyTokenProvider.php
namespace App\Mercure;

use Symfony\Component\Mercure\Jwt\TokenProviderInterface;

final class MyTokenProvider implements TokenProviderInterface
{
    public function getJwt(): string
    {
        return 'the-JWT';
    }
}

Then, reference this service in the bundle configuration:

次に、バンドル構成でこのサービスを参照します。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
# config/packages/mercure.yaml
mercure:
    hubs:
        default:
            url: https://mercure-hub.example.com/.well-known/mercure
            jwt:
                provider: App\Mercure\MyTokenProvider

This method is especially convenient when using tokens having an expiration date, that can be refreshed programmatically.

この方法は、有効期限があり、プログラムで更新できるトークンを使用する場合に特に便利です。

Web APIs

When creating a web API, it's convenient to be able to instantly push new versions of the resources to all connected devices, and to update their views.

Web API を作成する場合、リソースの新しいバージョンを接続されているすべてのデバイスに即座にプッシュし、それらのビューを更新できると便利です。

API Platform can use the Mercure Component to dispatch updates automatically, every time an API resource is created, modified or deleted.

API プラットフォームは、Mercure コンポーネントを使用して、API リソースが作成、変更、または削除されるたびに更新を自動的にディスパッチできます。

Start by installing the library using its official recipe:

公式レシピを使用してライブラリをインストールすることから始めます。
1
$ composer require api

Then, creating the following entity is enough to get a fully-featured hypermedia API, and automatic update broadcasting through the Mercure hub:

次に、次のエンティティを作成するだけで、フル機能のハイパーメディア API と、Mercure ハブを介した自動更新ブロードキャストを取得できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/Entity/Book.php
namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;

#[ApiResource(mercure: true)]
#[ORM\Entity]
class Book
{
    #[ORM\Id]
    #[ORM\Column]
    public string $name = '';

    #[ORM\Column]
    public string $status = '';
}

As showcased in this recording, the API Platform Client Generator also allows to scaffold complete React and React Native applications from this API. These applications will render the content of Mercure updates in real-time.

このレコーディングで紹介されているように、API Platform Client Generator では、この API から完全な React および React Native アプリケーションをスキャフォールディングすることもできます。これらのアプリケーションは、Mercure アップデートのコンテンツをリアルタイムでレンダリングします。

Checkout the dedicated API Platform documentation to learn more about its Mercure support.

Mercure サポートの詳細については、専用の API プラットフォーム ドキュメントをご覧ください。

Testing

During unit testing it's usually not needed to send updates to Mercure.

単体テスト中は、通常、更新を Mercure に送信する必要はありません。

You can instead make use of the `MockHub` class:

代わりに `MockHub` クラスを利用できます:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// tests/FunctionalTest.php
namespace App\Tests\Unit\Controller;

use App\Controller\MessageController;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\JWT\StaticTokenProvider;
use Symfony\Component\Mercure\MockHub;
use Symfony\Component\Mercure\Update;

class MessageControllerTest extends TestCase
{
    public function testPublishing()
    {
        $hub = new MockHub('https://internal/.well-known/mercure', new StaticTokenProvider('foo'), function(Update $update): string {
            // $this->assertTrue($update->isPrivate());

            return 'id';
        });

        $controller = new MessageController($hub);

        // ...
    }
}

For functional testing, you can instead create a stub of the Hub:

機能テストのために、代わりにハブのスタブを作成できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// tests/Functional/Stub/HubStub.php
namespace App\Tests\Functional\Stub;

use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;

class HubStub implements HubInterface
{
    public function publish(Update $update): string
    {
        return 'id';
    }

    // implement rest of HubInterface methods here
}

Use HubStub to replace the default hub service so no updates are actually sent:

HubStub を使用してデフォルトのハブ サービスを置き換え、更新が実際に送信されないようにします。
1
2
3
# config/services_test.yaml
mercure.hub.default:
    class: App\Tests\Functional\Stub\HubStub

As MercureBundle support multiple hubs, you may have to replace the other service definitions accordingly.

MercureBundle は複数のハブをサポートしているため、それに応じて他のサービス定義を置き換える必要がある場合があります。

Tip

ヒント

Symfony Panther has a feature to test applications using Mercure.

Symfony Panther には、Mercure を使用してアプリケーションをテストする機能があります。

Debugging

0.2

0.2

The WebProfiler panel was introduced in MercureBundle 0.2.

WebProfiler パネルは MercureBundle 0.2 で導入されました。

Enable the panel in your configuration, as follows:

次のように、構成でパネルを有効にします。

MercureBundle is shipped with a debug panel. Install the Debug pack to enable it:

MercureBundle にはデバッグ パネルが付属しています。デバッグ パックをインストールして有効にします。
1
$ composer require --dev symfony/debug-pack

Async dispatching

Tip

ヒント

Async dispatching is discouraged. Most Mercure hubs already handle publications asynchronously and using Messenger is usually not necessary.

非同期ディスパッチは推奨されません。ほとんどの Mercure ハブは既にパブリケーションを非同期で処理しており、Messenger を使用する必要は通常ありません。

Instead of calling the Publisher service directly, you can also let Symfony dispatching the updates asynchronously thanks to the provided integration with the Messenger component.

Publisher サービスを直接呼び出す代わりに、Messenger コンポーネントとの統合が提供されているため、Symfony に非同期で更新を送信させることもできます。

First, be sure to install the Messenger component and to configure properly a transport (if you don't, the handler will be called synchronously).

最初に、Messenger コンポーネントをインストールし、トランスポートを適切に構成してください (そうしないと、ハンドラーが同期的に呼び出されます)。

Then, dispatch the Mercure Update to the Messenger's Message Bus, it will be handled automatically:

次に、Mercure Update を Messenger のメッセージ バスにディスパッチします。これは自動的に処理されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// src/Controller/PublishController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Messenger\MessageBusInterface;

class PublishController extends AbstractController
{
    public function publish(MessageBusInterface $bus): Response
    {
        $update = new Update(
            'https://example.com/books/1',
            json_encode(['status' => 'OutOfStock'])
        );

        // Sync, or async (Doctrine, RabbitMQ, Kafka...)
        $bus->dispatch($update);

        return new Response('published!');
    }
}

Going further

  • The Mercure protocol is also supported by the Notifier component. Use it to send push notifications to web browsers.
    Mercure プロトコルは、Notifier コンポーネントでもサポートされています。これを使用して、Web ブラウザーにプッシュ通知を送信します。
  • Symfony UX Turbo is a library using Mercure to provide the same experience as with Single Page Applications but without having to write a single line of JavaScript!
    Symfony UX Turbo は、Mercure を使用してシングル ページ アプリケーションと同じエクスペリエンスを提供するライブラリですが、JavaScript を 1 行も書く必要はありません!