さて、自己紹介ばかりではなく、今学習してるLaravelについてちょっと書いてみます。
ちなみにこれは #DeafEngineers Advent Calendar 2022 22日目の記事になります。
最初はLaravelの不思議なところを書こうとおもったのですが、うまく纏まらなかったのと、
今悩んでいたことが解決したのもありそれをまとめたかったので今回はこれにしました。
前書き
・筆者はLaravelを使って開発するのは今回実は初めて
・一応、一通り学んではいるが、所々理解が怪しいところがある(ミドルウェアとか)
・ちなみに今回使う方法はおそらくメジャーな使い方ではないので注意
(なぜなら、モデルを使うのに、テーブルも使わないし、Viewも使わないため)
やりたかったこと
・コマンドスクリプト、いわゆるバッチで、何らかのメッセージをGoogleChatの作ったチャンネルに通知したかった
(もちろん何らかのメッセージは別のところから受け取らせる予定だが、今は通知システム内で仮に作ったのを送る形)
・Laravelバージョンは9.0で、使いたかったNotificationクラスは下記に説明あり
https://laravel.com/docs/9.x/notifications
(英語)
https://readouble.com/laravel/9.x/ja/notifications.html
(日本語版・翻訳途中)
・以下のLaravelのGoogle-chatクラスを使う
https://github.com/laravel-notification-channels/google-chat
最初やったこと
1)Laravelを入れているプロジェクトに移動して、Google-chatクラスをComposerでインストールする
(インストール完了までちょっと時間がかかる)
cd (Laravelを入れているプロジェクトパス)
composer require laravel-notification-channels/google-chat
2)GoogleChatで通知したいチャンネルを作成し、そこでWebhookURLを取得しておく
「GoogleChat webhookURL 取得」で調べたら画面付きで説明してくれてるのがあるのでそれを参考に。
URLは下のようなのが出ます。これはメモ帳などにコピペして保存しておく。
https://chat.googleapis.com/room-webhook-for-all-notifications?key=xxxxx
(xxxxはユーザーによって違う。URLもチャンネルの種類によってはちょっと違うかも。)
3)GoogleChatの構成ファイルをコマンドで作成
composer require laravel-notification-channels/google-chat
すると、configフォルダに「google-chat.php」が出てくるのでそれを開いて、さっきコピペしておいた
WebhookのURLを下記のように入れて保存。 ※カッコはいらない
return [
'space' => '(さっきコピペしたWebhookのURL)'
]
4) で、GoogleChatの説明を読むと、
In order to send a notification via the Google Chat channel, you'll need to specify the channel in the via() method of your notification:
use NotificationChannels\GoogleChat\GoogleChatChannel;
// ...
public function via($notifiable)
{
return [
GoogleChatChannel::class
]
}
If you haven't already, make sure you understand how notification classes are constructed.
とあるのだ。
というわけで、Laravelに元々あるNotificationクラスが関係あることはわかった。
じゃあこれをもとにクラス作ればいいよなということで。
今度はNotificationクラスの説明を読んで、Notificationクラスをコマンドで作る。
php artisan make:notification (好きなクラス名でOK。※ここではGoogleChatとした)
5)すると、App > Notifications フォルダの下に、上で作った xxx.phpが出てくるので開く。
そこにviaメソッドがあったので、4)のコードに修正。
6)で最後に、GoogleChatの説明を読むと、
Simple messages can be created easily using a NotificationChannels\GoogleChat\GoogleChatMessage instance directly, and returned in your notification class like so:
use NotificationChannels\GoogleChat\GoogleChatMessage;
public function toGoogleChat($notifiable)
{
return GoogleChatMessage::create('Hello world!');
}
とりあえずuse文章を入れて、その上で、toGoogleChatメソッドをどこかの通知クラスに入れればOKと言ってるので、通知クラスであるGoogleChat.phpの中のGoogleChatクラスのviaメソッドの下に、toGoogleChatを追加。
public function via($notifiable)
{
return [
GoogleChatChannel::class
]
}
+ public function toGoogleChat($notifiable)
+ {
+ return GoogleChatMessage::create('Hello world!');
+ }
7) あとはこれを発動させればいいのだが、バッチでやりたかったので、下記を参考に、コマンドを作成。
https://laravel.com/docs/9.x/artisan#writing-commands
php artisan make:notification (※コマンドクラス名。ここではGoogleChatCommandとした)
すると、App > Console > Commandsの下に、「GoogleChatCommand.php」が出るので
下記のように編集。
class SendEmails extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:toGoogleChat';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Googleチャット通知';
/**
* Execute the console command.
*
* @param \App\Support\DripEmailer $drip
* @return mixed
*/
public function handle()
{
$GoogleChatCall = new ¥App¥Notifications¥GoogleChat;
$GoogleChatCall->toGoogleChat();
}
}
とかいて、
コマンドスクリプトで、Laravelプロジェクト上に移動して、以下のコマンドを叩くと
発動する、、、はずだった。ちなみにコマンドは間違っておらず正常に実行されている。
php artisan command:toGoogleChat
ところが、エラーも出ず、かといって通知もされず。
色々書き方を変更してみたが、全く通知されなかった。
右往左往
Notificationクラスの意味を全く理解できていなかったのが原因なのだが、
今度はNotificationクラスを見てみよう。
https://laravel.com/docs/9.x/notifications
***
Notifications may be sent in two ways: using the notify method of the Notifiable trait or using the Notification facade. The Notifiable trait is included on your application's App\Models\User model by default:
***
とある。
つまり、Notifiableトレイトか、Notificationファサードのどちらかのメソッドを使わないと通知がされない。
しかしすでにNotifiableトレイトはもうすでにUserモデルに入っているようだ。
その次に、
***
The notify method that is provided by this trait expects to receive a notification instance:
***
notifyメソッドは Notifibaleトレイトにあるので、通知インスタンスにすれば使えると言っているらしい。
そして例文があったのでこちらを
+ use App\Notifications\GoogleChat;
public function toGoogleChat($notifible){
- return GoogleChatMessage::create('Alert!!');
+ $user->notify(new GoogleChat();
}
などとしてもエラーが出た。
じゃあコマンドかな?と思ってそちらに追加してみた。
public funcion handle(){
- $GoogleChatCall = new ¥App¥Notifications¥GoogleChat;
- $GoogleChatCall->toGoogleChat();
+ $user->notify(new GoogleChat());
}
としてもダメだった。
正解
Commandでも良いし、Controllerを新たに作って使っても良いが、
Userモデルをインスタンス化したものを使い、それにNotifyメソッドを使わせれば良い。
とりあえず簡単にCommandで使ってみる。
//上の方のuse群に追加
+ use App\Models\User;
+ use App\Notifications\GoogleChat;
public function handle{
$user = new User();
$user->notify(new GoogleChat());
}
これで通知がされる。
実際に。GoogleChatに通知が来た。
なぜそうなるのか
さっき、「すでにNotifiableトレイトはもうすでにUserモデルに入っているようだ」と述べた。
つまりUserクラスをインスタンス化すればNotifiableトレイトは使えることになる。
そしてトレイトのメソッド、notify()も使えてしまうということだ。
いまUserモデルをインスタンス化して、$userにし、そこからnotifyメソッドを発動させたということになる。
ここで、再度Notificationクラスのここを読んでみよう。
***
The notify method that is provided by this trait expects to receive a notification instance:
***
ここ、わたしが読み間違えていた。
notifyメソッドはこのトレイトによって提供される、これはnotificationインスタンスを受け取ることを期待(想定)している、と。つまり、このトレイトのnotifyメソッドは、Notificationインスタンスを引数として取りますよ、と言っているのだ。ギャフン。
なので、使い方としては、下のように、$user->notifyメソッドの引数として、NotificationクラスであるGoogleChatをnewしてインスタンス化したのを放り込めば良かったのだ。
public function handle{
$user = new User();
$user->notify(new GoogleChat());
}
読解力不足だなあと感じた昨今である。
とはいえ、12月の初めからずっと悩んでいたことが解決したのでホッとした。
検索結果を見ると、なかなかこういうのが出てこなかったので
他にも困っている人がいるんじゃないかと思ったのと、
アドベントカレンダーにも参加しているのでこういうことをやってますよというので
カキコした。