Appointment Reminders with PHP and Laravel

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.

Configure the application to use Twilio

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.

Loading Code Samples...
Language
SDK Version:
  • env
APP_ENV=local
APP_DEBUG=true
APP_KEY=ufxhZiQcKxi1eHVmGq8MwfAcRgZHJ1Qq

DB_HOST=localhost
DB_DATABASE=appointments
DB_USERNAME=appointments
DB_PASSWORD=appointments

# Twilio API credentials
# Found at https://www.twilio.com/user/account/settings
TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXX
TWILIO_AUTH_TOKEN=your_token

# Twilio phone number
# Purchase one at https://www.twilio.com/user/account/phone-numbers/incoming
TWILIO_NUMBER=your_twilio_number
.env.example
Configure the application

.env.example

Next let's see how we create a new Appointment.

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.

Notice that we use the Carbon date library to make it easier for us to parse and do simple operations with the time.

Loading Code Samples...
Language
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Carbon\Carbon;

use App\Http\Requests;
use App\Http\Controllers\Controller;

class AppointmentController extends Controller
{
    private $appointment;
    private $validInputConditions = array(
        'name' => 'required',
        'phoneNumber' => 'required|min:5',
        'when' => 'required',
        'timezoneOffset' => 'required',
        'delta' => 'required|numeric'
    );

    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index()
    {
        $allAppointments = \App\Appointment::orderBy('id', 'ASC')->get();
        return response()->view('appointment.index', array('apts' => $allAppointments));
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return Response
     */
    public function create()
    {
        $appointment = new \App\Appointment;
        return \View::make('appointment.create', array('appointment' => $appointment));
    }

    /**
     * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store(Request $request)
    {
        $newAppointment = $this->appointmentFromRequest($request);
        $newAppointment->save();
        return redirect()->route('appointment.index');
    }

    /**
     * Delete a resource in storage.
     *
     * @return Response
     */
    public function destroy($id) {
        \App\Appointment::find($id)->delete();
        return redirect()->route('appointment.index');
    }

    public function edit($id) {
        $appointmentToEdit = \App\Appointment::find($id);
        return \View::make('appointment.edit', array('appointment' => $appointmentToEdit));
    }

    public function update(Request $request, $id) {
        $updatedAppointment = $this->appointmentFromRequest($request);
        $existingAppointment = \App\Appointment::find($id);

        $existingAppointment->name = $updatedAppointment->name;
        $existingAppointment->phoneNumber = $updatedAppointment->phoneNumber;
        $existingAppointment->timezoneOffset = $updatedAppointment->timezoneOffset;
        $existingAppointment->when = $updatedAppointment->when;
        $existingAppointment->notificationTime = $updatedAppointment->notificationTime;

        $existingAppointment->save();
        return redirect()->route('appointment.index');
    }

    private function appointmentFromRequest(Request $request) {
        $this->validate($request, $this->validInputConditions);
        $newAppointment = new \App\Appointment;

        $newAppointment->name = $request->input('name');
        $newAppointment->phoneNumber = $request->input('phoneNumber');
        $newAppointment->timezoneOffset = $request->input('timezoneOffset');
        $newAppointment->when = $request->input('when');

        $notificationTime = Carbon::parse($request->input('when'))->subMinutes($request->delta);
        $newAppointment->notificationTime = $notificationTime;

        return $newAppointment;
    }
}
app/Http/Controllers/AppointmentController.php
Create a new appointment

app/Http/Controllers/AppointmentController.php

Now that we have our Appointment created, let's see how to schedule a reminder for it.

Schedule a job to send reminders

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.

Loading Code Samples...
Language
<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        '\App\Console\Commands\SendReminders'
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('reminders:send')->everyMinute();
    }
}
app/Console/Kernel.php
Schedule a job to send reminders

app/Console/Kernel.php

With our job configured, we're now ready to write the actual console command code that will send out our reminders.

Create a console command to run the job

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.

