Broadcast a Voicemail to Multiple Numbers in Laravel PHP with Twilio Voice

September 19, 2019
Written by

Broadcast a Voicemail to Multiple Numbers in Laravel PHP with Twilio Voice.png

My son just started kindergarten and his first year of not being homeschooled. To say that everyone was nervous was an understatement. Luckily, his school is filled with caring people who aren’t afraid to embrace technology for efficient communication (and calming nervous parents).

There we were, running errands after his drop-off and our phone was alerted with an email including a photo and message of his progress. The personal touch of being able to see him relaxed calmed our anxiety, allowing us to enjoy the remainder of our afternoon.

No matter how much we advance technology, it’s my opinion that we must find ways to include “human touch” as much as possible, even if it’s automated.

This got me thinking about how else I would like to be contacted by his school, especially in case of an emergency. Wouldn’t it be cool if, instead of 160 characters, I could receive a call from his teacher assuring me that everything is okay? While this does sound comforting, it wouldn’t scale well. The more efficient route would be to create a voice-based broadcast that could call every parent and share the teacher’s words.

What are we building?

In this tutorial, we will learn how to create a Teacher-Parent Broadcast App (using newly released Laravel 6.0) to simultaneously broadcast a voice message to multiple numbers.

Our application will implement Laravel’s Authentication, with the addition of the user’s phone numbers added to the signup form. This will enable us to create a collection of numbers to simultaneously call.

Lastly, we will create a Laravel Command to execute the broadcast directly from our terminal.

You will need the following tools to complete this tutorial:

Create a New Laravel Application

Our Teacher-Parent Broadcast App will be built on the Laravel framework. If you haven’t installed it locally, a simple guide is available to get you started. Once Laravel is installed run the following commands in your terminal.

$ laravel new broadcast-voicemessage
$ cd broadcast-voicemessage

Include the Twilio PHP SDK

The Twilio Voice API will be responsible for processing our requests to make the phone calls. While it boasts many features and use-cases, we will use it to provide a pre-recorded message to play when the user answers their phone.

In order for our application to connect to the API, we will need to include the Twilio PHP SDK in our project. This can be accomplished by running the following command.

$ composer require twilio/sdk

Add Your Twilio Credentials to the Project

Now that the SDK is installed, our credentials to authenticate each request to the API will need to be added to the project. Laravel includes a dotenv file in each installation to securely expose 3rd party credentials. In your root folder open .env and add the following variables with their respective values.

TWILIO_ACCOUNT_SID="ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
TWILIO_AUTH_TOKEN="Your Auth Token"
TWILIO_PHONE_NUMBER="Your Twilio Phone Number in E.164 format (i.e. +13365555555)"

Create a Custom Laravel Configuration

The dotenv file will provide support out of the box to read credentials using the dotenv() function. However, this isn’t the recommended way as it requires us to hard code the credentials within each location used. It also may not translate well to production environments. Instead, we will wrap these variables within Laravel’s config object and create multiple dynamic variables, keeping in line with SOLID principles.

Custom configurations in Laravel are found within config/services.php. Open this file and add the following array to the services array.

'twilio' => [
'account_sid' => env('TWILIO_ACCOUNT_SID'),
'auth_token'  => env('TWILIO_AUTH_TOKEN'),
]

This update will globalize our Twilio credentials, making them available for use within our system command.

Create a Collection of Phone Numbers

In a real-world application, our database would be prepopulated with users' contact information. We will need to provide a mechanism for simulating this data within our demo.

Our Laravel application comes preloaded with scaffolding for storing users’ information, but we will need to modify the declarations in order to store their phone numbers.

Open the User model located at app/User.php and add the number key to the $fillable (or mass assignable) attributes.

   /**
    * The attributes that are mass assignable.
    *
    * @var array
    */
   protected $fillable = [
       'name', 'email', 'password', 'phone_number'
   ];
   /**
    * Set the default values
    */
   protected $attributes = [
       'phone_number' => '',
   ];

In order to create a column for the user’s phone number, the database migration will need to be modified. Open the CreateUsersTable migration located at database/migrations/2014_10_12_000000_create_users_table.php and add the 'phone_number' definition to the schema in the up() method.

$table->string('phone_number');

Now that the model and migration have been updated, the migration can be run to generate the users table. Run the following command in your terminal:

$ php artisan migrate


NOTE: Your database will need to be defined in the DB_DATABASE var of your .env file. Without it, the previous command will not know which database to generate the tables in.

Register a Single User

We’re ready to add a user to the application. To do so, we’ll enable Laravel’s authentication system which will generate the registration and login for us. Run the following commands in your terminal.

$ composer require laravel/ui --dev
$ php artisan ui vue --auth
$ npm install && npm run dev

Now start the local web server using the Valet command share.

$ valet share

NOTE: You will need to open a new terminal and leave this one running.

The default registration page only includes fields for name, email, and password. A field will need to be added to the registration page to allow the user to input their phone number. Open up resources/views/auth/register.blade.php and add the following code above the block of code for the Register submit button.

<div class="form-group row">
    <label for="phone_number" class="col-md-4 col-form-label text-md-right">{{ __('Phone Number') }}</label>

    <div class="col-md-6">
        <input id="phone_number" type="text" class="form-control @error('password') is-invalid @enderror" name="phone_number" required>

        @error('phone_number')
            <span class="invalid-feedback" role="alert">
                <strong>{{ $message }}</strong>
            </span>
        @enderror
    </div>
