How to Handle Incoming Calls in CakePHP With Twilio

May 17, 2024
Written by
Popoola Temitope
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

How to Handle Incoming Calls in CakePHP With Twilio

Integrating voice communication into business applications can help provide a good customer experience. Voice communication can be used to integrate customer auto-response call centers, collect feedback, or add an additional security layer to user security.

In this tutorial, you'll learn how to set up a CakePHP application that receives and responds to Twilio incoming calls.

Requirements

To follow along with this tutorial, ensure that you have the following:

Set up a new CakePHP project

To get started, let's create a new CakePHP project using Composer. To do that, open your terminal and navigate to the directory where you want to create the project. Then run the command below.

composer create-project --prefer-dist cakephp/app:~5.0 Twilio_call

The command above will prompt you with ' Set Folder Permissions? (Default to Y) [Y, n]?' Answer 'Y' to complete the project installation.

Next, you need to navigate the terminal to the working directory by running the command below.

cd Twilio_call

Now, open the project folder in your preferred code editor or IDE.

Configure the database

Let’s connect the application to the MySQL database. To do that, navigate to the config folder and open the app_local.php file. In the Datasource section under the default subsection, change the database settings, as shown in the screenshot below:

Next, log in to your MySQL database server and create a new database named incoming_call_feedback.

Create the database schema 

Next, you need to create the database table for the application using the CakePHP migration command. To do this, first, you need to generate a migration file using the command below.

bin/cake bake migration feedback

After running the above command, navigate to config/Migrations/, open the generated migration file that ends with _Feedback.php, and update the change() function with the following code.

$table = $this->table('feedback');
$table->addColumn('caller_no', 'string', [
    'limit' => 20,
    'null' => true,
]);
$table->addColumn('caller_name', 'string', [
    'limit' => 50,
    'null' => true,
]);
$table->addColumn('feedback', 'string', [
    'limit' => 255,
    'null' => true,
]);
$table->create();

To complete the database migration and start the application development server, run the commands below:

bin/cake migrations migrate
bin/cake server

Now, open http://localhost:8765 in your browser. You should see the application's default page, similar to the screenshot below.

Install the Twilio PHP Helper Library

To interact with Twilio Programmable Voice, you need to install the Twilio PHP Helper Library. To do this, open a new terminal tab and run the following command:

composer require twilio/sdk

Create a model and an entity

To enable the application to interact with the database table, you'll need to create a model and an entity by running the command below.

bin/cake bake model feedback

Create the application controller

To handle the application logic, let's create a controller named IncomingCall by running the command below.

bin/cake bake controller IncomingCall --no-actions

Navigate to the src/Controller directory. You will see the generated controller file named IncomingCallController.php. Open the file and replace its code with the following logic.

<?php

declare(strict_types=1);

namespace App\Controller;

use Cake\Http\Response;
use Cake\ORM\Table;
use Twilio\TwiML\VoiceResponse;

class IncomingCallController extends AppController
{
    private Table $feedbackTable;

    public function initialize(): void
    {
        parent::initialize();
        $this->feedbackTable = $this->fetchTable('Feedbacks');
    }

    public function incomingCall()
    {
        $this->request->allowMethod('get');
        $this->autoRender = false;
        $response = new VoiceResponse();
        $gatherName = $response->gather([
            'input' => 'speech',
            'method' => 'GET',
            'action' => '/process-name',
            'timeout' => 3,
            'speechTimeout' => 'auto',
            'language' => 'en-US'
        ]);
        $gatherName->say('Please say your name.');
        $response->say("Sorry, we didn't receive your name. Goodbye.");
        $httpResponse = new Response();
        $httpResponse = $httpResponse->withType('text/xml')->withStringBody($response->asXml());
        return $httpResponse;
    }

    public function processName()
    {
        $this->request->allowMethod('get');
        $this->autoRender = false;

        $name = $this->request->getQuery('SpeechResult');
        $callerPhoneNumber = $this->request->getQuery('From');

        $existingFeedback = $this->feedbackTable->find()->where(['caller_no' => $callerPhoneNumber])->first();
        if (!$existingFeedback) {
            $feedback = $this->feedbackTable->newEmptyEntity();
            $feedback->caller_no = $callerPhoneNumber;
            $feedback->caller_name = $name;
            $this->feedbackTable->save($feedback);
        } else {
            $existingFeedback->feedback = $name;
            $this->feedbackTable->save($existingFeedback);
        }
        $response = new VoiceResponse();
        $gatherMessage = $response->gather([
            'input' => 'speech',
            'method' => 'GET',
            'action' => "/process-message",
            'timeout' => 5,
            'speechTimeout' => 'auto',
            'language' => 'en-US'
        ]);
        $gatherMessage->say("Thank you, $name. Please say your feedback message.");
        $response->say("Sorry, we didn't receive your message. Goodbye.");
        $httpResponse = new Response();
        $httpResponse = $httpResponse->withType('text/xml')->withStringBody($response->asXml());

        return $httpResponse;
    }

