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
_17APP_ENV=local_17APP_DEBUG=true_17APP_KEY=ufxhZiQcKxi1eHVmGq8MwfAcRgZHJ1Qq_17_17DB_HOST=localhost_17DB_DATABASE=appointments_17DB_USERNAME=appointments_17DB_PASSWORD=appointments_17_17# Twilio API credentials_17# Found at https://www.twilio.com/user/account/settings_17TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXX_17TWILIO_AUTH_TOKEN=your_token_17_17# Twilio phone number_17# Purchase one at https://www.twilio.com/user/account/phone-numbers/incoming_17TWILIO_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
_100<?php_100_100namespace App\Http\Controllers;_100_100use Illuminate\Http\Request;_100use Illuminate\Http\Response;_100use Carbon\Carbon;_100_100use App\Http\Requests;_100use App\Http\Controllers\Controller;_100_100class AppointmentController extends Controller_100{_100 private $appointment;_100 private $validInputConditions = array(_100 'name' => 'required',_100 'phoneNumber' => 'required|min:5',_100 'when' => 'required',_100 'timezoneOffset' => 'required',_100 'delta' => 'required|numeric'_100 );_100_100 /**_100 * Display a listing of the resource._100 *_100 * @return Response_100 */_100 public function index()_100 {_100 $allAppointments = \App\Appointment::orderBy('id', 'ASC')->get();_100 return response()->view('appointment.index', array('apts' => $allAppointments));_100 }_100_100 /**_100 * Show the form for creating a new resource._100 *_100 * @return Response_100 */_100 public function create()_100 {_100 $appointment = new \App\Appointment;_100 return \View::make('appointment.create', array('appointment' => $appointment));_100 }_100_100 /**_100 * Store a newly created resource in storage._100 *_100 * @return Response_100 */_100 public function store(Request $request)_100 {_100 $newAppointment = $this->appointmentFromRequest($request);_100 $newAppointment->save();_100 return redirect()->route('appointment.index');_100 }_100_100 /**_100 * Delete a resource in storage._100 *_100 * @return Response_100 */_100 public function destroy($id) {_100 \App\Appointment::find($id)->delete();_100 return redirect()->route('appointment.index');_100 }_100_100 public function edit($id) {_100 $appointmentToEdit = \App\Appointment::find($id);_100 return \View::make('appointment.edit', array('appointment' => $appointmentToEdit));_100 }_100_100 public function update(Request $request, $id) {_100 $updatedAppointment = $this->appointmentFromRequest($request);_100 $existingAppointment = \App\Appointment::find($id);_100_100 $existingAppointment->name = $updatedAppointment->name;_100 $existingAppointment->phoneNumber = $updatedAppointment->phoneNumber;_100 $existingAppointment->timezoneOffset = $updatedAppointment->timezoneOffset;_100 $existingAppointment->when = $updatedAppointment->when;_100 $existingAppointment->notificationTime = $updatedAppointment->notificationTime;_100_100 $existingAppointment->save();_100 return redirect()->route('appointment.index');_100 }_100_100 private function appointmentFromRequest(Request $request) {_100 $this->validate($request, $this->validInputConditions);_100 $newAppointment = new \App\Appointment;_100_100 $newAppointment->name = $request->input('name');_100 $newAppointment->phoneNumber = $request->input('phoneNumber');_100 $newAppointment->timezoneOffset = $request->input('timezoneOffset');_100 $newAppointment->when = $request->input('when');_100_100 $notificationTime = Carbon::parse($request->input('when'))->subMinutes($request->delta);_100 $newAppointment->notificationTime = $notificationTime;_100_100 return $newAppointment;_100 }_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
_41<?php_41_41namespace App\Console;_41_41use Illuminate\Console\Scheduling\Schedule;_41use Illuminate\Foundation\Console\Kernel as ConsoleKernel;_41_41class Kernel extends ConsoleKernel_41{_41 /**_41 * The Artisan commands provided by your application._41 *_41 * @var array_41 */_41 protected $commands = [_41 '\App\Console\Commands\SendReminders'_41 ];_41_41 /**_41 * Define the application's command schedule._41 *_41 * @param \Illuminate\Console\Scheduling\Schedule $schedule_41 * @return void_41 */_41 protected function schedule(Schedule $schedule)_41 {_41 $schedule->command('reminders:send')->everyMinute();_41 }_41_41 /**_41 * Register the commands for the application._41 *_41 * @return void_41 */_41 protected function commands()_41 {_41 $this->load(__DIR__.'/Commands');_41_41 require base_path('routes/console.php');_41 }_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
_43<?php_43_43namespace App\Console\Commands;_43_43use Illuminate\Console\Command;_43_43class SendReminders extends Command_43{_43 /**_43 * The name and signature of the console command._43 *_43 * @var string_43 */_43 protected $signature = 'reminders:send';_43_43 /**_43 * The console command description._43 *_43 * @var string_43 */_43 protected $description = 'Send reminders using Twilio';_43_43 /**_43 * Create a new command instance._43 *_43 * @return void_43 */_43 public function __construct()_43 {_43 parent::__construct();_43 }_43_43 /**_43 * Execute the console command._43 *_43 * @return void_43 */_43 public function handle()_43 {_43 $appointmentReminder = new \App\AppointmentReminders\AppointmentReminder();_43 $appointmentReminder->sendReminders();_43 }_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
_78<?php_78_78namespace App\AppointmentReminders;_78_78use Illuminate\Log;_78use Carbon\Carbon;_78use Twilio\Rest\Client;_78_78class AppointmentReminder_78{_78 /**_78 * Construct a new AppointmentReminder_78 *_78 * @param Illuminate\Support\Collection $twilioClient The client to use to query the API_78 */_78 function __construct()_78 {_78 $this->appointments = \App\Appointment::appointmentsDue()->get();_78_78 $twilioConfig =\Config::get('services.twilio');_78 $accountSid = $twilioConfig['twilio_account_sid'];_78 $authToken = $twilioConfig['twilio_auth_token'];_78 $this->sendingNumber = $twilioConfig['twilio_number'];_78_78 $this->twilioClient = new Client($accountSid, $authToken);_78 }_78_78 /**_78 * Send reminders for each appointment_78 *_78 * @return void_78 */_78 public function sendReminders()_78 {_78 $this->appointments->each(_78 function ($appointment) {_78 $this->_remindAbout($appointment);_78 }_78 );_78 }_78_78 /**_78 * Sends a message for an appointment_78 *_78 * @param Appointment $appointment The appointment to remind_78 *_78 * @return void_78 */_78 private function _remindAbout($appointment)_78 {_78 $recipientName = $appointment->name;_78 $time = Carbon::parse($appointment->when, 'UTC')_78 ->subMinutes($appointment->timezoneOffset)_78 ->format('g:i a');_78_78 $message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";_78 $this->_sendMessage($appointment->phoneNumber, $message);_78 }_78_78 /**_78 * Sends a single message using the app's global configuration_78 *_78 * @param string $number The number to message_78 * @param string $content The content of the message_78 *_78 * @return void_78 */_78 private function _sendMessage($number, $content)_78 {_78 $this->twilioClient->messages->create(_78 $number,_78 array(_78 "from" => $this->sendingNumber,_78 "body" => $content_78 )_78 );_78 }_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
_78<?php_78_78namespace App\AppointmentReminders;_78_78use Illuminate\Log;_78use Carbon\Carbon;_78use Twilio\Rest\Client;_78_78class AppointmentReminder_78{_78 /**_78 * Construct a new AppointmentReminder_78 *_78 * @param Illuminate\Support\Collection $twilioClient The client to use to query the API_78 */_78 function __construct()_78 {_78 $this->appointments = \App\Appointment::appointmentsDue()->get();_78_78 $twilioConfig =\Config::get('services.twilio');_78 $accountSid = $twilioConfig['twilio_account_sid'];_78 $authToken = $twilioConfig['twilio_auth_token'];_78 $this->sendingNumber = $twilioConfig['twilio_number'];_78_78 $this->twilioClient = new Client($accountSid, $authToken);_78 }_78_78 /**_78 * Send reminders for each appointment_78 *_78 * @return void_78 */_78 public function sendReminders()_78 {_78 $this->appointments->each(_78 function ($appointment) {_78 $this->_remindAbout($appointment);_78 }_78 );_78 }_78_78 /**_78 * Sends a message for an appointment_78 *_78 * @param Appointment $appointment The appointment to remind_78 *_78 * @return void_78 */_78 private function _remindAbout($appointment)_78 {_78 $recipientName = $appointment->name;_78 $time = Carbon::parse($appointment->when, 'UTC')_78 ->subMinutes($appointment->timezoneOffset)_78 ->format('g:i a');_78_78 $message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";_78 $this->_sendMessage($appointment->phoneNumber, $message);_78 }_78_78 /**_78 * Sends a single message using the app's global configuration_78 *_78 * @param string $number The number to message_78 * @param string $content The content of the message_78 *_78 * @return void_78 */_78 private function _sendMessage($number, $content)_78 {_78 $this->twilioClient->messages->create(_78 $number,_78 array(_78 "from" => $this->sendingNumber,_78 "body" => $content_78 )_78 );_78 }_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
_78<?php_78_78namespace App\AppointmentReminders;_78_78use Illuminate\Log;_78use Carbon\Carbon;_78use Twilio\Rest\Client;_78_78class AppointmentReminder_78{_78 /**_78 * Construct a new AppointmentReminder_78 *_78 * @param Illuminate\Support\Collection $twilioClient The client to use to query the API_78 */_78 function __construct()_78 {_78 $this->appointments = \App\Appointment::appointmentsDue()->get();_78_78 $twilioConfig =\Config::get('services.twilio');_78 $accountSid = $twilioConfig['twilio_account_sid'];_78 $authToken = $twilioConfig['twilio_auth_token'];_78 $this->sendingNumber = $twilioConfig['twilio_number'];_78_78 $this->twilioClient = new Client($accountSid, $authToken);_78 }_78_78 /**_78 * Send reminders for each appointment_78 *_78 * @return void_78 */_78 public function sendReminders()_78 {_78 $this->appointments->each(_78 function ($appointment) {_78 $this->_remindAbout($appointment);_78 }_78 );_78 }_78_78 /**_78 * Sends a message for an appointment_78 *_78 * @param Appointment $appointment The appointment to remind_78 *_78 * @return void_78 */_78 private function _remindAbout($appointment)_78 {_78 $recipientName = $appointment->name;_78 $time = Carbon::parse($appointment->when, 'UTC')_78 ->subMinutes($appointment->timezoneOffset)_78 ->format('g:i a');_78_78 $message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";_78 $this->_sendMessage($appointment->phoneNumber, $message);_78 }_78_78 /**_78 * Sends a single message using the app's global configuration_78 *_78 * @param string $number The number to message_78 * @param string $content The content of the message_78 *_78 * @return void_78 */_78 private function _sendMessage($number, $content)_78 {_78 $this->twilioClient->messages->create(_78 $number,_78 array(_78 "from" => $this->sendingNumber,_78 "body" => $content_78 )_78 );_78 }_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.