LaravelとTwilioによるSMSポータルの作成方法

March 22, 2019
執筆者
Brian Iyoha
寄稿者
Twilio の寄稿者によって表明された意見は彼ら自身のものです

xfZiGIwY7kMFumujtS99SsDUOzzE3HEluKNzI4Td5Ba7yorAqM9583sLeRzWPPuI2iDcAOjRAj2f0OZoGkCTjizc-ZoKWtWz98AHxpunO00UwgncR4v9XNn3bgJrLKPySdF2rbC0f2CuaGby8A

アプリケーションワークフローのある時点において、重要な情報をユーザーに渡すことが必要になる場合があります。Webテクノロジーが進歩したおかげで、この処理はプッシュ通知で簡単にできます。ただし、このようなサービスの多くは、ユーザーのインターネットに接続していることが必要ですが、残念なことに、常に当てはまるとは限りません。幸い、インターネットに依存しない通知システムを使用すると、この問題を解決できます。

このチュートリアルでは、TwilioのProgrammable SMSを使用し、Laravelを使用するSMS通知ポータルの作成方法について説明します。チュートリアルを終えると、カスタムのSMS通知ポータルを開発できます。このポータルでは、SMSにより、ダッシュボードを介してユーザーに通知できます。

必要条件 

このチュートリアルを利用するには、以下の項目が必要です。

プロジェクトを設定する

始めに、LaravelインストーラーかComposerを使用し、新しいLaravelプロジェクトを作成する必要があります。このチュートリアルでは、Laravelインストーラーを利用します。Laravelがインストールされていない場合は、Laravelドキュメントでインストール方法を確認してください。新規にLaravelプロジェクトを生成するには、以下のコマンドをターミナルにて実行します。

$ laravel new sms-portal

作業ディレクトリをsms-portalに変更し、Composerを介してTwilio SDKをインストールします。

$ cd sms-portal
$ composer require twilio/sdk 

ComposerがPCにインストールされていない場合は、こちらの手順に従い、インストールしてください。

Twilio PHP SDKをインストール後、Twilio認証情報をTwilioダッシュボードから取得する必要があります。ダッシュボードに移動し、account_sidauth_tokenを確認します。

Twilio認証情報スクリーンショット

Phone Number](電話番号)セクションに移動し、SMS対応の電話番号を確認します。

Twilioのアクティブな番号パネル

次のステップでは、Twilio認証情報を使用し、.envファイルを更新します。プロジェクトディレクトリのルートにある.envを開き、次の値を追加します。

TWILIO_SID="INSERT YOUR TWILIO SID HERE"
TWILIO_AUTH_TOKEN="INSERT YOUR TWILIO TOKEN HERE"
TWILIO_NUMBER="INSERT YOUR TWILIO NUMBER IN [E.164] FORMAT"

データベースを設定する

Twilio SDKがインストールされた基本的なLaravelプロジェクトが生成されました。次に、データベースを作成しましょう。phpMyAdminなどのGUIアプリケーションを使用してデータベースを管理している場合は、それを使用してsms_portalという名前のデータベースを作成し、このセクションをスキップしてください。同等のアプリケーションやツールがなく、MySQLがインストールされていない場合は、プラットフォームの公式サイトからMySQLをインストールします。

ターミナルを起動し、以下のコマンドを実行し、MySQLにログインします。

$ mysql -u {your_user_name}

注記: MySQLインスタンスのパスワードがある場合は、-pフラグを追加します。

ログイン後、次のコマンドを実行し、新しいデータベースを作成します。

mysql> create database sms_portal;
mysql> exit;

次に進み、プロジェクトフォルダーのルートにある.envファイルのデータベース設定を適宜変更しましょう。

DB_DATABASE=sms_portal
DB_USERNAME=root
DB_PASSWORD=

移行を作成する

データベースが作成されたため、次はデータベース移行を作成しましょう。データベース移行は、このコマンドをターミナルで実行するだけで作成できます。

