Laravel and Twilio: Cloud communications in a nutshell

April 05, 2024 • 6 min read

Home / Blog / Laravel and Twilio: Cloud communications in a nutshell
Laravel and Twilio: Cloud communications in a nutshell | Learn how to send SMS using Laravel and Twilio in a step-by-step tutorial

Introduction

In a previous chapter, I added a small guide with my tips on how to get your 10DLC phone number verified in Twilio, but I never made the guide on how to wrap everything up to send SMS from within your platform. That being said, Twilio itself offers some small code implementations on different programming languages in order to send the SMS. I want to go one step further and share my code implementation for a real-world app that handles sending around 3000 messages/day. Let's get to it.

 

Note: in order to send SMS from a 10DLC number in a production environment, you need to have an approved and active A2P 10DLC campaign. Please refer to https://ggomez.dev/blog/twilio-create-an-a2p-10dlc-campaign for more information.

 

Additionally, if you need to send SMS from a Toll-Free number, you'll need to go through the Toll-Free Message Verification inside Twilio. Please refer to https://help.twilio.com/articles/5377174717595-Toll-Free-Message-Verification-for-US-Canada for more information.

 

Getting started

There are 2 possible ways of using Twilio in our app: through their SDK's or their API. This guide will make use of the SDK since it already communicates with the Twilio API for us.

 

In your Laravel (or PHP for that matter) application, install the Twilio SDK

composer require twilio/sdk

 

Next, it's all about making use of the send() method of the Twilio client that we need to instanciate from within our app. Personally, I like to have all my logic across services and make use of the Dependency Injection pattern when building in Laravel, so let's see how we can use the Twilio SDK in our service:

 

app\Services\TwilioMessageService.php

<?php

namespace App\Services;

use App\Models\TwilioSmsLog;
use Error;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;
use Twilio\Rest\Api\V2010\Account\MessageInstance;
use Twilio\Rest\Client;

class TwilioMessageService
{
    public function send(Model $model, string $message)
    {
        $client = new Client(config('services.twilio.account_sid'), config('services.twilio.auth_token'));

        $twilioMessage = $client->messages->create(
            $model->PhoneNumberE164,
            array(
                'from' => config('services.twilio.from'),
                'body' => $message
            )
        );

        $this->saveLogs($twilioMessage);
    }

    protected function saveLogs(mixed $message)
    {
        try {
            if (!$message instanceof MessageInstance) {
                throw new Error("The message is not an instance of MessageInstance: {$message}");
            }

            TwilioSmsLog::create([
                'sid' => $message->sid,
                'body' => $message->body,
                'direction' => $message->direction,
                'from' => $message->from,
                'to' => $message->to,
                'dateCreated' => $message->dateCreated,
                'dateUpdated' => $message->dateUpdated,
                'price' => $message->price,
                'accountSid' => $message->accountSid,
                'status' => $message->status,
                'priceUnit' => $message->priceUnit,
                'apiVersion' => $message->apiVersion
            ]);
        } catch (Exception $ex) {
            Log::error("Something went wrong storing the SMS logs: {$ex->getMessage()} at {$ex->getLine()}");
        }
    }
}

 

That's our basic service, which can be extended to use even more functionalities that the SDK can provide to us. Next, we can create an SMS service, which is going to interact with our Twilio service:

 

app\Services\SmsService.php

<?php

namespace App\Services;

use App\Models\AppointmentMessage;
use App\Services\TwilioMessageService;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Blade;

class SmsService
{
    public TwilioMessageService $twilioMessageService;
    public VonageService $vonageService;

    public function __construct(TwilioMessageService $twilioMessageService, VonageService $vonageService)
    {
        $this->twilioMessageService = $twilioMessageService;
        $this->vonageService = $vonageService;
    }

    /**
     * Send an SMS to either a customer or a lead model.
     * The type of message to send is determined by the type parameter.
     * 
     * @return void
     */
    public function send(string $type, Model $model)
    {
        $appointmentMessage = AppointmentMessage::where('type', $type)->first();

        if ($appointmentMessage) {
            $data = $this->mapMessageVariables($type, $model);
            $message = Blade::render($appointmentMessage->message, $data, true);

            $this->twilioMessageService->send($model, $message);
        }
    }

