Laravel10 + FCM (Firebase Cloud Messaging) HTTP v1 でのWebプッシュ通知

# 概要
FCMでWebプッシュ通知を実装してみました。
Laravel10 + FCM HTTP v1 の記事が見当たらなかったため、
備忘録 + アウトプットとして残しておくことにしました。
至らぬ点がありましたらご指摘いただけますと幸いです。

# 前提
この記事ではHTTP v1の解説のため、Firebaseのアカウント作成やFirebaseプロジェクトの作成手順については説明しません。Firebaseアカウントが作成済みで、プロジェクトの設定が完了していることを前提に進めます。以下の設定が済んでいることが必要です。

:::note info
Firebaseコンソールでプロジェクトが作成されている。
Firebase Cloud Messaging (FCM) の設定が完了している。
フロントエンドでtokenの取得ができている
:::

参考にした記事

https://qiita.com/WYI/items/eda32337926899ea0e70

# Firebase Cloud Messaging (FCM)とは？
Firebase Cloud Messaging（FCM）は、Googleが提供する無料のクロスプラットフォームメッセージングソリューションで、iOS、Android、ウェブなどのデバイスに対して信頼性の高いメッセージや通知を送信できます。これにより、アプリケーション開発者はユーザーにリアルタイムで情報を提供し、エンゲージメントを高めることが可能です。

FCMを使用すると、同期可能な新しいメールやその他のデータがあることをクライアントアプリに通知できます。通知メッセージを送信することで、ユーザーにアプリを再度アピールし、定着率の向上につなげることができます。インスタントメッセージなどの用途では、メッセージで最大4,096バイトのペイロードをクライアントアプリに転送することも可能です。

https://firebase.google.com/docs/cloud-messaging?hl=ja&utm_source=chatgpt.com

# 秘密鍵jsonファイルの取得
サービス アカウント用の秘密鍵jsonファイルを取得します。
HTTP v1はサーバーキーではなく秘密鍵を使用してアクセストークンを取得します。

Firebaseコンソールで
プロジェクトの設定(歯車アイコン)＞サービスアカウント＞新しい秘密鍵を生成
で秘密鍵のjsonファイルをダウンロードできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3876674/edb4db36-85c9-8f3d-a727-674d358d2e21.png)

ダウンロードしたファイルをプロジェクトの任意のストレージパスに配置します。
(例)：strage\app\firebase\[ファイル名].json

# 実装
ServiceAccountCredentialsとGuzzle使用して実装します。

### アクセストークンの取得
さきほど取得した秘密鍵ファイルを使用してアクセストークンを取得します
```php
// サービスアカウントのJSONファイルのパス
$keyFilePath = storage_path('app/firebase/[jsonファイル名].json'); 
$scopes = ['https://www.googleapis.com/auth/firebase.messaging'];

$credentials = new ServiceAccountCredentials($scopes, $keyFilePath);
$accessToken = $credentials->fetchAuthToken()['access_token'];
```

### WEBプッシュ通知の送信
HTTP v1ではHTTP APIからメッセージのjsonの形式が変更されています。
以下公式のドキュメントを参考に実装します。

https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ja

```php
$fields = [
    'message' => [
        'token' => フロントエンドで取得したtoken,
        'notification' => [
            'title' => 'タイトル',
            'body' => 'メッセージ',
        ],
    ]
];
```

HTTP v1ではclick_actionがnotificationにないのでdataでの実装が必要です。(フロントエンドで自力実装の必要あり)
その他jsonについての詳細な仕様は公式のドキュメントを参照してください。

https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?hl=ja&_gl=1*6tpetf*_up*MQ..*_ga*NDEzODU5MTA5LjE3MzY4NjI3OTE.*_ga_CW55HF8NVT*MTczNjg2Mjc5MS4xLjAuMTczNjg2Mjc5MS4wLjAuMA..#Notification

全体は以下のような実装になります。
```php
use Google\Auth\Credentials\ServiceAccountCredentials;
use GuzzleHttp\Client;

private static function getAccessToken()
{
    // サービスアカウントのJSONファイルのパス
    $keyFilePath = storage_path('app/firebase/[jsonファイル名].json'); 
    $scopes = ['https://www.googleapis.com/auth/firebase.messaging'];

    $credentials = new ServiceAccountCredentials($scopes, $keyFilePath);
    return $credentials->fetchAuthToken()['access_token'];
}


/**
 * FCMを使用して通知を送信する
 *
 * @param string $token フロントエンドで取得した受信者のトークン
 * @return void
 */
public static function handle($token)
{
    $client = new Client();
    // プロジェクトIDを指定
    $fcm_endpoint = 'https://fcm.googleapis.com/v1/projects/[プロジェクトID]/messages:send';

    $accessToken = self::getAccessToken();

    $headers = [
        'Authorization' => 'Bearer ' . $accessToken,
        'Content-Type' => 'application/json'
    ];

    $fields = [
        'message' => [
            'token' => $token,
            'notification' => [
                'title' => $title,
                'body' => $content,
            ],
        ]
    ];

    // 通知の送信
    $client->post($fcm_endpoint, [
        'headers' => $headers,
        'json' => $fields
    ]);
}

```