📱

Laravel でSMSに通知する

7 min read

こんにちはみなさん。

プロダクト触っている身としては、ユーザにどうやって通知して、プロダクトをより使ってもらうかって、結構重要です。通知の手段としてはメールとかSlackとかあるわけですが、よりカジュアルで通じやすい手段として、SMSを使うのもあります。
でも、使ったことないものを使うのはハードルが高く、プロダクトに入れるにも勇気がいるものです。
このハードルを下げるのが、素振り、ということで素振りしていきましょう。

今回はPHPerらしく、ちゃんとPHP, Laravelの話です。
マニュアルにあるじゃん!て思った人はそっち見てもいいよ。

環境

ここは動作環境の用意をしている箇所なので、もうあるよって人はNEXMOの項目まで飛ばしてください。

動作環境はこんな感じでいつものようにコンテナ用意します。

FROM php:8

RUN apt-get update && apt-get install -y git unzip libonig-dev libzip-dev && \
docker-php-ext-install mbstring pdo pdo_mysql zip

COPY --from=composer /usr/bin/composer /usr/bin/composer

ちょっとDB使うのでdocker-compose.yml は以下のように定義します

version: "3"

services:
    workspace:
        build: workspace
        command: sleep infinity
        volumes:
            - ../:/var/www/
        environment:
            - LANGUAGE=en_US.UTF-8
            - LC_ALL=en_US.UTF-8
        ports:
            - 8888:8888

    db:
        image: mysql
        environment:
            - MYSQL_ROOT_PASSWORD=secret
            - MYSQL_USER=niisan
            - MYSQL_DATABASE=niisan
            - MYSQL_PASSWORD=secret

以降はコンテナ内部で実行していきます。

今回の主題はLaravelですので、さっそく適当にプロジェクト作ります。

composer create-project --prefer-dist laravel/laravel sample-sms

Vonage Communications APIsを使った通知

SMSの通知方法はマニュアルの以下の項目を見れば、大体 書いてあります。

https://laravel.com/docs/8.x/notifications#sms-notifications
微妙に変なところではまりやすいので注意してください。

で、マニュアルサイトによると、NEXMOを使ってくれとありますが、現在は名前が変わってVonage Communications APIsになっています。
とりあえず、こいつを使えばSMS投げられるということです。

アカウントゲット

今回はフリーのアカウントをゲットしましょう。

https://www.vonage.com/communications-apis/
のtry it free でアカウントを作成します。

アカウントをゲットしたら以下のページにアクセスします。

https://dashboard.nexmo.com/getting-started/sms
ここでPHPのコードをコピーすると、IDとシークレットIDがクリップボードにコピーされます。
$basic  = new \Nexmo\Client\Credentials\Basic('*******', '*****************');
$client = new \Nexmo\Client($basic);

$message = $client->message()->send([
    'to' => '111111111111111',
    'from' => 'Vonage APIs',
    'text' => 'Hello from Vonage SMS API'
]);

Basicのコンストラクタの引数の一つめがIDで、二つめがシークレットIDですね。
とりあえず、.envに突っ込みます。

NEXMO_KEY=********
NEXMO_SECRET=**************

通知作る

通知するためのパッケージ突っ込んでおきます。

composer require laravel/nexmo-notification-channel

データベースのユーザーのmigrationを更新して、電話番号入れておきます。

            $table->string('email')->unique();
+           $table->string('phone_number')->nullable();
            $table->timestamp('email_verified_at')->nullable();

データベースに適当に自分の電話番号突っ込んでおきます。

次に通知作ります。artisan make:notification使ってもいいんですが、結構書き換え発生します。
Notifications/SmsNotification.php

<?php

namespace App\Notifications;

use Illuminate\Notifications\Messages\NexmoMessage;
use Illuminate\Notifications\Notification;

class SmsNotification extends Notification
{

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['nexmo'];
    }

    /**
     * Get the Nexmo / SMS representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return NexmoMessage
     */
    public function toNexmo($notifiable)
    {
        return (new NexmoMessage)
                    ->content('hello world!こんにちは!' . $notifiable->name);
    }
}

こんな感じ。

最後にUserモデルにどのフィールドでSMS送るか指定します。
Models/User.php

    /**
     * Route notifications for the Nexmo channel.
     *
     * @param  \Illuminate\Notifications\Notification  $notification
     * @return string
     */
    public function routeNotificationForNexmo($notification)
    {
        return $this->phone_number;
    }

これがないと、一切送られません。

通知する

適当なコマンドで通知してみます。
Console/Commands/SmsNotify.php

<?php

namespace App\Console\Commands;

use App\Models\User;
use App\Notifications\SmsNotification;
use Illuminate\Console\Command;

class SmsNotify extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'notify:sms';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Send SMS notification!';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $user = User::find(1);
        $user->notify(new SmsNotification);
    }
}

これでコマンドができたので、メッセージを送ります。
なお、フリープランだと送信元の番号はある程度ランダムになるっぽいです。
かけても意味ないとは思いますが、いたずらせんようにね。

php artisan notify:sms

さあこい!
化けてる
送れた!!送れたけど...!
化けとるやないかーい!

というわけで、unicode文字列が化けます。

文字化けしない方法

unicode文字列を使用するためにはunicodeメソッドを使います。
Notifications/SmsNotification.php

    /**
     * Get the Nexmo / SMS representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return NexmoMessage
     */
    public function toNexmo($notifiable)
    {
        return (new NexmoMessage)
                    ->unicode()
                    ->content('hello world!こんにちは!' . $notifiable->name);
    }

あとは修正してみましょう。

php artisan notify:sms

今度こそ!
ちゃんと送れた
送れました!やったね!

注意事項

今回の記事の注意事項です。

日本からのSMSはお高い

VonageのAPIは従量課金制で、価格は

https://www.vonage.com/communications-apis/pricing/
にあります。
一見、SMSの料金は0.0062ユーロって書いてありますが、国際電話になるせいか、日本からの送信は0.07ユーロと、10倍以上になります。
https://www.vonage.com/communications-apis/sms/pricing/
気をつけましょう。

フリーアカウントの制限

フリーアカウントは以下の制限があります。

  • APIの使用は利用価格にして2ユーロ分です。
  • 送信元の電話番号は不定っぽい
  • SMSのメッセージにわざわざ「テストだよ!」って入っている

というわけで、プロダクションでは有料アカウント使いましょうね。

まとめ

というわけで、SMSを使った通知をLaravelからやる方法を素振りしました。
いや、まあ、なんというか、何が微妙って、nexmoって固定のサービス名入れている挙句、そのサービス名は別の新しいサービス名に移行した挙句、でも通知のチャンネル(via)に残っちゃっているというさま。
また、日本というお国の事情もあるのだろうけど、SESのメール一通の値段の700倍強のお値段と、ちょっと気軽に使うにはしり込みする設定にも、ちょっとびっくりです。
ただ、ユーザへのアプローチはメールよりも高いだろうし、使いどころを見極めて使っていきたいですね。

本日はこんなところです。