    /**
     * Send a raw message to a customer or a lead model.
     * 
     * @return void
     */
    public function sendRaw(Model $model, string $message)
    {
        $this->twilioMessageService->send($model, $message);
    }

    /**
     * Map the message variables to send in the SMS.
     * 
     * @return array
     */
    protected function mapMessageVariables(string $type, Model $model): array
    {
        switch ($type) {
            case 'created':
                return [
                    'clientName' => "{$model->Name} {$model->Surname}",
                    'date' => date('m/d/Y', strtotime($model->AppointmentDate)),
                    'time' => Carbon::createFromFormat('H:i:s', $model->AppointmentTime)->format('g:iA'),
                    'timezone' => $model->office->availabilityProfile->Timezone,
                    'officeAddress' => $model->office->address,
                    'officePhoneNumber' => $model->office->PhoneNumber
                ];
            break;

            case 'updated':
                return [
                    'clientName' => "{$model->Name} {$model->Surname}",
                    'date' => date('m/d/Y', strtotime($model->AppointmentDate)),
                    'time' => Carbon::createFromFormat('H:i:s', $model->AppointmentTime)->format('g:iA'),
                    'timezone' => $model->office->availabilityProfile->Timezone,
                    'officeAddress' => $model->office->address,
                    'officePhoneNumber' => $model->office->PhoneNumber
                ];
            break;

            case 'cancelled':
                return [
                    'clientName' => "{$model->Name} {$model->Surname}",
                    'date' => date('m/d/Y', strtotime($model->AppointmentDate)),
                    'timezone' => $model->office->availabilityProfile->Timezone,
                    'officePhoneNumber' => $model->office->PhoneNumber,
                    'time' => Carbon::createFromFormat('H:i:s', $model->AppointmentTime)->format('g:iA'),
                ];
            break;

            case 'existing_client':
                return [
                    'officeName' => $model->office->name,
                    'officePhoneNumber' => $model->office->PhoneNumber
                ];
            break;

            default:
                return [];
            break;
        }
    }
}

 

The idea here is to decouple the SmsService from the TwilioService because you might want to change your cloud communications provider in the future while minimizing the code impact. In this example, by using the SmsService as a single point of contact in our app, we can swap between the Twilio and Vonage service to send the SMS without the client side knowing about such implementation and changes.

 

Conclusion

It is also important to note that, depending on your architecture, you might want to queue your messages before sending them to Twilio in, for example, batches. I personally did not use that approach because I rather Twilio queue up the messages for me on their end, as I do not have a need for messages to be instantly delivered, althought the throughput is defined based on your A2P 10DLC campaign.

 

That's all for now, if you found the implementation helpful, feel free to share!

-Gonza

682
Laravel,  Twilio
Published on April 05, 2024

Ready to take your project to the next level?

Contact me

About the author

Author

Gonzalo Gomez

Sr. Software Engineer

Senior software engineer located in Buenos Aires, Argentina. I specialize in building highly scalable web applications and I've been delivering MVPs and helping companies with their digital transformation for the past 7 years. I am proficient in a variety of technologies, including Laravel, Vue.js, Twilio, AWS, React.js, Node.js and MySQL.

Subscribe to my newsletter

If you enjoy the content that I make, you can subscribe and receive insightful information through email. No spam is going to be sent, just updates about interesting posts or specialized content that I talk about.

Related posts

Laravel 12, Laravel Cloud and Starter Kits

February 27, 2025
Introducing Laravel 12: Enhancements and New Features for Web Developers Laravel 12, the latest iteration of the popular PHP framework, has been released with a focus... Continue reading

Supercharge Your Contact Center: Building Custom Twilio Flex Plugins with React

April 30, 2025
IntroductionTwilio Flex is often sold as the programmable contact center, but too many companies just scratch the surface. With custom plugins, you can turn Flex... Continue reading

How to Stop Answering the Same Customer Questions 5x a Day (Without Hiring More Agents)

May 12, 2025
IntroductionYour team is answering the same exact questions every single day: “What’s your address?”“Are you open on holidays?”“What’s the return policy?”“Do you offer WhatsApp support?” Whether it’s... Continue reading

Traits and Laravel: the practical guide

April 18, 2024
IntroductionTraits are a mechanism for code re-use in single inheritance languages such as PHP. They were designed to allow us developers to save logic and... Continue reading