Ahoy! We now recommend you build your appointment reminders with Twilio's built-in Message Scheduling functionality. Head on over to the Message Scheduling documentation to learn more about scheduling messages.
This Laravel 5 web application shows how to create appointments for customers at a date in future, and send out reminders for those appointments in a background job that runs every few minutes.
In this tutorial, we'll point out the key bits of code that make this application work. Check out the project README on GitHub to see how to run the code yourself.
Check out how Yelp uses SMS to confirm restaurant reservations for diners.
Let's get started! Click the button below to begin.
Before we can use the Twilio API to send reminder text messages, we need to configure our account credentials. These can be found on your Twilio Console. You'll also need an SMS-enabled phone number - you can find or purchase a new one to use here.
We configure these values using Dotenv, the configuration library of choice for Laravel applications. More information on how to configure this application can be found in the project README.
.env.example
1APP_ENV=local2APP_DEBUG=true3APP_KEY=ufxhZiQcKxi1eHVmGq8MwfAcRgZHJ1Qq45DB_HOST=localhost6DB_DATABASE=appointments7DB_USERNAME=appointments8DB_PASSWORD=appointments910# Twilio API credentials11# Found at https://www.twilio.com/user/account/settings12TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXX13TWILIO_AUTH_TOKEN=your_token1415# Twilio phone number16# Purchase one at https://www.twilio.com/user/account/phone-numbers/incoming17TWILIO_NUMBER=your_twilio_number
Next let's see how we create a new Appointment
.
In order to send an appointment reminder, we first need to create an appointment! In the controller, we take information submitted in a form (notably a customer's name and phone number, plus a time for the appointment in the future) and save it in an Appointment
model.
We use the Carbon date library to parse and do operations with the time.
app/Http/Controllers/AppointmentController.php
1<?php23namespace App\Http\Controllers;45use Illuminate\Http\Request;6use Illuminate\Http\Response;7use Carbon\Carbon;89use App\Http\Requests;10use App\Http\Controllers\Controller;1112class AppointmentController extends Controller13{14private $appointment;15private $validInputConditions = array(16'name' => 'required',17'phoneNumber' => 'required|min:5',18'when' => 'required',19'timezoneOffset' => 'required',20'delta' => 'required|numeric'21);2223/**24* Display a listing of the resource.25*26* @return Response27*/28public function index()29{30$allAppointments = \App\Appointment::orderBy('id', 'ASC')->get();31return response()->view('appointment.index', array('apts' => $allAppointments));32}3334/**35* Show the form for creating a new resource.36*37* @return Response38*/39public function create()40{41$appointment = new \App\Appointment;42return \View::make('appointment.create', array('appointment' => $appointment));43}4445/**46* Store a newly created resource in storage.47*48* @return Response49*/50public function store(Request $request)51{52$newAppointment = $this->appointmentFromRequest($request);53$newAppointment->save();54return redirect()->route('appointment.index');55}5657/**58* Delete a resource in storage.59*60* @return Response61*/62public function destroy($id) {63\App\Appointment::find($id)->delete();64return redirect()->route('appointment.index');65}6667public function edit($id) {68$appointmentToEdit = \App\Appointment::find($id);69return \View::make('appointment.edit', array('appointment' => $appointmentToEdit));70}7172public function update(Request $request, $id) {73$updatedAppointment = $this->appointmentFromRequest($request);74$existingAppointment = \App\Appointment::find($id);7576$existingAppointment->name = $updatedAppointment->name;77$existingAppointment->phoneNumber = $updatedAppointment->phoneNumber;78$existingAppointment->timezoneOffset = $updatedAppointment->timezoneOffset;79$existingAppointment->when = $updatedAppointment->when;80$existingAppointment->notificationTime = $updatedAppointment->notificationTime;8182$existingAppointment->save();83return redirect()->route('appointment.index');84}8586private function appointmentFromRequest(Request $request) {87$this->validate($request, $this->validInputConditions);88$newAppointment = new \App\Appointment;8990$newAppointment->name = $request->input('name');91$newAppointment->phoneNumber = $request->input('phoneNumber');92$newAppointment->timezoneOffset = $request->input('timezoneOffset');93$newAppointment->when = $request->input('when');9495$notificationTime = Carbon::parse($request->input('when'))->subMinutes($request->delta);96$newAppointment->notificationTime = $notificationTime;9798return $newAppointment;99}100}
Now that we have our Appointment
created, let's see how to schedule a reminder for it.
Every ten minutes, we'd like our application to check the appointments database to see if any appointments are coming up that require reminders to be sent out. We configure both the job code we'd like to run and the interval on which to run it here.
app/Console/Kernel.php
1<?php23namespace App\Console;45use Illuminate\Console\Scheduling\Schedule;6use Illuminate\Foundation\Console\Kernel as ConsoleKernel;78class Kernel extends ConsoleKernel9{10/**11* The Artisan commands provided by your application.12*13* @var array14*/15protected $commands = [16'\App\Console\Commands\SendReminders'17];1819/**20* Define the application's command schedule.21*22* @param \Illuminate\Console\Scheduling\Schedule $schedule23* @return void24*/25protected function schedule(Schedule $schedule)26{27$schedule->command('reminders:send')->everyMinute();28}2930/**31* Register the commands for the application.32*33* @return void34*/35protected function commands()36{37$this->load(__DIR__.'/Commands');3839require base_path('routes/console.php');40}41}
With our job configured, we're now ready to write the actual console command code that will send out our reminders.
To actually execute our recurring job logic, we create an Artisan console command which queries the database for upcoming appointments and sends reminders as necessary. As an added bonus, defining our job logic in this way allows us to run the reminder job whenever we want from the command line.
app/Console/Commands/SendReminders.php
1<?php23namespace App\Console\Commands;45use Illuminate\Console\Command;67class SendReminders extends Command8{9/**10* The name and signature of the console command.11*12* @var string13*/14protected $signature = 'reminders:send';1516/**17* The console command description.18*19* @var string20*/21protected $description = 'Send reminders using Twilio';2223/**24* Create a new command instance.25*26* @return void27*/28public function __construct()29{30parent::__construct();31}3233/**34* Execute the console command.35*36* @return void37*/38public function handle()39{40$appointmentReminder = new \App\AppointmentReminders\AppointmentReminder();41$appointmentReminder->sendReminders();42}43}
Let's dig further into the ApplicationReminder class
Our recurring job uses an instance of the AppointmentReminder
class to query the database for appointments coming up in the next ten minutes and send out reminder messages.
In the constructor, we execute the database query using a custom scope on the Appointment
model. This should give us a list of all appointments with a date and time that falls within the next ten minutes.
app/AppointmentReminders/AppointmentReminder.php
1<?php23namespace App\AppointmentReminders;45use Illuminate\Log;6use Carbon\Carbon;7use Twilio\Rest\Client;89class AppointmentReminder10{11/**12* Construct a new AppointmentReminder13*14* @param Illuminate\Support\Collection $twilioClient The client to use to query the API15*/16function __construct()17{18$this->appointments = \App\Appointment::appointmentsDue()->get();1920$twilioConfig =\Config::get('services.twilio');21$accountSid = $twilioConfig['twilio_account_sid'];22$authToken = $twilioConfig['twilio_auth_token'];23$this->sendingNumber = $twilioConfig['twilio_number'];2425$this->twilioClient = new Client($accountSid, $authToken);26}2728/**29* Send reminders for each appointment30*31* @return void32*/33public function sendReminders()34{35$this->appointments->each(36function ($appointment) {37$this->_remindAbout($appointment);38}39);40}4142/**43* Sends a message for an appointment44*45* @param Appointment $appointment The appointment to remind46*47* @return void48*/49private function _remindAbout($appointment)50{51$recipientName = $appointment->name;52$time = Carbon::parse($appointment->when, 'UTC')53->subMinutes($appointment->timezoneOffset)54->format('g:i a');5556$message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";57$this->_sendMessage($appointment->phoneNumber, $message);58}5960/**61* Sends a single message using the app's global configuration62*63* @param string $number The number to message64* @param string $content The content of the message65*66* @return void67*/68private function _sendMessage($number, $content)69{70$this->twilioClient->messages->create(71$number,72array(73"from" => $this->sendingNumber,74"body" => $content75)76);77}78}
Now let's setup the Twilio REST Client in order to send some SMS reminder messages.
Also in the AppointmentReminder
constructor, we create a Twilio REST Client to send out reminders via SMS. We initialize it with the Twilio account credentials we configured earlier.
app/AppointmentReminders/AppointmentReminder.php
1<?php23namespace App\AppointmentReminders;45use Illuminate\Log;6use Carbon\Carbon;7use Twilio\Rest\Client;89class AppointmentReminder10{11/**12* Construct a new AppointmentReminder13*14* @param Illuminate\Support\Collection $twilioClient The client to use to query the API15*/16function __construct()17{18$this->appointments = \App\Appointment::appointmentsDue()->get();1920$twilioConfig =\Config::get('services.twilio');21$accountSid = $twilioConfig['twilio_account_sid'];22$authToken = $twilioConfig['twilio_auth_token'];23$this->sendingNumber = $twilioConfig['twilio_number'];2425$this->twilioClient = new Client($accountSid, $authToken);26}2728/**29* Send reminders for each appointment30*31* @return void32*/33public function sendReminders()34{35$this->appointments->each(36function ($appointment) {37$this->_remindAbout($appointment);38}39);40}4142/**43* Sends a message for an appointment44*45* @param Appointment $appointment The appointment to remind46*47* @return void48*/49private function _remindAbout($appointment)50{51$recipientName = $appointment->name;52$time = Carbon::parse($appointment->when, 'UTC')53->subMinutes($appointment->timezoneOffset)54->format('g:i a');5556$message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";57$this->_sendMessage($appointment->phoneNumber, $message);58}5960/**61* Sends a single message using the app's global configuration62*63* @param string $number The number to message64* @param string $content The content of the message65*66* @return void67*/68private function _sendMessage($number, $content)69{70$this->twilioClient->messages->create(71$number,72array(73"from" => $this->sendingNumber,74"body" => $content75)76);77}78}
With the client and the reminders in hand. All that is left is to send an SMS for them.
These two private functions are called for every appointment coming up that requires a reminder to be sent. The first formats the text of the message to be sent out. The second actually uses the Twilio REST API client to send out a text message.
We provide a to
parameter which is the customer's phone number, a from
parameter which is a number in our account, and a body
parameter which contains the text of the message.
app/AppointmentReminders/AppointmentReminder.php
1<?php23namespace App\AppointmentReminders;45use Illuminate\Log;6use Carbon\Carbon;7use Twilio\Rest\Client;89class AppointmentReminder10{11/**12* Construct a new AppointmentReminder13*14* @param Illuminate\Support\Collection $twilioClient The client to use to query the API15*/16function __construct()17{18$this->appointments = \App\Appointment::appointmentsDue()->get();1920$twilioConfig =\Config::get('services.twilio');21$accountSid = $twilioConfig['twilio_account_sid'];22$authToken = $twilioConfig['twilio_auth_token'];23$this->sendingNumber = $twilioConfig['twilio_number'];2425$this->twilioClient = new Client($accountSid, $authToken);26}2728/**29* Send reminders for each appointment30*31* @return void32*/33public function sendReminders()34{35$this->appointments->each(36function ($appointment) {37$this->_remindAbout($appointment);38}39);40}4142/**43* Sends a message for an appointment44*45* @param Appointment $appointment The appointment to remind46*47* @return void48*/49private function _remindAbout($appointment)50{51$recipientName = $appointment->name;52$time = Carbon::parse($appointment->when, 'UTC')53->subMinutes($appointment->timezoneOffset)54->format('g:i a');5556$message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";57$this->_sendMessage($appointment->phoneNumber, $message);58}5960/**61* Sends a single message using the app's global configuration62*63* @param string $number The number to message64* @param string $content The content of the message65*66* @return void67*/68private function _sendMessage($number, $content)69{70$this->twilioClient->messages->create(71$number,72array(73"from" => $this->sendingNumber,74"body" => $content75)76);77}78}
That's it! Our Laravel application is all set to send out reminders for upcoming appointments.
We hope you found this sample application useful. If you're a PHP developer working with Twilio, you might enjoy these other tutorials:
Put a button on your web page that connects visitors to live support or sales people via telephone.
Improve the security of Laravel's built-in login functionality by adding two-factor authentication via text message.