$ php artisan make:migration create_users_phone_number_table

移行ファイル{current_time_stamp}_create_users_phone_number_table/database/migrationsディレクトリに生成されます。

プロジェクトフォルダーをIDEまたはテキストエディタで開くと、必要に応じて変更できるようになります。作成した移行ファイルを開きます。内容は次のようになります。

移行ファイルをIDEにて作成

phone_number列をテーブルに追加する必要があります。これを行うには、up()メソッドを次のように編集します。

public function up() {
    Schema::create('users_phone_number', function (Blueprint $table) {
        $table->increments('id');
        $table->string('phone_number'); 
        $table->timestamps();
    });
}

移行ファイルを移行できるようになりました。次のコマンドをターミナルで実行するだけで移行できます。

$ php artisan migrate

出力結果は次のようになります。

artisan migrateのConsole出力結果

ユーザーインターフェイスを作成する

この時点において、プロジェクトのセットアップが完了しています。次に、データをデータベースに追加し、SMS通知を送信するためのシンプルなユーザーインターフェイスを構築します。

/resources/views/welcome.blade.phpを開き、<head>ブロックを次のように変更します。

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>SMS Portal With Twilio</title>
    <!-- Bootstrap styles CDN -->
     <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
</head>

ページの初期スタイルを削除し、簡単にスタイル設定できるようBootstrapCDNを使用し、ブートストラップを追加しました。次に、2つのフォームをページに作成します。フォームのうち、1つはユーザーの電話番号を登録するため、もう1つは選択したユーザーにカスタム通知メッセージを送信するためのものです。bodyブロックを次のように変更します。

<body>
    <div class="container">
        <div class="jumbotron">
            <div class="row">
                <div class="col">
                    <div class="card">
                        <div class="card-header">
                            Add Phone Number
                        </div>
                        <div class="card-body">
                            <form>
                                <div class="form-group">
                                    <label>Enter Phone Number</label>
             <input type="tel" class="form-control" placeholder="Enter Phone Number">
                                </div>
                  <button type="submit" class="btn btn-primary">Register User</button>
                            </form>
                        </div>
                    </div>
                </div>
                <div class="col">
                    <div class="card">
                        <div class="card-header">
                            Send SMS message
                        </div>
                        <div class="card-body">
                            <form>
                                <div class="form-group">
                                    <label>Select users to notify</label>
                                    <select multiple class="form-control">
                                        @foreach ($users as $user)
                                        <option>{{$user->phone_number}}</option>
                                        @endforeach
                                    </select>
                                </div>
                                <div class="form-group">
                                    <label>Notification Message</label>
                                 <textarea class="form-control" rows="3"></textarea>
                                </div>
             <button type="submit" class="btn btn-primary">Send Notification</button>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

上記のコードのうち、次の部分に注目します。

      <select multiple class="form-control">
            @foreach ($users as $user)
              <option>{{$user->phone_number}}</option>
           @endforeach
      </select>

このスニペットは、このビューで返される利用可能なユーザーの電話番号を保存するオプションフィールドを生成するのに役立ちます。この機能は後ほど実装します。  

ユーザーの電話番号を保存する

まず、クエリを実行し、レコードをデータベースに挿入するのに使用するモデルを作成しましょう。プロジェクトディレクトリにてターミナルを起動し、次のコマンドを実行します。

 $ php artisan make:model UsersPhoneNumber

app/UsersPhoneNumber.phpにある作成したファイルを開き、次のコードを追加します。

<?php
namespace App;

use Illuminate\Database\Eloquent\Model;

class UsersPhoneNumber extends Model
{
   protected $table= "users_phone_number";
   protected $fillable = [
        'phone_number'
    ];
}

注記: 次の点に注意してください。

  • protected $table= "users_phone_number";を追加するということは、Eloquentにテーブル名の使用を指示するということです。これを指定しない場合は、クラス名の複数形がテーブル名として使用されます。 
  • protected $fillableを追加すると、Eloquentにフィールドの一括割り当てを可能にするよう指示します(詳しくは、上記のリンク先を参照)。

