How to send a follow-up call to users in a Symfony app using Twilio

How to send a follow-up call to users in a Symfony app using Twilio

When you observe that a user visits your website but does not return after registration, you can reach out to them through various channels, including sending follow-up messages via SMS, email, or phone calls. Informing them about what is new on your website may help bring them back and increase your website traffic.

In this article, you will learn how to send periodic messages to users who are inactive in a Symfony application using Twilio Programmable Voice.

Prerequisites

To complete this tutorial, you will need the following:

Create a new Symfony project

To get started with the tutorial, you need to create a new Symfony project by running the command below in your terminal.

```bash

Symfony new User_follow_up_app --version="7.0.*" --webapp

```

After running the command above, you need to navigate to the application folder and start the application development server using the commands below:

```bash

cd User_follow_up_app

symfony server:start

```

Next, open localhost:8000 in your browser. You should see the application's default welcome page as shown in the image below.

Database configuration

To connect the application to the MySQL database, open the project in your code editor and then open the .env file from the project's root directory.

Inside the .env file, comment out DATABASE_URL for Postgresql. Then uncomment the DATABASE_URL for MySQL and update the database username and password with your actual MySQL database values.

```bash

DATABASE_URL="mysql://<database_user>:<database_password>@127.0.0.1:3306/users"

```

Next, open another terminal tab and run the command below to complete the database configuration.

```bash

php bin/console doctrine:database:create

```

Create a member entity

Let’s create the database table schema and a property called entity that will hold information about the registered users. You can create the userInfo entity by running the command below:

```bash

php bin/console make:entity

```

This command will prompt you to enter the entity name and the database table properties. Set them as shown in the screenshot below.

Next, run the migration commands below to connect the application entity to the MySQL database server.

```bash

php bin/console make:migration

php bin/console doctrine:migrations:migrate

```

The migration command above will prompt you to confirm whether you want to continue with the migration or not. Answer 'yes' to complete the migration.

Create the application form type

You need to create a form type to handle form input in a Symfony application. This form type is used to define the input fields, their types, validation rules, and other form attributes.

Let’s create register and login form types. To create the register form type, open the project in a code editor, navigate to the src folder, and create a new folder named Form. Then, inside the folder, create a RegisterType.php file and add the code below.

```php

<?php

namespace App\Form;

use App\Entity\UserInfo;

use Symfony\Component\Form\AbstractType;

use Symfony\Component\Form\FormBuilderInterface;

use Symfony\Component\OptionsResolver\OptionsResolver;

use Symfony\Component\Form\Extension\Core\Type\PasswordType;

class RegisterType extends AbstractType

{

public function buildForm(FormBuilderInterface $builder, array $options): void

{

$builder

->add('fullname')

->add('email')

->add('phone')

->add('password', PasswordType::class)

;

}

public function configureOptions(OptionsResolver $resolver): void

{

$resolver->setDefaults([

'data_class' => Userinfo::class,

]);

}

}

```

Next, to create the login form type, inside the src/Form folder, create a LoginType.php file and add the code below.

```php

<?php

namespace App\Form;

use App\Entity\UserInfo;

use Symfony\Component\Form\AbstractType;

use Symfony\Component\Form\FormBuilderInterface;

use Symfony\Component\OptionsResolver\OptionsResolver;

use Symfony\Component\Form\Extension\Core\Type\PasswordType;

class LoginType extends AbstractType

{

public function buildForm(FormBuilderInterface $builder, array $options): void

{

$builder

->add('email')

->add('password', PasswordType::class)

;

}

public function configureOptions(OptionsResolver $resolver): void

{

$resolver->setDefaults([

'data_class' => Userinfo::class,

]);

}

}

```

Retrieve Twilio API token

To retrieve your Twilio API access token, log in to your Twilio Console dashboard. You will find the API token under the account info section, as shown in the screenshot below.

Twilio Account SID, Auth Token, and twilio phone number

Copy the access token somewhere safe; you will use it in the next section.

Securing the Twilio access token in the .env file

To ensure that the access token is secure, let’s store it in the .env file. To do that, from the application's root directory, open the .env file and add the following environment variable.

```bash

TWILIO_ACCOUNT_SID=<twilio_account_sid>

TWILIO_AUTH_TOKEN=<twilio_auth_token>

TWILIO_PHONE_NUMBER=<twilio_phone_number>

```

In the code above, replace the placeholders <twilio_account_sid>, <twilio_auth_token>, and <twilio_phone_number> with your actual Twilio access token.

Installing Twilio SDK