    public function processMessage()
    {
        $this->request->allowMethod('get');
        $this->autoRender = false;
        $response = new VoiceResponse();
        $message = $this->request->getQuery('SpeechResult');
        $callerPhoneNumber = $this->request->getQuery('From');
        $existingFeedback = $this->feedbackTable->find()->where(['caller_no' => $callerPhoneNumber])->first();
        if ($existingFeedback) {
            $existingFeedback->feedback = $message;
            if ($this->feedbackTable->save($existingFeedback)) {
                $response->say("Thank you for your feedback. Goodbye.");
            } else {
                $response->say("Sorry, we encountered an error while saving your feedback. Please call again.");
            }
        } else {
            $response->say("Sorry, we couldn't find your previous feedback. Please provide feedback again.");
        }
        $httpResponse = new Response();
        $httpResponse = $httpResponse->withType('text/xml')->withStringBody($response->asXml());
        return $httpResponse;
    }

    public function dashboard()
    {
        $feedback = $this->feedbackTable->find()->all();
        $this->set(compact('feedback'));
    }
}

Here is the break down of the above code:

  • The incomingCall() method initiates the call handling process by prompting the caller to speak their name using the gather() method, which in turn triggers the processName() method.

  •  Upon receiving the name, the processName() method prompts the caller to provide their feedback message.

  • This message is then processed and stored in the database using the processMessage() method.

  •  The dashboard() method retrieves all collected feedback from the database and prepares it for display on the dashboard page.

Create the dashboard template

Now, let’s create a dashboard template that displays all the collected feedback. To do this, navigate to the templates folder and create a new folder named IncomingCall. Inside the folder, create a file named dashboard.php and add the following code.

<h1>Feedback Dashboard</h1>
<table>
    <thead>
        <tr>
            <th>Caller Number</th>
            <th>Caller Name</th>
            <th>Feedback</th>
        </tr>
    </thead>
    <tbody>
        <?php foreach ($feedback as $feedback_record): ?>
        <tr>
            <td><?= h($feedback_record->caller_no) ?></td>
            <td><?= h($feedback_record->caller_name) ?></td>
            <td><?= h($feedback_record->feedback) ?></td>
        </tr>
        <?php endforeach; ?>
    </tbody>
</table>

Configure the routes

Let's configure the route for the IncomingCallController's methods to make the application accessible on a web browser. To do that, navigate to the config folder, open the routes.php file and add the following routes inside the $routes->scope() method.

$builder->connect('/incoming-call',['controller' => 'IncomingCall', 'action' => 'incomingCall']);
$builder->connect( '/process-name', ['controller' => 'IncomingCall', 'action' => 'processName']);
$builder->connect( '/process-message',['controller' => 'IncomingCall', 'action' => 'processMessage']);
$builder->connect( '/dashboard',['controller' => 'IncomingCall', 'action' => 'dashboard']);

Configure the Twilio webhook

To configure the Twilio webhook, you need to make the application accessible over the internet using ngrok. To do so, open another terminal and run the command below.

ngrok http http://localhost:8765

The above command will generate a Forwarding URL. Copy the URL as shown in the screenshot below.

Next, on your Twilio Console dashboard, navigate to Phone Numbers > Manage > Active numbers. Then, select your Twilio number, and click Configure as shown in the screenshot below.

Under the Voice Configuration section, update the configuration with the following:

  • A call comes in: Webhook

  • URL: Paste the generated Ngrok forwarding URL and append /incoming-call at the end

  • HTTP (Method): HTTP GET

Then click on the Save configuration button to save the configuration.

Test the application

To test the application, dial your Twilio number from your mobile phone. The system will ask you to say your name. After saying your name, the system will ask you to provide feedback before the call ends.

After completing the call, to view all the received feedback messages, open http://localhost:8765/dashboard in your browser. You should see all the collected feedback displayed as shown in the screenshot below.

That is how to handle Twilio incoming calls in CakePHP apps

In this tutorial, you learned how to receive and respond to a Twilio incoming call in a CakePHP application, aiming to enhance customer engagement and streamline feedback collection processes. 

Popoola Temitope is a mobile developer and a technical writer who loves writing about frontend technologies. He can be reached on LinkedIn.