次に、各リクエストルートに必要なロジックを実装するコントローラーを作成します。再度、ターミナルを起動し、次のコマンドを実行します。

$ php artisan make:controller HomeController

その結果、次のような内容のコントローラーファイルがapp/Http/Controllers/HomeController.phpに生成されます。

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HomeController extends Controller
{
    //
}

電話番号の保存メソッドを作成する

リクエスト本文から渡されたデータを使い、新しいUsersPhoneNumberモデルをインスタンス化する関数を作成しましょう。

<?php

/**
 * Store a new user phone number.
 *
 * @param  Request  $request
 * @return Response
 */
public function storePhoneNumber(Request $request)
{
    //run validation on data sent in
    $validatedData = $request->validate([
        'phone_number' => 'required|unique:users_phone_number|numeric'
    ]);
    $user_phone_number_model = new UsersPhoneNumber($request->all());
    $user_phone_number_model->save();
    return back()->with(['success'=>"{$request->phone_number} registered"]);
}

上記のコードでは、$request本文から渡されたデータで検証を実行してから、UsersPhoneNumberの新しいインスタンスを作成しています。ユーザーの電話番号を保存後、ウェルカムページに戻り、成功メッセージがセッションにフラッシュされます。

ユーザーの電話番号をビューに返す

次に、登録された電話番号のデータをビューに返す必要があります。HomeControllerにて、users_phone_numberテーブルのクエリを実行し、クエリ結果をビューに表示するシンプルな関数を記述しましょう。

<?php

/**
 * Show the forms with users phone number details.
 *
 * @return Response
 */
public function show()
{
    $users = UsersPhoneNumber::all(); //query db with model
    return view('welcome', compact("users")); //return view with data
}

注記: compact()は、変数名と値の配列を作成できるPHP関数です。

Twilio Programmable SMSによるメッセージの送信

次のステップでは、TwilioのProgrammable SMSライブラリーを使用したSMSの送信を実装します。HomeControllerにて、メッセージ送信のhelper関数として機能するprivate関数を作成します。

<?php

/**
 * Sends sms to user using Twilio's programmable sms client
 * @param String $message Body of sms
 * @param Number $recipients string or array of phone number of recepient
 */
private function sendMessage($message, $recipients)
{
    $account_sid = getenv("TWILIO_SID");
    $auth_token = getenv("TWILIO_AUTH_TOKEN");
    $twilio_number = getenv("TWILIO_NUMBER");
    $client = new Client($account_sid, $auth_token);
    $client->messages->create($recipients, 
            ['from' => $twilio_number, 'body' => $message] );
}

この関数は、$message$recipientsの2つのパラメータを受け取ります。次に、組み込みのPHP getenv()関数を使用し、保存されているTwilio認証情報を環境変数から取得します。その後、新しいTwilioクライアントを、認証情報を使いインスタンス化します。次の呼び出しにより、SMSを送信できるようになりました。

$client->messages->create($recipients, [
    'from' => $twilio_number, 
    'body' => $message
]);

Twilioのmessages->create()関数は、2つのパラメータを受け取ります。1つはメッセージの受信者、もう1つはfrombodyのプロパティによる配列です。from有効なTwilio電話番号です。

登録時にユーザー通知を送信する

sendMessage()関数が完成しました。この関数を使用し、メッセージをユーザーに送信します。次に、ユーザー登録が成功したことをユーザーに通知するためにstorePhoneNumber()関数を更新しましょう。これを行うには、storePhoneNumber()関数を次のように変更します。

<?php

/**
 * Store a new user phone number.
 *
 * @param  Request  $request
 * @return Response
 */