Next, let’s install the Twilio PHP helper library, which will be used to interact with Twilio API endpoints, including programmable voice. To install the SDK, run the command below:

```bash

composer require twilio/sdk

```

Creating the application controller

Now, let’s create a controller that will handle the application login. You can create the controller file by running the command below.

```bash

php bin/console make:controller UserFollowUP

```

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

```php

<?php

namespace App\Controller;

use App\Entity\UserInfo;

use App\Form\LoginType;

use App\Form\RegisterType;

use Doctrine\ORM\EntityManagerInterface;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

use Symfony\Component\HttpFoundation\Request;

use Symfony\Component\HttpFoundation\Response;

use Symfony\Component\Routing\Annotation\Route;

use Symfony\Component\HttpFoundation\Session\SessionInterface;

use Twilio\Rest\Client;

class UserFollowUPController extends AbstractController

{

private $entityManager;

private $passwordEncoder;

public function __construct(EntityManagerInterface $entityManager)

{

$this->entityManager = $entityManager;

}

#[Route('/register', name: 'app_user_follow_up')]

public function register(Request $request,SessionInterface $session): Response

{

$user = new UserInfo();

$form = $this->createForm(RegisterType::class, $user);

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {

$currentDate = (new \DateTime())->format('Y-m-d H:i:s');

$user->setLastSeen($currentDate);

$this->entityManager->persist($user);

$this->entityManager->flush();

$session->set('user', $user);

return $this->redirectToRoute('dashboard');

}

return $this->render('user_follow_up/register.html.twig', [

'form' => $form->createView(),

]);

}

#[Route('/login', name: 'app_login')]

public function login(Request $request, SessionInterface $session): Response

{

$form = $this->createForm(LoginType::class);

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {

$email = $password = $form->get('email')->getData();

$password = $password = $form->get('password')->getData();

$UserInfo = new UserInfo();

$repository = $this->entityManager->getRepository(UserInfo::class);

$user = $repository->findOneBy([

'email' => $email,

'password' => $password,

]);

if ($user) {

$currentDate = (new \DateTime())->format('Y-m-d H:i:s');

$user->setLastSeen($currentDate);

$this->entityManager->persist($user);

$this->entityManager->flush();

return $this->redirectToRoute('dashboard');

} else {

return $this->redirectToRoute('app_login');

}

}

return $this->render('user_follow_up/login.html.twig', [

'form' => $form->createView(),

]);

}

#[Route('/dashboard', name: 'dashboard')]

public function dashboard(SessionInterface $session): Response

{

if (null !== $session->get('user')) {

$user = $session->get('user');

return $this->render('user_follow_up/dashboard.html.twig', [

'user' => $user,

]);

}else{

return $this->redirectToRoute('app_login');

}

}

#[Route('/follow-up', name: 'app_follow_up')]

public function followUp(): Response

{

$twilioSid = $_ENV['TWILIO_ACCOUNT_SID'];

$twilioToken = $_ENV['TWILIO_AUTH_TOKEN'];

$twilioNumber = $_ENV['TWILIO_PHONE_NUMBER'];

$twilioClient = new Client($twilioSid, $twilioToken);

$userRepository = $this->entityManager->getRepository(UserInfo::class);

$inactiveUsers = $userRepository->createQueryBuilder('u')

->where('u.lastSeen <= :oneMonthAgo')

->setParameter('oneMonthAgo', new \DateTime('-1 month'))

->getQuery()

->getResult();

foreach ($inactiveUsers as $user) {

$phoneNo = $user->getPhone();

$fullname = $user->getFullname();

$call = $twilioClient->calls->create(

$phoneNo,

$twilioNumber,

[

'twiml' => '<Response><Say>Hi '.$fullname.', We noticed that you have not logged in to our platform for a while. Please log in to your dashboard to explore new features.</Say></Response>'

]

);

}

return new Response('Follow-up completed.');

}

}

```

In the code above:

  • The register() method handles user registration details and stores them in the database.

  • The login() method handles the user login process and updates the "last seen" timestamp of the user to the current login time.

  • The dashboard() method is used to display the user details on their dashboard page.

  • While the followUp() method is used to create a cron job endpoint that calls all users who are not active within a specified period.

Create the application templates

Now, we need to create template files that will render register, login and dashboard . To do that, navigate to the templates/user_follow_up directory and create the following files.

  • register.html.twig

  • login.html.twig

  • dashboard.html.twig

Add the code below to register.html.twig

