プッシュ通知をクライアントに送る方法やプラグインはいくつかあるが、ある程度ノーコスト、高品質で実装するやり方として、Googleの提供するFirebaseを通して送る方法がある。
以前仕事でプッシュ通知のプログラムを組んだことがあるが、久々に触ってみたらFCMの仕様が少し変わっていた。
勉強のついでに、自分なりに過去のコードをリメイクしてみたので、Firebaseの提供するAPIとJS、PHPを使ってプッシュ通知を送るプログラムを組んでみたいと思う。
Table of Contents
FCMを使ってプッシュ通知を送る条件
Firebaseを使ってプッシュ通知を送るには下記の条件が必要になる。
- ①PWAによるアプリ化が完了していること
- ②各ファイルをルートに配置すること
- ③サイトがHTTPSで通信されていること(ローカルホストでは不可)
- ④Firebaseのプロジェクトをセットアップしておくこと
今回はコードだけではなく、上記の解説も行っていく。
Firebase側の準備
まずはFirebaseに登録し、プロジェクトを立ち上げてもらいたい。名前は何でもオッケーだ。
プロジェクトを立ち上げたら、左側にある「プロジェクトの概要」→「プロジェクトの設定」に移動する
このページの「全般」、「Cloud Messaging」のタブの中にある情報(送信者 IDやサーバーキー)などは後にコードを作成する中で使用することになるので、このページはブックマークしておこう。
サーバーキーがない場合
上記のページをみていると、サーバーキーがない、もしくは「Cloud Messaging API(レガシー)無効」となっていて見れない場合がある。
その場合はAPIを有効にする必要があるので、指示に従いアクティベートしよう。有効にするとサーバーキーが表示されるようになる。
FCM経由でプッシュ通知を送るためのコードを準備
ここからはコードの作成に移っていく。
冒頭でも説明したように、サイトをPWAでアプリ化しておく必要があるので、まだの人は下記を先にやってもらいたい。
index.php
まずはihndex.phpで基盤となる部分を整えていく。
自身のアプリとFirebaseを通信させるために、必ず初期化をさせる必要がある。
Firebaseプロジェクトの設定情報をオブジェクト「firebaseConfig」としてセットし、Firebaseプロジェクトとの接続を開始させよう。
<head>
<meta charset="utf-8">
<script src="https://corporate.t-creative-works.com/js/jquery-3.5.0.min.js"></script>
<link rel="manifest" href="manifest.json">
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js"></script>
</head>
<script src="push.js"></script>
<script>
// Firebase初期化設定 start
const firebaseConfig = { //firebaseのプロジェクトの設定→マイアプリ→SDK の設定と構成→configに表示されている内容をペースト
apiKey: "firebaseプロジェクトのapiキー",
authDomain: "authドメイン",
projectId: "プロジェクトID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "センダーID",
appId: "appID"
};
firebase.initializeApp(firebaseConfig); //Firebaseを初期化するための関数呼び出し
// Firebase初期化設定 end
// ServiceWorker登録
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js').then(function(registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
console.log('ServiceWorker registration failed: ', err);
});
}
</script>
headタグのfirebase.jsのスクリプトは、最新の9.1.0で試してみたが、エラーが起きたので8.10.0を使用する。
これはFirebaseのSDKバージョン9からは、モジュールバンドラーが必要となるため、exportキーワードが理解できず、トークンが取得できなかったからだ(詳しくは後ほど解説する)。
「// Firebase初期化設定 start」からendまでが主に今回追加してもらいたい箇所となっている。
- ①「initializeApp」はFirebase SDKが提供する関数の1つ
- ②「firebase.」は、Firebase SDK(Software Development Kit)を参照している。
- →SDKとは、Firebaseが提供する各種機能をJavaScriptで使用するためのライブラリの一部。
- ③もともと定義されているfirebaseというグローバルオブジェクトを通じてFirebaseの各種機能を使用できるイメージ
PWA化もしておく
プッシュ通知を実装するにはサイトをPWAでアプリ化する必要があるので、その部分もしっかり実装しておこう。
index.phpには「// ServiceWorker登録」以下の箇所、headタグにmanifest.jsを読み込んでもらえればオッケーだ。
PWAによるアプリ化に関しては以前の記事を参考にしてもらいたい。
push.jsでプッシュ通知の許可をユーザーに申請
ブラウザからユーザーに通知の許可を求め、ユーザーが許可すると、Firebase Cloud Messagingにトークンをリクエストする。
$(window).on('load', function () {
const messaging = firebase.messaging();
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
console.log('Notification permission granted.');
messaging.getToken({
vapidKey: "BBNwbVttm3uS3aDaHlJiiRk9jezyxn0YxjVk2P5eVO5VZUPl2MYX8-YqRO-znHtp4o_pYKGZG22-hoEbrIMmdtQ" // public VAPID key
}).then((currentToken) => {
if (currentToken) {
console.log('Token: ', currentToken);
// 確認用にHTMLに表示
document.getElementById('token-display').textContent = "Current token: " + currentToken;
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
});
} else {
console.log('Unable to get permission to notify.');
}
});
});
VAPID(鍵ペア)キーは、Firebaseに対してアプリケーションを一意に識別するために使用される。これはfirebaseのプロジェクトの設定→Cloud Messaging→ウェブの構成→鍵ペアに書かれているので、各々の鍵ペアを使用する
ユーザーからプッシュ通知の許可が下りたら、Firebase Cloud Messagingからトークンを取得できる仕組みになっている。
なにも問題なくプッシュ通知許可が得られれば、コンソール画面と画面下に「Token: aaaa……」のようにテスト用としてトークンが表示される。
firebase-messaging-sw.js
最後に「firebase-messaging-sw.js」でバックグラウンドでプッシュ通知を受信するための準備を行う。
このJSはService Workerと呼ばれるもので、バックグラウンドでネットワークリクエストを処理できるため、プッシュ通知を受信したりすることができるのだ。
簡単にいうと、ネットワークリクエストが発生したときにそのリクエストを「捕まえて」何らかの処理をリアルタイムに行うことができるということだ。
ちなみに、iondex.phpにはこのfirebase-messaging-sw.jsへのパスは書かれていないが、絶対パスで読み込んでいるFirebaseのSDK(firebase-messaging.js)が内部でこのファイルを参照するために使っているため、パスは通っていることになる。
importScripts('https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js');
firebase.initializeApp({
apiKey: "firebaseプロジェクトのapiキー",
authDomain: "authドメイン",
projectId: "プロジェクトID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "センダーID",
appId: "appID"
});
const messaging = firebase.messaging();//Firebase Cloud Messagingの機能を利用するためのメソッド
messaging.onBackgroundMessage((payload) => {//この関数はFirebaseが提供しているAPIで、バックグラウンドで受信したメッセージ(プッシュ通知)に対する処理を定義
//この関数は引数としてコールバック関数を受け取り、そのコールバック関数は受信したメッセージ(payload)を引数として実行する
//payloadはFirebaseから送られてくるメッセージの内容が格納されているので変更は不可
console.log('[firebase-messaging-sw.js] Received background message ', payload);//プッシュ通知の内容はコンソールロゴにも表示される
// 通知のカスタマイズ
const notificationTitle = payload.notification.title;//payload.notification.titleというプロパティはFirebaseが送信したメッセージ内の構造を表していて、これ自体を変更することはできない
const notificationOptions = {
body: payload.notification.body,
};
return self.registration.showNotification(//この部分はService Workerが提供するAPIで、ブラウザの通知を表示するためのもの。この関数に通知のタイトルとオプション(通知の本文など)を渡すことで、通知を表示
//self.registrationプロパティ=現在のService Workerの登録(Registration)を参照
//showNotification巻数=Notification APIのメソッドで、デスクトップ通知を表示
notificationTitle,
notificationOptions
);
});
- ①importScriptsはService Workerで外部スクリプトを読み込むためのもので、この関数を使うことでService Worker内で他のJavaScriptファイルを読み込むことができる。
上記のコードではFirebaseの基本的なアプリケーションとメッセージングを使用するためにスクリプトを読み込んでいる。 - ②再びFirebasaseを初期化する
index.phpではページを読み込んだときの初期化を行っているが、Service Workerは通知を受け取った時など「ブラウザのバックグラウンドで動作」する独立したスクリプトなので、ここでもFirebaseを初期化する必要があるのだ。 - ③firebase.messaging()はFirebaseが提供するメソッドで、Firebase Cloud Messagingの機能を利用するためのもだ。
これにより、メッセージを送信・受信するためのインターフェースを取得することが可能となる。 - ④messaging.onBackgroundMessage((payload) => { … })の部分では、バックグラウンドでメッセージが受信されたときの処理を定義している。
ここで受け取ったペイロード(メッセージの本体)から通知のタイトルと本文を取得し、それをもとに通知を表示する。
このコードにより、アプリがバックグラウンドの状態でもプッシュ通知を受け取って表示することが可能となる。
Firebase側のプロパティは変更できない
ちなみにだが、firebaseから「payload.notification.title」部分のプロパティは、もともとFirebase側で決められているものなので、この部分を直接変更することはできない。
恐らくjsonでかきのような構造になっていると予測できる。
{
"notification": {
"title": "タイトル",
"body": "ボディ"
},
"data": {
// メッセージと一緒に送りたい追加データ等
}
}
この辺りはfirebase-messaging-sw.jsの中で自分で定数などを指定して、臨機応変にカスタマイズしよう。
send.php
最後は送信を行うための処理をPHPで書いてく。
なお、トークンを入力する必要があるのだが、ここは先ほどpush.jsの部分で実装した通り、ユーザーが通知許可を出すことによってトークンがコンソールロゴで表示されるようになっている。
今回はあくまでも送信するまでのテストとして実装しているので、一旦それを張り付けてもらってオッケーだ。
<?php
//"registration_ids"はFirebase Cloud Messaging (FCM) のAPIにおいて、通知を送信するデバイスのトークンを指定するための配列(FCMによって予め用意されている)。
//1つのリクエストで最大で500個のデバイストークンを指定することが可能
$json = '{
"registration_ids": [
"トークンを入力",
],
"notification":
{
"title": "プッシュ通知テスト送信",
"body": "送信テストですの本文です",
"click_action":"/"
},
}';
$ch = curl_init();//PHPのライブラリのcURLを使ってAPIリクエスト(新しいcURLセッショ)を初期化して開始
$headers = array(
'Content-Type: application/json',
'Authorization: key=Firebaseからサーバーキーを取得'
//Authorization(サーバーキー)はFirebase Cloud Messagingへのリクエストを認証するために必要(FCMの管理ページ参照)
);
curl_setopt_array($ch, array(//URL、HTTPヘッダー('Content-Type'と'Authorization')、POSTデータ($json)はFirebaseのAPI仕様に従って設定する必要がある
CURLOPT_URL => 'https://fcm.googleapis.com/fcm/send',//cURLで要求するURLを指定’(Firebase Cloud MessagingのAPIエンドポイント)
CURLOPT_HTTPHEADER => $headers,//求に含めるHTTPヘッダーにContent-TypeとAuthorization(Firebaseプロジェクトのサーバーキーを含む認証情報)を指定
CURLOPT_SSL_VERIFYPEER => true,//cURLがSSL証明書の検証を行うかどうかを決定
CURLOPT_POST => true,//HTTP POSTメソッドを使ってデータを送信
CURLOPT_RETURNTRANSFER => true,//cURL実行時にデータを直接出力するのではなく、文字列として返すように指示
CURLOPT_POSTFIELDS => $json//HTTP "POST"操作で全てのデータを送信。ここでは$json変数の中身がPOSTフィールドとして送信
));
$response = curl_exec($ch);//実際にAPIリクエストを実行し、レスポンスを受け取る
curl_close($ch);//cURLセッションを閉じる
?>
‘Authorization:と”registration_ids”:、”notification”、これらの文字列は、すべてFirebase Cloud MessagingのAPI仕様の一部であり、APIを使用するために必要なパラメータやヘッダーとなっている。
処理の流れとしては下記の通りだ。
- ①このsend.phpを実行したら指定したトークンやnotificatio情報がAuthorizationキーを使ってFCMにjson形式で渡る
- ②FCMはその情報を受け取り、登録済みのデバイス(指定したトークンに対応)にプッシュ通知を送信
- ③もしアプリがバックグラウンドにあるか、ブラウザが閉じている場合には、firebase-messaging-sw.jsのサービスワーカーで受け取り、ユーザに通知を表示
まとめ
プッシュ通知はユーザーへアプローチをかけるための大変便利な機能ではあるが、一度通知を拒否されたら、ユーザーにcookie情報を削除してもらうまで再通知できないというデメリットもある。
実装して公開する際は、念入りに行うことをおすすめする。
また今回はFirebaseからユーザーへプッシュ通知を送るまでは実現できたが、ユーザーが取得したトークンをDBに入れて、send,.phpの中身をさらに整えなければ、一斉送信はできない。
この辺りは時間がかかりそうなので、作り終えたらまた別途解説したいと思う。