PHP/LaravelでFCMpush通知を実装した件

こんばんは！
アドベントカレンダー4日目を担当します、サーバーサイドエンジニアの@takapon21です
よろしくお願いします。


今回は、**PHP/Laravelを使用してFCM Push通知を送信する**
という実装についてのまとめを記載したいと思います。  

firebase公式でサポートしてくれないPHP/Laravelを用いてどうしても実装したいんだ！！
という方向けに書いていきます。
実際私も参考文献が乏しすぎて苦労しました。備忘録に近いです




## やりたいこと
**LaravelからiOSデバイス、Androidデバイスにpush通知を送りたい**
今回はWeb Push通知ではなく（googleカレンダーが10分前くらいに会議を教えてくれるアレ）
Laravelを起点として、FCMを利用し、google, appleが提供するpush通知サーバーを経由して各デバイスにpush通知を送信する
という実装をします。


## 今回省略すること
push通知を送信するために、**デバイスのトークン**が必要です
このトークンは、「誰に送信するか」を指定するために使用します。
ネイティブアプリケーション側の仕事なので、「トークンはもう取得している」前提で書いていきます





では、始めていきましょう！


## ① firebase側の準備
FCMを利用する旨の設定をしてください。
この辺の細かい設定は省略しますが、やってほしいことは全部で以下の2つだけです！！

1. （Appleアプリを作成される方）、**APNs認証キー又はAPNs証明書**をアップロードしてください
2. サービスアカウントから秘密鍵（project_name-firebase-adminsdk-hogefuga.json）をダウンロードしてください

ここをスキップしますと、以降の作業をいくら頑張ってもpush通知は送信できません



## ② 秘密鍵を開いてみる
先ほどダウンロードしたサービスアカウントの秘密鍵を覗いてみましょう
これは何度も言いますが、秘密鍵は重要な秘匿情報に分類しますので、生でgithubにアップロードしないようにご注意ください。（AWSのパラメータストアなど使用しましょう）


```json:project_name-firebase-adminsdk-hogefuga.json
{
  "type": "service_account",
  "project_id": "",
  "private_key_id": "",
  "private_key": "-----BEGIN PRIVATE KEY----- yeeeeeeeeeeeah -----END PRIVATE KEY-----\n",
  "client_email": "firebase-adminsdk@my-project.iam.gserviceaccount.com",
  "client_id": "123454321",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-my-project.iam.gserviceaccount.com",
  "universe_domain": "googleapis.com"
}
```

このようなjsonファイルが取得できましたらOKとなります。
何度も言いますが、Laravel側でconfigファイルやenvファイルに記載する際は生データをコミットかけないようにご注意ください


## ③ push通知を飛ばすエンドポイントを作る
下記のコードを環境変数として登録してください。
こちらのエンドポイントにpush通知をリクエストすることになります
```php
https://fcm.googleapis.com/v1/projects/${PROJECT_NAME}/messages:send
```
プロジェクト名で置き換えて、登録します。
何度も言いますが、秘匿情報は生でコミットかけないようにご注意ください

:::note
以降
env: PUSH_API
config: push_api => env('PUSH_API')
として活用していきます
:::



## ④ Laravel側の実装をしていきます
今回はHTTP v1 APIを利用した実装をしていきます。

よくwebで「Laravel FCM push通知」と調べますと、ライブラリを利用した実装などをお見受けしますが、このHTTP v1 API に対応できていないものが含まれていますのでご注意ください。

上記のような実装でも動かないこともないですが、
公式から発表されていますように**いつ廃止されるか分かりません**のでご注意くださいませ

HTTP v1 APIについて
https://firebase.google.com/docs/cloud-messaging/migrate-v1?hl=ja


v1に以降することでOAuth2.0アクセストークンが認証で必要になります。
公式ドキュメントでは[この辺です](https://firebase.google.com/docs/cloud-messaging/migrate-v1?hl=ja#update-authorization-of-send-requests)

これを突破するために、Google Clientライブラリを使用します

### Google APIClientライブラリのインストール

使用ライブラリ: https://github.com/googleapis/google-api-php-client

まずは下記のコマンドを入力してGoogle API Clientライブラリをインストールしましょう

```shell
$ composer require google/apiclient:^2.15.0
```

### envの準備
先ほどダウンロードしたjsonファイルをenvに登録していきます
下記はキー名の例として書いておきます
```php: .env

// 認証関係
TYPE=""
PROJECT_ID=""
PRIVATE_KEY_ID=""
PRIVATE_KEY=""
CLIENT_EMAIL=""
CLIENT_ID=""
AUTH_URI=""
TOKEN_URI=""
AUTH_PROVIDER_X509_CERT_URL=""
CLIENT_X509_CERT_URL=""

// FCMエンドポイント
PUSH_API=""
```


### configの準備
環境変数を使用しますので、configファイルも作成しておきましょう
今回はfirebase.phpを新規作成しました
```php: firebase.php
return [
    'auth' => [
        'type' => env('TYPE'),
        'project_id' => env('PROJECT_ID'),
        'private_key_id' => env('PRIVATE_KEY_ID'),
        'private_key' => env('PRIVATE_KEY'),
        'client_email' => env('CLIENT_EMAIL'),
        'client_id' => env('CLIENT_ID'),
        'auth_uri' => env('AUTH_URI'),
        'token_uri' => env('TOKEN_URI'),
        'auth_provider_x509_cert_url' => env('AUTH_PROVIDER_X509_CERT_URL'),
        'client_x509_cert_url' => env('CLIENT_X509_CERT_URL'),
    ],
    'push_api' => env('PUSH_API'),
];
```


### push通知を実装していきます
完成形です
```php: PushNotification.php

public function notify(string $token, string $title, string $messageBody): void
{
    $googleClient = new Google_client;
    $googleClient->useApplicationDefaultCredentials();

    // 先ほど設定した認証情報を用います
    $googleClient->setAuthConfig(config('firebase.auth'));

    // FCMにアクセスするためのスコープを追加します
    $googleClient->addScope('https://www.googleapis.com/auth/firebase.messaging');

    // ここで認証されたHTTPクライアントを取得
    $httpClient = $googleClient->authorize();


    // テストで送信するメッセージを作成します
    $title = "試しに送信するタイトル";
    $messageBody = "試しに送信するメッセージ本文";

    // 送信するメッセージの本体
    $data = [
        'message' => [
            // 送信対象者（1名分だけ有効です）
            'token' => $token,
            'notification' => [
                'body' => $title,
                'title' => $messageBody,
            ],
            'apns' => [
                'payload' => [
                    'aps' => [
                        'alert' => [
                            'title' => $title,
                            'body' => $messageBody
                        ]
                    ]
                ]
            ],
            "android" => [
                "priority" => "high",
                "notification" => [
                    "sound" => "default",
                ]
            ],
        ]
    ];

    // FCMに向けてPOSTリクエストを飛ばします
    $httpClient->post(config('firebase.push_api'), ['json' => $data]);
}
```


メッセージ本文の構成はこれ以外にもオプションがつけられます
https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ja
こちらからご確認くださいませ


## 試しに動かしてみる
![スクリーンショット 2023-11-25 23.34.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1422262/33802daf-6304-8354-0311-552f94d5d2ef.png)


はい、push通知の受信に成功しました！




いかがでしたか？
firebaseのFCMを利用する
↓
PHPは公式でサポートされていない！！詰んだ！！

で諦めず、Google API Clientを使用する
という選択肢が新しく増えたのではないでしょうか？

ぜひこのコードをベースとして参考にしていただけますと幸甚です


### 参考文献

https://cam-inc.co.jp/p/techblog/461059438439040041