Loading Code Samples...
Language
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class SendReminders extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'reminders:send';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Send reminders using Twilio';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        $appointmentReminder = new \App\AppointmentReminders\AppointmentReminder();
        $appointmentReminder->sendReminders();
    }
}
app/Console/Commands/SendReminders.php
Create a console command to run the job

app/Console/Commands/SendReminders.php

Let's dig further into the ApplicationReminder class

Find appointments that need reminders

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.

Loading Code Samples...
Language
<?php

namespace App\AppointmentReminders;

use Illuminate\Log;
use Carbon\Carbon;
use Twilio\Rest\Client;

class AppointmentReminder
{
    /**
     * Construct a new AppointmentReminder
     *
     * @param Illuminate\Support\Collection $twilioClient The client to use to query the API
     */
    function __construct()
    {
        $this->appointments = \App\Appointment::appointmentsDue()->get();

        $twilioConfig = config('services.twilio');
        $accountSid = $twilioConfig['twilio_account_sid'];
        $authToken = $twilioConfig['twilio_auth_token'];
        $this->sendingNumber = $twilioConfig['twilio_number'];

        $this->twilioClient = new Client($accountSid, $authToken);
    }

    /**
     * Send reminders for each appointment
     *
     * @return void
     */
    public function sendReminders()
    {
        $this->appointments->each(
            function ($appointment) {
                $this->_remindAbout($appointment);
            }
        );
    }

    /**
     * Sends a message for an appointment
     *
     * @param Appointment $appointment The appointment to remind
     *
     * @return void
     */
    private function _remindAbout($appointment)
    {
        $recipientName = $appointment->name;
        $time = Carbon::parse($appointment->when, 'UTC')
              ->subMinutes($appointment->timezoneOffset)
              ->format('g:i a');

        $message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";
        $this->_sendMessage($appointment->phoneNumber, $message);
    }

    /**
     * Sends a single message using the app's global configuration
     *
     * @param string $number  The number to message
     * @param string $content The content of the message
     *
     * @return void
     */
    private function _sendMessage($number, $content)
    {
        $this->twilioClient->messages->create(
            $number,
            array(
                "from" => $this->sendingNumber,
                "body" => $content
            )
        );
    }
}
app/AppointmentReminders/AppointmentReminder.php
Find appointments that need reminders

app/AppointmentReminders/AppointmentReminder.php

Now let's setup the Twilio REST Client in order to send some SMS reminder messages.

Set up a Twilio API client

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.

Loading Code Samples...
Language
<?php

namespace App\AppointmentReminders;

use Illuminate\Log;
use Carbon\Carbon;
use Twilio\Rest\Client;

class AppointmentReminder
{
    /**
     * Construct a new AppointmentReminder
     *
     * @param Illuminate\Support\Collection $twilioClient The client to use to query the API
     */
    function __construct()
    {
        $this->appointments = \App\Appointment::appointmentsDue()->get();

        $twilioConfig = config('services.twilio');
        $accountSid = $twilioConfig['twilio_account_sid'];
        $authToken = $twilioConfig['twilio_auth_token'];
        $this->sendingNumber = $twilioConfig['twilio_number'];

        $this->twilioClient = new Client($accountSid, $authToken);
    }

    /**
     * Send reminders for each appointment
     *
     * @return void
     */
    public function sendReminders()
    {
        $this->appointments->each(
            function ($appointment) {
                $this->_remindAbout($appointment);
            }
        );
    }

    /**
     * Sends a message for an appointment
     *
     * @param Appointment $appointment The appointment to remind
     *
     * @return void
     */
    private function _remindAbout($appointment)
    {
        $recipientName = $appointment->name;
        $time = Carbon::parse($appointment->when, 'UTC')
              ->subMinutes($appointment->timezoneOffset)
              ->format('g:i a');

        $message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";
        $this->_sendMessage($appointment->phoneNumber, $message);
    }