public function storePhoneNumber(Request $request)
{
    //run validation on data sent in
    $validatedData = $request->validate([
        'phone_number' => 'required|unique:users_phone_number|numeric',
    ]);
    $user_phone_number_model = new UsersPhoneNumber($request->all());
    $user_phone_number_model->save();
    $this->sendMessage('User registration successful!!', $request->phone_number);
    return back()->with(['success' => "{$request->phone_number} registered"]);
}

いいですね!その結果、ユーザーの電話番号がデータベースに追加されるたびに、実行されたアクションについて注意喚起する通知メッセージをユーザーに送信できます。

カスタム通知を送信する

次に、選択したユーザーにカスタムメッセージを送信するための関数を記述しましょう。HomeControllerに次のコードを追加します。

<?php 

/**
 * Send message to a selected users
 */
public function sendCustomMessage(Request $request)
{
    $validatedData = $request->validate([
        'users' => 'required|array',
        'body' => 'required',
    ]);
    $recipients = $validatedData["users"];
    // iterate over the array of recipients and send a twilio request for each
    foreach ($recipients as $recipient) {
        $this->sendMessage($validatedData["body"], $recipient);
    }
    return back()->with(['success' => "Messages on their way!"]);
}

この関数は、検証済みのデータを$request本文から$validatedData変数に渡します。その結果、$validatedData[users]の配列を反復処理し、$validatedData["body"]から受信したメッセージを各ユーザーに送信できます。その後、ウェルカムページにリダイレクトし、セッションにメッセージがフラッシュされます。

ルートを作成する

コントローラー関数が正常に作成されました。次に、ルートをアプリケーションに追加しましょう。routes/web.phpを開き、次のように変更します。

<?php
Route::get('/', 'HomeController@show');
Route::post('/', 'HomeController@storePhoneNumber');
Route::post('/custom', 'HomeController@sendCustomMessage');

フォームフィールドをルートで更新する

次に、resources/views/welcome.blade.phpに移動し、フォームフィールドを次のように変更します。

//add the method attribute to the Register User form
// also add the name attributes to the input field 
<form method="POST">
    @csrf
    <div class="form-group">
        <label>Enter Phone Number</label>
        <input type="tel" class="form-control" name="phone_number" placeholder="Enter Phone Number">
    </div>
    <button type="submit" class="btn btn-primary">Register User</button>  
</form>

または

//add the method and action attributes to the Send custom message form
// also add the name attributes to the input fields 
<form method="POST" action="/custom">
    @csrf
    <div class="form-group">
        <label>Select users to notify</label>
        <select name="users[]" multiple class="form-control">
            @foreach ($users as $user)
            <option>{{$user->phone_number}}</option>
            @endforeach
        </select>
    </div>
    <div class="form-group">
        <label>Notification Message</label>
        <textarea name="body" class="form-control" rows="3"></textarea>
    </div>
    <button type="submit" class="btn btn-primary">Send Notification</button>
</form>

コードをテストする

これまでに作成したすべてのコードを1つにまとめましょう。この時点において、HomeController.phpは次のようになります。

<?php
namespace App\Http\Controllers;

use App\UsersPhoneNumber;
use Illuminate\Http\Request;
use Twilio\Rest\Client;

class HomeController extends Controller
{
    /**
     * Show the forms with users phone number details.
     *
     * @return Response
     */
    public function show()
    {
        $users = UsersPhoneNumber::all();
        return view('welcome', compact("users"));
    }
    /**
     * Store a new user phone number.
     *
     * @param  Request  $request
     * @return Response
     */
    public function storePhoneNumber(Request $request)
    {
        //run validation on data sent in
        $validatedData = $request->validate([
            'phone_number' => 'required|unique:users_phone_number|numeric',
        ]);
        $user_phone_number_model = new UsersPhoneNumber($request->all());
        $user_phone_number_model->save();
        $this->sendMessage('User registration successful!!', $request->phone_number);
        return back()->with(['success' => "{$request->phone_number} registered"]);
    }
    /**
     * Send message to a selected users
     */
    public function sendCustomMessage(Request $request)
    {
        $validatedData = $request->validate([
            'users' => 'required|array',
            'body' => 'required',
        ]);
        $recipients = $validatedData["users"];
        // iterate over the array of recipients and send a twilio request for each
        foreach ($recipients as $recipient) {
            $this->sendMessage($validatedData["body"], $recipient);
        }
        return back()->with(['success' => "Messages on their way!"]);
    }
    /**
     * Sends sms to user using Twilio's programmable sms client
     * @param String $message Body of sms
     * @param Number $recipients Number of recipient
     */
    private function sendMessage($message, $recipients)
    {
        $account_sid = getenv("TWILIO_SID");
        $auth_token = getenv("TWILIO_AUTH_TOKEN");
        $twilio_number = getenv("TWILIO_NUMBER");
        $client = new Client($account_sid, $auth_token);
        $client->messages->create($recipients, ['from' => $twilio_number, 'body' => $message]);
    }
}