</div>

Validation will be required for this field to successfully submit. In the RegistrationController.php file update the validator() method to include the phone_number.

We require the field to be entered, restrict its type to a string, and set a minimum length of nine (9) characters.

'phone_number' => ['required','string', 'min:9'],

In the same file, add the field to the array in the create() method to allow it to be inserted into the database.

'phone_number' => $data['phone_number'],

Registration scaffolding is now complete. Navigate to http://127.0.0.1:8000/register in your browser and register yourself. Be sure to use your real phone number as our application will use this value to make a phone call.

NOTE: To see a full test of this application, register an additional user with a phone number you have access to.
Create an Artisan Console CommandFor sake of brevity, we will create a console command to trigger our broadcast from the terminal. Ideally, this could be added as a button within an administration dashboard. To generate the scaffolding for our command, run the following in your terminal.

$ php artisan make:command callEveryone

Define the Signature for the Command

The previous command generated a file callEveryone.php in app/Console/Commands/. The actual command that we’ll use in the terminal needs to be specified in our class variables. Update the $signature and $description variables as shown below. This will allow us to register the command php artisan call:everyone.

protected $signature = 'call:everyone';
protected $description = 'Calls everyone with a pre-recorded message';

When this command is run our application will register a Twilio client, query the database for all users, and return their attributes in a collection.

New to Laravel are Lazy Collections, a new class that “leverage PHP's generators to allow you to work with very large datasets while keeping memory usage low.” This means that if our application contained 10K users, each user would automatically be read in very small batches versus loading all into before processing.

Accessing this class is simple. On any model, just call ::cursor() in a foreach loop and include the respective logic. Each record will be processed individually and memory preserved.

Add the following code to the handle() method in callEveryone.php to create a batch calling script.

    /**
     * Execute the console command. 
     * 
     * @return mixed
     */
    public function handle()
    {
        $sid    = config('twilio.account_sid');
        $token  = config('twilio.auth_token');
        $twilio = new Client( $sid, $token );

        foreach ( User::cursor() as $user ) {

            $call = $twilio->calls
                ->create( $user->phone_number, // to
                        config('twilio.phone_number'), // from
                        [ "url" => "http://demo.twilio.com/docs/voice.xml" ]
                );

        }
    }

Test the Broadcast

At this point, we have created a fully operational registration system with an artisan command that calls each user. In your terminal run php artisan call:everyone and wait for the call to come to your phone.

Setup a Custom Endpoint to Broadcast our Message

Everything we need to broadcast a message to multiple phone numbers is complete. The only item left to complete is controlling the message the recipient hears. To do this we will need to create an endpoint that supplies a TwiML response.

The first thing required to create an endpoint is a new controller. Our controller will be responsible for creating a callback to add to a command. In your terminal run:

$ php artisan make:controller API/VoiceController

This command will create the controller VoiceController.php in the app/Http/Controllers/API folder. Open this file and add the following code.

PHP
<?php

namespace App\Http\Controllers\API;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Twilio\TwiML\VoiceResponse;

class VoiceController extends Controller
{
   public function broadcast()
   {
       $response = new VoiceResponse()
       $response->play('https://api.twilio.com/cowbell.mp3');

       echo $response;
   }
}

The code above uses Twilio’s VoiceResponse class to generate TwiML and play an mp3 file for the caller. Your application will replace the https://api.twilio.com/cowbell.mp3 URL with your actual voice message.

NOTE: The URL to your mp3 must be publicly accessible.

We’ll need to create a route to access the controller via HTTPS. In the routes folder is a file named api.php. This routing file is the home of all CRUD endpoints for our applications. Open the file and add the following line of code.

Route::post( '/broadcast', 'API\VoiceController@broadcast' );

Lastly, we need to tell the command we created earlier to use this endpoint as a callback URL. This URL will be executed via a POST request and play the voice message when the command is executed.

Navigate back to the terminal that is running your webserver. Copy the HTTPS Forwarding URL and open callEveryone.php again. Replace the url parameter of http://demo.twilio.com/docs/voice.xml with the Forwarding URL + /api/broadcast.

foreach ( User::cursor() as $user ) {
        
    $call = $twilio->calls
        ->create( $user->phone_number, // to
                config('twilio.phone_number'), // from
                [ "url" => "http://demo.twilio.com/docs/voice.xml" ]
        );
}

Conclusion

Your Teacher-Parent Broadcast App is now complete! Give it a try by running the php artisan call:everyone command in your terminal.

I’d love to see how you build upon this. Maybe you’ll add a GUI to upload your voice message directly or write programmatic responses using the <Say> verb. Either way, feel free to reach out to me on Twitter or via email if you have any questions or modifications.

Additional Resources

An Introduction to Lazy Collections - The official Laravel Documentation about how the LazyCollection class leverages PHP Generators.

Understanding PHP Generators - The official PHP Documentation offers an overview with examples to help you understand how PHP generators preserve memory.

Use Laravel for a Quick Development Server - How to Create a Local WordPress Setup in 5 Minutes using Valet, by Marcus Battle, describes how Laravel Valet can improve your existing local development.

 

If you need any assistance, don’t hesitate to reach out on Twitter or shoot me an email.

Marcus Battle is Twilio’s PHP Developer of Technical Content. You can learn more about him on the blog.