    /**
     * Sends a single message using the app's global configuration
     *
     * @param string $number  The number to message
     * @param string $content The content of the message
     *
     * @return void
     */
    private function _sendMessage($number, $content)
    {
        $this->twilioClient->messages->create(
            $number,
            array(
                "from" => $this->sendingNumber,
                "body" => $content
            )
        );
    }
}
app/AppointmentReminders/AppointmentReminder.php
Set up a Twilio API client

app/AppointmentReminders/AppointmentReminder.php

With the client and the reminders in hand. All that is left is to send an SMS for them.

Send reminder messages with the Twilio API

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.

Loading Code Samples...
Language
<?php

namespace App\AppointmentReminders;

use Illuminate\Log;
use Carbon\Carbon;
use Twilio\Rest\Client;

class AppointmentReminder
{
    /**
     * Construct a new AppointmentReminder
     *
     * @param Illuminate\Support\Collection $twilioClient The client to use to query the API
     */
    function __construct()
    {
        $this->appointments = \App\Appointment::appointmentsDue()->get();

        $twilioConfig = config('services.twilio');
        $accountSid = $twilioConfig['twilio_account_sid'];
        $authToken = $twilioConfig['twilio_auth_token'];
        $this->sendingNumber = $twilioConfig['twilio_number'];

        $this->twilioClient = new Client($accountSid, $authToken);
    }

    /**
     * Send reminders for each appointment
     *
     * @return void
     */
    public function sendReminders()
    {
        $this->appointments->each(
            function ($appointment) {
                $this->_remindAbout($appointment);
            }
        );
    }

    /**
     * Sends a message for an appointment
     *
     * @param Appointment $appointment The appointment to remind
     *
     * @return void
     */
    private function _remindAbout($appointment)
    {
        $recipientName = $appointment->name;
        $time = Carbon::parse($appointment->when, 'UTC')
              ->subMinutes($appointment->timezoneOffset)
              ->format('g:i a');

        $message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";
        $this->_sendMessage($appointment->phoneNumber, $message);
    }

    /**
     * Sends a single message using the app's global configuration
     *
     * @param string $number  The number to message
     * @param string $content The content of the message
     *
     * @return void
     */
    private function _sendMessage($number, $content)
    {
        $this->twilioClient->messages->create(
            $number,
            array(
                "from" => $this->sendingNumber,
                "body" => $content
            )
        );
    }
}
app/AppointmentReminders/AppointmentReminder.php
Send reminder messages with the Twilio API

app/AppointmentReminders/AppointmentReminder.php

That's it! Our Laravel application is all set to send out reminders for upcoming appointments.

Where to next?

We hope you found this sample application useful. If you're a PHP developer working with Twilio, you might enjoy these other tutorials:

Click to Call

Put a button on your web page that connects visitors to live support or sales people via telephone.

Two-Factor Authentication

Improve the security of Laravel's built-in login functionality by adding two-factor authentication via text message.

Did this help?

Thanks for checking out this tutorial! If you have any feedback to share with us, please reach out on Twitter... we'd love to hear your thoughts, and know what you're building!

Mario Celi
David Prothero
Andrew Baker
Agustin Camino
Kevin Whinnery

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd browsing the Twilio tag on Stack Overflow.

1 / 1
Loading Code Samples...
SDK Version:
  • env
APP_ENV=local
APP_DEBUG=true
APP_KEY=ufxhZiQcKxi1eHVmGq8MwfAcRgZHJ1Qq

DB_HOST=localhost
DB_DATABASE=appointments
DB_USERNAME=appointments
DB_PASSWORD=appointments

# Twilio API credentials
# Found at https://www.twilio.com/user/account/settings
TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXX
TWILIO_AUTH_TOKEN=your_token

# Twilio phone number
# Purchase one at https://www.twilio.com/user/account/phone-numbers/incoming
TWILIO_NUMBER=your_twilio_number
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Carbon\Carbon;

use App\Http\Requests;
use App\Http\Controllers\Controller;