ビューでは、welcome.blade.phpが次のようになります。

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>SMS Portal With Twilio</title>
    <!-- Styles -->
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
        crossorigin="anonymous">
</head>
<body>
    <div class="container">
        <div class="jumbotron">
            @if (session('success'))
            <div class="alert alert-success">
                {{ session('success') }}
            </div>
            @endif
            @if ($errors->any())
            <div class="alert alert-danger">
                <ul>
                    @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                    @endforeach
                </ul>
            </div>
            @endif
            <div class="row">
                <div class="col">
                    <div class="card">
                        <div class="card-header">
                            Add Phone Number
                        </div>
                        <div class="card-body">
                            <form method="POST">
                                @csrf
                                <div class="form-group">
                                    <label>Enter Phone Number</label>
                                    <input type="tel" class="form-control" name="phone_number" placeholder="Enter Phone Number">
                                </div>
                                <button type="submit" class="btn btn-primary">Register User</button>
                            </form>
                        </div>
                    </div>
                </div>
                <div class="col">
                    <div class="card">
                        <div class="card-header">
                            Send SMS message
                        </div>
                        <div class="card-body">
                            <form method="POST" action="/custom">
                                @csrf
                                <div class="form-group">
                                    <label>Select users to notify</label>
                                    <select name="users[]" multiple class="form-control">
                                        @foreach ($users as $user)
                                        <option>{{$user->phone_number}}</option>
                                        @endforeach
                                    </select>
                                </div>
                                <div class="form-group">
                                    <label>Notification Message</label>
                                    <textarea name="body" class="form-control" rows="3"></textarea>
                                </div>
                                <button type="submit" class="btn btn-primary">Send Notification</button>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

コードが同じの場合は、次に進むことができます。コードに違いがある場合は、コードを再確認し、不足している部分を見つけてください。

アプリケーションを実行する

ターミナルを開き、プロジェクトディレクトリに移動し、次のコマンドを実行します。

$ php artisan serve

Laravelアプリケーションは、ローカルホストポート(通常、8000)にて実行されます。ブラウザーにおいてコマンドの実行後に表示されるローカルホストリンクを開きます。ページは次のように表示されます。

ローカルホストにて実行するLaravelアプリ

次に進み、新しい電話番号を登録します。すべての処理が正常に完了すると、まもなく登録について通知するSMSが届きます。

Twilioトライアルアカウントのテキストメッセージ例

 選択フィールドからユーザーを選択し、選択したユーザーに送信するテキストをテキスト領域に入力した場合、カスタム通知の送信もテストできます。完了後、[Send Notification]ボタンをクリックすると、カスタム通知メッセージがSMSにて届きます。

カスタム通知

まとめ

チュートリアルが完了しました。Twilio Programmable SMSをLaravelアプリケーションに統合し、SMSを介して通知メッセージを送信できます。このチュートリアルの全ソースコードは、GitHubにて確認できます。 

ユーザーが送られてきたSMS通知から行動を起こせるようにすることもできます。 

このチュートリアルに関する質問はお気軽にお寄せください。連絡先は次のとおりです。