```html
{# templates/user_follow_up/register.html.twig #}

{% extends 'base.html.twig' %}

{% block title %}User Registration{% endblock %}

{% block body %}

<div class="registration-form">

<h1>User Registration</h1>

{{ form_start(form, {'attr': {'class': 'form'}}) }}

<div class="form-group">

{{ form_row(form.fullname, {'attr': {'class': 'form-control', 'placeholder': 'Full Name'}}) }}

</div>

<div class="form-group">

{{ form_row(form.phone, {'attr': {'class': 'form-control', 'placeholder': 'Phone'}}) }}

</div>

<div class="form-group">

{{ form_row(form.email, {'attr': {'class': 'form-control', 'placeholder': 'Email'}}) }}

</div>

<div class="form-group">

{{ form_row(form.password, {'attr': {'class': 'form-control', 'placeholder': 'Password'}}) }}

</div>

<button type="submit" class="btn btn-primary">Register</button>

<p>Already a member? <a href="{{ path('app_login') }}">Login</a></p>

{{ form_end(form) }}

</div>

{% endblock %}

```

Add the code below to login.html.twig

```html

{# templates/user_follow_up/login.html.twig #}

{% extends 'base.html.twig' %}

{% block title %}Login{% endblock %}

{% block body %}

<div class="login-form">

<h1>Login</h1>

{{ form_start(form, {'attr': {'class': 'form'}}) }}

<div class="form-group">

{{ form_row(form.email, {'attr': {'class': 'form-control', 'placeholder': 'Email'}}) }}

</div>

<div class="form-group">

{{ form_row(form.password, {'attr': {'class': 'form-control', 'placeholder': 'Password'}}) }}

</div>

<button type="submit" class="btn btn-primary">Login</button>

<p>Don't have an account? <a href="{{ path('app_user_follow_up') }}">Register</a></p>

{{ form_end(form) }}

</div>

{% endblock %}

```

Add the code below to dashboard.html.twig

```html

{# templates/dashboard/index.html.twig #}

{% extends 'base.html.twig' %}

{% block title %}Dashboard{% endblock %}

{% block body %}

<div class="container">

<div class="jumbotron">

<h1 class="display-4">Welcome to Your Dashboard</h1>

<p class="lead">Email: {{ user.fullname }}</p>

<p class="lead">Email: {{ user.email }}</p>

<p class="lead">Phone Number: {{ user.phone }}</p>

<hr class="my-4">

<p>This is where you can manage your account and perform various actions.</p>

</div>

</div>

{% endblock %}

To add style to the application, within the templates/user_follow_up directory, open base.html.twig and replace its existing code with the following.

```html

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>{% block title %}{% endblock %}</title>

<link rel="stylesheet" href="stackpath.bootstrapcdn.com/bootstrap/4.5.2/..">

</head>

<body>

<div class="container">

{% block body %}{% endblock %}

</div>

<script src="code.jquery.com/jquery-3.5.1.slim.min.js" </script>

<script src="cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/d.." ></script>

<script src="stackpath.bootstrapcdn.com/bootstrap/4.5.2/.." ></script>

</body>

</html>

```

Tunnel the application

Let's make the application accessible over the internet using Ngrok. To do that run the command below.

```bash

ngrok http localhost:8000

```

Running the above command will generate a Forwarding URL as shown in the image below.

Copy the Forwarding URL somewhere, you will need it in the next section.

Schedule the follow-up call

Finally, let’s set up a cron job that runs at specific intervals using Cron-job. Cron-job is an online tool that allows developers to run cron jobs freely without installing any packages. To set up the cron job, log in to your Cron-job dashboard and click on the CREATE CRONJOB button located at the top right of the dashboard page.

Next, on the Create cronjob page, configure the cron job as follows:

  • Title: Follow-up call

  • URL: Paste the generated forward URL and add follow-up at the end.

  • Execution schedule: select every 10 minutes

Then, click on the CREATE button to save the configuration and start running the cron.

Test the application

To test the application, open http://localhost:8000/register on your browser and create a new account as shown in the GIF below.

After logging in to your dashboard, close the application tab. If you remain inactive for about 10 minutes, you will receive a follow-up call asking you to log in to your account to explore newly added features.

That is how to Send a follow-up call to users in the Symfony app using Twilio

Conclusion

In this article, you have learned how to send follow-up call messages to users who are not active in a Symfony application using Twilio Programmable Voice. This enhances the user experience and provides a more personal touch to your application. Remember, effective communication is key to user retention and engagement.

BioData

I am David Adewale, a software developer and and a technical writer passionate about extending my knowledge to the developers community through technical writing. Here is my LinkedIn profile.