class AppointmentController extends Controller
{
    private $appointment;
    private $validInputConditions = array(
        'name' => 'required',
        'phoneNumber' => 'required|min:5',
        'when' => 'required',
        'timezoneOffset' => 'required',
        'delta' => 'required|numeric'
    );

    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index()
    {
        $allAppointments = \App\Appointment::orderBy('id', 'ASC')->get();
        return response()->view('appointment.index', array('apts' => $allAppointments));
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return Response
     */
    public function create()
    {
        $appointment = new \App\Appointment;
        return \View::make('appointment.create', array('appointment' => $appointment));
    }

    /**
     * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store(Request $request)
    {
        $newAppointment = $this->appointmentFromRequest($request);
        $newAppointment->save();
        return redirect()->route('appointment.index');
    }

    /**
     * Delete a resource in storage.
     *
     * @return Response
     */
    public function destroy($id) {
        \App\Appointment::find($id)->delete();
        return redirect()->route('appointment.index');
    }

    public function edit($id) {
        $appointmentToEdit = \App\Appointment::find($id);
        return \View::make('appointment.edit', array('appointment' => $appointmentToEdit));
    }

    public function update(Request $request, $id) {
        $updatedAppointment = $this->appointmentFromRequest($request);
        $existingAppointment = \App\Appointment::find($id);

        $existingAppointment->name = $updatedAppointment->name;
        $existingAppointment->phoneNumber = $updatedAppointment->phoneNumber;
        $existingAppointment->timezoneOffset = $updatedAppointment->timezoneOffset;
        $existingAppointment->when = $updatedAppointment->when;
        $existingAppointment->notificationTime = $updatedAppointment->notificationTime;

        $existingAppointment->save();
        return redirect()->route('appointment.index');
    }

    private function appointmentFromRequest(Request $request) {
        $this->validate($request, $this->validInputConditions);
        $newAppointment = new \App\Appointment;

        $newAppointment->name = $request->input('name');
        $newAppointment->phoneNumber = $request->input('phoneNumber');
        $newAppointment->timezoneOffset = $request->input('timezoneOffset');
        $newAppointment->when = $request->input('when');

        $notificationTime = Carbon::parse($request->input('when'))->subMinutes($request->delta);
        $newAppointment->notificationTime = $notificationTime;

        return $newAppointment;
    }
}
<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        '\App\Console\Commands\SendReminders'
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('reminders:send')->everyMinute();
    }
}
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class SendReminders extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'reminders:send';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Send reminders using Twilio';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        $appointmentReminder = new \App\AppointmentReminders\AppointmentReminder();
        $appointmentReminder->sendReminders();
    }
}
<?php

namespace App\AppointmentReminders;

use Illuminate\Log;
use Carbon\Carbon;
use Twilio\Rest\Client;

class AppointmentReminder
{
    /**
     * Construct a new AppointmentReminder
     *
     * @param Illuminate\Support\Collection $twilioClient The client to use to query the API
     */
    function __construct()
    {
        $this->appointments = \App\Appointment::appointmentsDue()->get();

        $twilioConfig = config('services.twilio');
        $accountSid = $twilioConfig['twilio_account_sid'];
        $authToken = $twilioConfig['twilio_auth_token'];
        $this->sendingNumber = $twilioConfig['twilio_number'];

        $this->twilioClient = new Client($accountSid, $authToken);
    }

    /**
     * Send reminders for each appointment
     *
     * @return void
     */
    public function sendReminders()
    {
        $this->appointments->each(
            function ($appointment) {
                $this->_remindAbout($appointment);
            }
        );
    }

    /**
     * Sends a message for an appointment
     *
     * @param Appointment $appointment The appointment to remind
     *
     * @return void
     */
    private function _remindAbout($appointment)
    {
        $recipientName = $appointment->name;
        $time = Carbon::parse($appointment->when, 'UTC')
              ->subMinutes($appointment->timezoneOffset)
              ->format('g:i a');

        $message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";
        $this->_sendMessage($appointment->phoneNumber, $message);
    }

