Build an SMS Appointment Reminder App Using CakePHP and Twilio

February 18, 2025
Written by
Temitope Taiwo Oyedele
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

Build an SMS appointment reminder app using Cakephp and Twilio

Reminders are crucial in sectors like healthcare, education, professional services, and many more. They come in handy — especially when scheduling appointments, and timely follow-ups are critical. This is why a robust and functional reminder management app is needed. What if I told you that you can achieve this using just CakePHP and Twilio?

In this tutorial, you'll learn how by building an SMS reminder app using CakePHP and Twilio. By the end of this tutorial, you will have created an app that allows you to schedule and send out appointment reminders via SMS.

Prerequisites

To follow this tutorial, the following prerequisites should be met:

Buy a phone number

The first step is to buy a phone for sending SMS messages. Log in to the Twilio Console, and on the left side, click on Phone Numbers > Manage > Buy a number. There, filter the phone number list by country and ensure that at least SMS is checked under Capabilities.Then, choose the number you prefer.

The project scope

Before proceeding to building, it is crucial that you understand what the project will look like. The project will include a form where appointment details can be entered and stored in a database.

These details can then be viewed on a separate page in a table showing the appointment dates. The table will include a button for each appointment, which will be green if it is more than a day away, yellow if it is within a day, and red if it is within 24 hours. When you click the button, a reminder SMS will be sent to the customer using Twilio.

Set up the Database

Navigate to your MySQL database provider. In my case, I'm using XAMPP. There, create a database named "Appointments".

Set up a new CakePHP application

To create a new CakePHP application, navigate to the folder where you want to scaffold the project and run these commands:

​​composer create-project --prefer-dist cakephp/app:~5.0 sms-appointment-app
cd sms-appointment-app

When asked "Set Folder Permissions ? (Default to Y) [Y,n]?", answer with Y.

The new CakePHP project will be available in a directory named sms-appointment-app, and you'll have changed into that directory.

Once you've done that, go back to your project folder and locate config/app_local.php. Inside it, in the Datasources section, update the database configuration with your database's credentials. This includes your database username, password (if you have any), and database name, as exemplified in the code below.

