This content originally appeared on Twilio Blog and was authored by Diba Kalantari
In this tutorial, you'll bring a little joy to people you know this Christmas by building an app with Laravel, Laravel Sail, Twilio Programmable Voice and Programmable SMS, and SendGrid.
Specifically, you'll give a gift of good feeling to someone special in your life, by letting them receive inspirational quotes every single day, and in order to avoid boring them, you will do this through three different channels: SMS, phone calls, and email. That’s why I called the project Hype Someone Up.
Prerequisites
To implement this project you will need the following:
- A free Twilio account
- A Twilio phone number
- A mobile phone which can receive phone calls and SMS
- A free SendGrid account
- An email address to send the email which has a verified identity
- Docker Engine or Docker Desktop
- PHP 8.1 (or 8.2 when released)
- Composer globally installed
- curl
Create the application
Initialize the Laravel project
The project will use Laravel Sail as a development environment to save you time setting up the application's dependencies. If you're not familiar with it, Laravel Sail, as the official documentation says, is:
a light-weight command-line interface for interacting with Laravel's default Docker development environment, and provides a great starting point for building a Laravel application.
To create a new Laravel project using it, run the following command:
curl -s "https://laravel.build/hype-someone-up" | bash
After the project has been created, navigate to the new project's directory and start Laravel Sail by running the commands below
cd hype-someone-up
./vendor/bin/sail up -d
Now, you will be able to access the project in your browser of choice by opening http://localhost. It should look similar to the screenshot below.
Install Twilio's PHP helper library
The next step is to install Twilio's PHP helper library, which simplifies interacting with Twilio's APIs in PHP, by running the command below:
./vendor/bin/sail composer require twilio/sdk
Set the required credentials
Then, you need to set the credentials that the application will need to interact with both Twilio and SendGrid. Start off by, at the end of .env, adding the following variables.
TWILIO_SID=<<Your account SID>>
TWILIO_AUTH_TOKEN=<<Your auth token>>
TWILIO_SOURCE_NUMBER=<<The number you have purchased>>
Retrieve Account SID and Auth Token from Twilio
Then, you have to retrieve your Twilio Account SID, Auth Token, and phone number from your Twilio account. To do that, login to the Twilio Console, and under "Account Info", copy the Account SID, Auth Token, and phone number and paste them in place of the three respective placeholders that you previously added at the end of .env.
Create a SendGrid API key
Next, you need to create and set a SendGrid API key. To do that, log in to your SendGrid account and, in the dashboard under "Settings > API Keys", click "Create API Key".
Then, add a meaningful name for your API key, click "Create & View", copy your API key and paste it as the value of the MAIL_PASSWORD
key in .env.
In addition, you need to update the following MAIL_
settings in .env:
MAIL_MAILER=smtp
MAIL_HOST=smtp.sendgrid.net
MAIL_PORT=587
MAIL_USERNAME=<<Your SendGrid Username>>
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=<<A SendGrid email address which has a verified identity>>
After setting these values, in the /config directory you need to create a file called twilio.php. Then, paste the following code into it.
<?php
return [
'sid' => env('TWILIO_SID', ''),
'auth_token' => env('TWILIO_AUTH_TOKEN', ''),
'source_number' => env('TWILIO_SOURCE_NUMBER', ''),
];
It’s not a good practice to use .env variables inside code other than in config files – especially if you are running config:cache
command during your deployment process; your .env file will not be loaded once configuration has been cached.
In addition to the Twilio configuration, you need to store the receiver's information somewhere. In a real world project you would probably have receiver info stored inside a database, or you would get it via user input.
But as this small project has one specific receiver, it will be stored in .env. So add the following settings to the end of .env.
QUOTE_RECEIVER_NUMBER=<<you arbitrary receiver number>>
QUOTE_RECEIVER_EMAIL=<<you arbitrary receiver email address>>
Next, you have to create another config file in the /config directory called receiver.php, and add the following code to the file.
<?php
return [
'receiver_number' => env('QUOTE_RECEIVER_NUMBER', ''),
'receiver_email' => env('QUOTE_RECEIVER_EMAIL', '')
];
Generate the command
Now that the configuration is done, run the following command to generate a Command
class, named SendQuotes
, to send quotes to the receiver.
./vendor/bin/sail artisan make:command SendQuotes
Before getting into the code of this class, you need to create an interface for all the messengers (SMS, Call, and Email). That way you can randomly choose one of them as a communication channel of the day. This interface will work as a contract between the three implementations and enforce all classes to implement the send()
method.
Inside the app directory create a new directory named Messengers. Then, in app/Messengers, create a new file named MessengerInterface.php. In that file, paste the code below:
<?php
namespace App\Messengers;
interface MessengerInterface
{
public function send(string $text);
}
Next, create three implementations of this interface, in three new files inside the app/Messengers directory, respectively named Call.php, Email.php, and SMS.php.
Paste the code below into app/Messengers/Call.php.
<?php
namespace App\Messengers;
use Twilio\Rest\Client;
use Twilio\TwiML\VoiceResponse;
class Call implements MessengerInterface
{
public function send(string $text)
{
(app('provider'))
->calls
->create(
config('receiver.receiver_number'),
config('twilio.source_number'),
[
'url' => route(
'call.text',
[
'text' => urlencode($text)
]
)
]
);
}
}
Paste the code below into app/Messengers/Email.php.
<?php
namespace App\Messengers;
use Illuminate\Support\Facades\Mail;
class Email implements MessengerInterface
{
public function send(string $text)
{
Mail::raw($text, function ($message) {
$message
->to(config('receiver.receiver_email'))
->subject("Get Ready to feel good");
});
}
}
And paste the code below into app/Messengers/SMS.php.
<?php
namespace App\Messengers;
class SMS implements MessengerInterface
{
public function send(string $text)
{
app('provider')
->messages
->create(
config('receiver.receiver_number'),
[
'from' => config('twilio.source_number'),
'body' => $text
]
);
}
}
Now, you might be wondering what app(‘provider’)
, referenced in Call
and SMS
, is. Instead of repeating yourself by repeatedly creating a new Twilio
client, this code generates the client inside AppServiceProvider
's boot()
method, binds the instance into the Laravel service container, allowing it to be used wherever you want. A very handy timesaver!
To do all this, update the boot()
method in app/Providers/AppServiceProvider.php to match the code below:
public function boot()
{
$this->app->bind('provider', function () {
return new Client(
config('twilio.sid'),
config('twilio.auth_token'),
);
});
}
This approach also has the benefit of allowing you to switch the provider as and when you need to. For example, during testing you will be able to bind a fake provider instead of calling Twilio's APIs.
TwiML URL
As you can see in app/Messengers/Call.php, above, a URL is being passed as the third argument to the create()
method, the 'call.text' route. This URL will be called to get the TwiML (Twilio Markup Language) of the quote that needs to be played when the user answers the phone.
To define the route, add the code below to the bottom of /routes/api.php.
Route::post('/get-call-text', function (Request $request, VoiceResponse $voiceResponse) {
$voiceResponse->say($request->text);
echo $voiceResponse;
})->name('call.text');
Then, include the use
statement below, at the top of the file.
use Twilio\TwiML\VoiceResponse;
In this project, routes/api.php will only contain one route. That’s why creating a new controller class is not required. Bear in mind that this is not the best practice for all the projects.
Authentication of the URL
The last thing that you need to take care of is securing the above URL. No one, other than Twilio, should be able to access it, so you need to add some sort of authentication to the route.
How to do so has been thoroughly described in the Twilio webhook security guide, if you'd like to know more. What you need to do is to add a middleware to this route. First, generate the middleware with the following command:
./vendor/bin/sail artisan make:middleware TwilioWebhookMiddleware
It will be created in app/Http/Middleware/ and named TwilioWebhookMiddleware.php.
Now, replace the new middleware's code with the following:
<?php
class TwilioWebhookMiddleware
{
public function handle(Request $request, Closure $next)
{
$requestValidator = new RequestValidator(config('twilio.auth_token'));
$requestData = $request->post();
if (array_key_exists('bodySHA256', $requestData)) {
$requestData = $request->getContent();
}
$isValid = $requestValidator->validate(
$request->header('X-Twilio-Signature'),
$request->fullUrl(),
$requestData
);
if ($isValid) {
return $next($request);
}
abort(401,'You are not Twilio!');
}
}
Then, add the following use
statement to the top of the file.
use Twilio\Security\RequestValidator;
After creating the middleware, you have to apply it to the route you previously added to routes/api.php. Do that by updating the file to match the following code.
Route::middleware(TwilioWebhookMiddleware::class)
->post(
'/get-call-text',
function (Request $request, VoiceResponse $voiceResponse) {
$voiceResponse->say($request->text);
echo $voiceResponse;
}
)->name('call.text');
Then, add the following use
statement to the top of the file.
use App\Http\Middleware\TwilioWebhookMiddleware;
Write the command
In this step you are going to use the classes that you created to implement the functionality of the command class. Update app/Console/Commands/SendQuotes.php to match the following code.
<?php
namespace App\Console\Commands;
use App\Enums\Messenger;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
class SendQuotes extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'send:quote';
/**
* The console command description.
*
* @var string
*/
protected $description = 'This command will get a quote from php artisan inspire command and send it to specified used randomly via sms, call or email using Twilio';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
Artisan::call('inspire');
$quote = Artisan::output();
$options = Messenger::cases();
$todayMessenger = $options[array_rand($options)];
(new $todayMessenger->value())->send($quote);
}
}
Then, create a new directory named Enums and in that directory a new PHP file named Messenger.php. In that new file, paste the code below.
<?php
namespace App\Enums;
enum Messenger: string
{
case CALL = 'App\Messengers\Call';
case SMS = 'App\Messengers\SMS';
case EMAIL = 'App\Messengers\Email';
}
The Messenger
class is a PHP 8.1 enum (or enumeration) that contains namespaces of all the Messenger
implementations. You can get all the cases of this class by using Messenger::cases()
, and you can access the value of each case through the value
property. As you can see, you have to choose one of these cases randomly with array_rand() and call the send()
method of that implementation.
Schedule the command
The only thing missing is the scheduler to run this command daily at 6 O’Clock. In order to do that you have to add the below line inside the schedule()
method of app/Console/Kernel.php.
$schedule->command('send:quote')->dailyAt('06:30');
Testing
In order to test the scheduler locally, run the following command:
./vendor/bin/sail artisan schedule:work
You don’t need to wait until 6:30 in the morning to check your result. What I recommend you do is to change the dailyAt()
method to everyMinute()
and let the scheduler run your code, and change it back after your test is finished.
Here is an example of what an SMS will look like.
Conclusion
With this small project, you learnt how to implement Twilio Programmable SMS, voice and SendGrid using Laravel. I hope this article helped you to grasp a general idea of how to implement different communication services easily and with flexibility. I also hope that it helps you bring a little joy to someone special in your life.
Diba Kalantari is a backend developer at Smile IT Solutions which is a software development company based in London. You can reach her through Linkedin.
This content originally appeared on Twilio Blog and was authored by Diba Kalantari
Diba Kalantari | Sciencx (2022-12-08T10:40:12+00:00) Hype Someone Up This Christmas With Laravel Sail, Twilio Programmable Voice, and SendGrid. Retrieved from https://www.scien.cx/2022/12/08/hype-someone-up-this-christmas-with-laravel-sail-twilio-programmable-voice-and-sendgrid/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.