    /**
     * Sends a single message using the app's global configuration
     *
     * @param string $number  The number to message
     * @param string $content The content of the message
     *
     * @return void
     */
    private function _sendMessage($number, $content)
    {
        $this->twilioClient->messages->create(
            $number,
            array(
                "from" => $this->sendingNumber,
                "body" => $content
            )
        );
    }
}
<?php

namespace App\AppointmentReminders;

use Illuminate\Log;
use Carbon\Carbon;
use Twilio\Rest\Client;

class AppointmentReminder
{
    /**
     * Construct a new AppointmentReminder
     *
     * @param Illuminate\Support\Collection $twilioClient The client to use to query the API
     */
    function __construct()
    {
        $this->appointments = \App\Appointment::appointmentsDue()->get();

        $twilioConfig = config('services.twilio');
        $accountSid = $twilioConfig['twilio_account_sid'];
        $authToken = $twilioConfig['twilio_auth_token'];
        $this->sendingNumber = $twilioConfig['twilio_number'];

        $this->twilioClient = new Client($accountSid, $authToken);
    }

    /**
     * Send reminders for each appointment
     *
     * @return void
     */
    public function sendReminders()
    {
        $this->appointments->each(
            function ($appointment) {
                $this->_remindAbout($appointment);
            }
        );
    }

    /**
     * Sends a message for an appointment
     *
     * @param Appointment $appointment The appointment to remind
     *
     * @return void
     */
    private function _remindAbout($appointment)
    {
        $recipientName = $appointment->name;
        $time = Carbon::parse($appointment->when, 'UTC')
              ->subMinutes($appointment->timezoneOffset)
              ->format('g:i a');

        $message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";
        $this->_sendMessage($appointment->phoneNumber, $message);
    }

    /**
     * Sends a single message using the app's global configuration
     *
     * @param string $number  The number to message
     * @param string $content The content of the message
     *
     * @return void
     */
    private function _sendMessage($number, $content)
    {
        $this->twilioClient->messages->create(
            $number,
            array(
                "from" => $this->sendingNumber,
                "body" => $content
            )
        );
    }
}
<?php

namespace App\AppointmentReminders;

use Illuminate\Log;
use Carbon\Carbon;
use Twilio\Rest\Client;

class AppointmentReminder
{
    /**
     * Construct a new AppointmentReminder
     *
     * @param Illuminate\Support\Collection $twilioClient The client to use to query the API
     */
    function __construct()
    {
        $this->appointments = \App\Appointment::appointmentsDue()->get();

        $twilioConfig = config('services.twilio');
        $accountSid = $twilioConfig['twilio_account_sid'];
        $authToken = $twilioConfig['twilio_auth_token'];
        $this->sendingNumber = $twilioConfig['twilio_number'];

        $this->twilioClient = new Client($accountSid, $authToken);
    }

    /**
     * Send reminders for each appointment
     *
     * @return void
     */
    public function sendReminders()
    {
        $this->appointments->each(
            function ($appointment) {
                $this->_remindAbout($appointment);
            }
        );
    }

    /**
     * Sends a message for an appointment
     *
     * @param Appointment $appointment The appointment to remind
     *
     * @return void
     */
    private function _remindAbout($appointment)
    {
        $recipientName = $appointment->name;
        $time = Carbon::parse($appointment->when, 'UTC')
              ->subMinutes($appointment->timezoneOffset)
              ->format('g:i a');

        $message = "Hello $recipientName, this is a reminder that you have an appointment at $time!";
        $this->_sendMessage($appointment->phoneNumber, $message);
    }

    /**
     * Sends a single message using the app's global configuration
     *
     * @param string $number  The number to message
     * @param string $content The content of the message
     *
     * @return void
     */
    private function _sendMessage($number, $content)
    {
        $this->twilioClient->messages->create(
            $number,
            array(
                "from" => $this->sendingNumber,
                "body" => $content
            )
        );
    }
}