'Datasources' => [
    'default' => [
        'host' => '127.0.0.1',
        /*
        * CakePHP will use the default DB port based on the driver selected
        * MySQL on MAMP uses port 8889, MAMP users will want to uncomment
        * the following line and set the port accordingly
        */
        //'port' => 'non_standard_port_number',
        'username' => 'root',
        'password' => '',
        'database' => 'Appointments',
        /*
        * If not using the default 'public' schema with the PostgreSQL driver
        * set it here.
        */
        //'schema' => 'myapp',
        /*
        * You can use a DSN string to set the entire configuration
        */
        'url' => env('DATABASE_URL', null),
    ],

Now, let's create a new migration file to create the "appointments" table by running the following command.

bin/cake bake migration CreateAppointments

Now, navigate to config/Migrations folder and edit the migration file (ending with _CreateMigrations.php) updating it to match the code below, in order to define the table structure:

<?php

use Migrations\AbstractMigration;

class CreateAppointments extends AbstractMigration
{
    public function change()
    {
        $table = $this->table('appointments');
        $table->addColumn('customer_name', 'string', ['limit' => 255])
              ->addColumn('customer_phone', 'string', ['limit' => 20])
              ->addColumn('appointment_date', 'datetime')
              ->addColumn('details', 'text', ['null' => true])
              ->addColumn('created', 'datetime')
              ->addColumn('modified', 'datetime')
              ->create();
    }
}

After which, you run the migrate command to create the table:

bin/cake migrations migrate

Create the model, controller, and view

To create a model for the project, run this command:

bin/cake bake model appointments

This command will generate a model file in src/Model/Table/AppointmentsTable.php, and an entity file inside the src/Model/Entity/Appointment.php.

To create the Appointment controller, run the following code:

bin/cake bake controller appointments

This command generates a new controller file named AppointmentsController.php in the src/Controller/ directory.

Open src/Controller/AppointmentController.php. First, add a use statement at the top of your controller file for the TwilioService class.

use App\Service\TwilioService;

Next, include a new method called sendReminder, inside it:

public function sendReminder($id = null)
{
    $this->request->allowMethod(['post']);
    $appointment = $this->Appointments->get($id);
    $twilioService = new TwilioService();
    $message = "Reminder: You have an appointment on " . $appointment->appointment_date->format('Y-m-d H:i');
    try {
        $twilioService->sendSMS($appointment->customer_phone, $message);
        $this->Flash->success(__('Reminder sent successfully.'));
    } catch (\Exception $e) {
        $this->Flash->error(__('Failed to send reminder: ') . $e->getMessage());
    }

    return $this->redirect(['action' => 'index']);
}

The sendReminder() function is used to send an SMS reminder about an upcoming appointment. It restricts the method to only handle POST requests, retrieves the appointment details from the database using the appointment ID, and then constructs a reminder message.

The TwilioService (which we’ll create shortly) is instantiated inside it to send the SMS to the customer's phone number. The function uses a try-catch block to handle success or failure in sending the message, providing feedback to the user via flash messages.

For the view, you’ll be creating two files, the first for displaying the appointments in a table, and the second for adding a new appointment. Inside the templatesfolder, create a folder called Appointments. Then, inside this folder, first, create a file called index.php and add the code below:

<h1>Appointments</h1>
<table>
    <thead>
    <tr>
        <th>Customer Name</th>
        <th>Phone</th>
        <th>Appointment Date</th>
        <th>Details</th>
        <th>Actions</th>
    </tr>
    </thead>
    <tbody>
        <?php foreach ($appointments as $appointment): ?>
        <tr>
            <td><?= h($appointment->customer_name) ?></td>
            <td><?= h($appointment->customer_phone) ?></td>
            <td><?= h($appointment->appointment_date) ?></td>
            <td><?= h($appointment->details) ?></td>
            <td>
            <?php
                $now = new \DateTime();
                $appointmentDate = new \DateTime($appointment->appointment_date);
                $diff = $appointmentDate->diff($now);
                if ($diff->invert == 1) {
                    if ($diff->days <= 1) {
                        $buttonClass = 'btn-warning';
                    } else {
                        $buttonClass = 'btn-success';
                    }
                } else {
                    $buttonClass = 'btn-danger';
                }
                echo $this->Form->postLink(
                    'Send Reminder',
                    ['action' => 'sendReminder', $appointment->id],
                    [
                        'confirm' => 'Are you sure you want to send a reminder?',
                        'class' => "btn reminder-button $buttonClass",
                        'data-appointment-date' => $appointment->appointment_date
                    ]
                );
            ?>
            </td>
        </tr>
        <?php endforeach; ?>
    </tbody>
</table>

The code above creates a table to display the appointments. It includes key details such as customer name, phone number, appointment date, and additional information. The code first loops through an array of appointment data, outputting the relevant fields for each appointment into table rows.

Also, the appointment date is compared with the current date, and based on the difference, the button shown is based on the appointment time. If the appointment is more than a day away, a green button is displayed. If it's happening in a day, a yellow button appears. Lastly, if it's due or supposed to happen that same day, a red button appears.

The second file that you need to create inside the templates/Appointments folder is the add.php. Create it and then add the code below:

<h1>Add Appointment</h1>
<?= $this->Html->link('Add New Appointment', ['action' => 'add'], ['class' => 'btn btn-primary']) ?>
<?= $this->Form->create($appointment) ?>
<fieldset>
    <?= $this->Form->control('customer_name') ?>
    <?= $this->Form->control('customer_phone') ?>
    <?= $this->Form->control('appointment_date', ['type' => 'datetime-local']) ?>
    <?= $this->Form->control('details', ['type' => 'textarea']) ?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>

Here, you are creating a form to take in the user’s input for appointments. It collects essential information like the customer's name, phone number, appointment date, and any additional details about the appointment.

Next, head to the webroot/jsfolder directory. Create a file called appointments.js and paste in this code:

document.addEventListener("DOMContentLoaded", function () {
  function updateButtonClasses() {
    const buttons = document.querySelectorAll(".reminder-button");
    const now = new Date();
    buttons.forEach((button) => {
      const appointmentDate = new Date(button.dataset.appointmentDate);
      const diffDays = Math.ceil(
        (appointmentDate - now) / (1000 * 60 * 60 * 24),
      );
      button.classList.remove("btn-success", "btn-warning", "btn-danger");
      if (diffDays > 1) {
        button.classList.add("btn-success");
      } else if (diffDays <= 1 && diffDays > 0) {
        button.classList.add("btn-warning");
      } else {
        button.classList.add("btn-danger");
      }
    });
  }
  setInterval(updateButtonClasses, 60000);
  updateButtonClasses();
});

The code above is designed to dynamically update the classes of buttons based on how close an appointment date is.

Inside the updateButtonClasses() function, each button is looped through using forEach. For each button, the data-appointment-date attribute is retrieved and converted into a JavaScript Date object.

The difference in days between the current date and the appointment date is then calculated. Just like what was done earlier to the buttons in index.php, a similar thing is done, but this code is designed to complement what was done in index.php by providing real-time, client-side updates to the button's appearance.

So, while the server-side PHP sets the initial class based on when the page is generated, the JavaScript dynamically updates the button classes on the client-side every minute. This ensures that if an appointment moves closer to the current time while the user is viewing the page, the button will change accordingly without needing to refresh the page.

After this, locate the layout/default.php file in your folder and add this code right after the closing footer tag:

<?= $this->Html->script('appointments') ?>

This will load the JavaScript file into the page. You also need to load Bootstrap inside our page. Within the head tag, add this:

<?= $this->Html->css('https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css') ?>

Install the Twilio PHP Helper Library

You need to install the Twilio PHP Helper Library. This package provides convenient methods for interacting with Twilio's APIs. To install it, run the following command in your terminal:

composer require twilio/sdk

Register the required environment variables

Copy .env.example, as .env inside the config folder by running the following command:

cp -v config/.env.example config/.env

You then need to add your Twilio credentials by adding the following to the end of the file.

export TWILIO_ACCOUNT_SID=<<your_account_sid>>
export TWILIO_AUTH_TOKEN=<<your_auth_token>>
export TWILIO_PHONE_NUMBER=<<your_twilio_phone_number>>
Image displaying available phone numbers for purchase on the

Then, go back to the Twilio Console that you opened in your browser, earlier. There, from the main dashboard, copy your Account SID, Auth Token, and phone number. Then, replace the <<your_account_sid>>, <<your_auth_token>>, and <<your_twilio_phone_number>> placeholders, respectively, with the respective details which you just copied.

Then, navigate to config/bootstrap.php and uncomment the following:

if (!env('APP_NAME') && file_exists(CONFIG . '.env')) {
    $dotenv = new \josegonzalez\Dotenv\Loader([CONFIG . '.env']);
    $dotenv->parse()
        ->putenv()
        ->toEnv()
        ->toServer();
}

You need to create a Twilio service to handle SMS sending. Inside the src folder, create a new folder called Service, and inside it, create a file called TwilioService.php. Inside it, paste in the following code:

<?php

namespace App\Service;

use Twilio\Rest\Client;

class TwilioService
{
   private $client;
   private $fromNumber;

   public function __construct()
   {
       $sid = env('TWILIO_ACCOUNT_SID');
       $token = env('TWILIO_AUTH_TOKEN');
       $this->fromNumber = env('TWILIO_PHONE_NUMBER');
       $this->client = new Client($sid, $token);
   }

   public function sendSMS($to, $message)
   {
       return $this->client->messages->create(
           $to,
           [
               'from' => $this->fromNumber,
               'body' => $message
           ]
       );
   }
}

In the code above, a TwilioService class was created to allow a simplified process of sending SMS messages using Twilio’s API by calling a single function.

When the TwilioService class is instantiated, the constructor retrieves essential credentials from environment variables and then initializes the Twilio client, which will be used to send the messages.

Configure the routes

This will be the final step. To configure the routes, navigate to config/routes.php. Within the $routes->scope('/', function (RouteBuilder $builder): void method, add this:

$builder->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);
$builder->connect('/appointments', ['controller' => 'Appointments', 'action' => 'index']);
$builder->connect('/appointments/add', ['controller' => 'Appointments', 'action' => 'add']);
$builder->connect('/appointments/sendReminder/:id', ['controller' => 'Appointments', 'action' => 'sendReminder']);

This would cover all the routes needed for the application.

Test the application

To test out the application, open the terminal and navigate to your project. Run this code:

bin/cake server

This will start up the development server. Open http://localhost:8765/appointments/add to add a new appointment:

Image displaying the from in which the appointment details for a client is entered

Once you fill in the appointment date alongside every other detail and click Submit, you will be redirected to a table containing the list of appointments, with each having its own button color based on how close the appointment is.

A web dashboard showing a list of appointments with the option to send reminders.

When you click on Send Reminder, the client or customer will get an SMS reminder text on the registered phone.

Image displaying message reminder regarding an appointment

It works! That's really cool, isn’t it? That’s why Twilio is designed to meet your communication needs.

That’s how to build an SMS appointment reminder application

In this tutorial, we took a look at how to build an SMS reminder app using CakePHP for building and Twilio for sending SMS reminders out to the client. Communication is key to every business, and utilizing an effective tool such as Twilio for your communications would be a huge step to giving your customers a top-notch service. You can choose to enhance yours by adding a feature to delete and update appointments as the methods are included already to in the AppointmentsController.

What would you like to build with Twilio today?

Temitope Taiwo Oyedele is a software engineer and technical writer. He likes to write about things he’s learned and experienced.

The appointment icon in the post's main image was created by Three musketeers on Flaticon, and the SMS icon in the post's main image was created by Eucalyp on Flaticon.