Befriending Service with Symfony and Vonage

Published June 08, 2020 by Greg Holmes

With the current global situation, most countries are in some form of lockdown. Social distancing is critical right now to reduce the impact of Covid-19.
While some of us may have packed calendars of virtual hangouts, others may not have such a large pool of people to call or connect with to pass the time. My grandmother, for example, isn’t very technical, so is reliant on phone calls. Although she is lucky enough to have thirteen grandchildren, she often says she hasn’t heard from anyone some days and would like to speak to someone daily. At Vonage, we have regular opportunities to build something for our learning. In this opportunity, I chose to create a befriending service, which would introduce users that are vulnerable, lonely, or want a different person to talk to daily. The idea behind this is to enable people to make new friends while in lockdown, or at any time.


Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

Getting Started

Through this tutorial, you will build a new PHP project using Symfony. You will use Vonage Verify, Vonage Voice and MapQuest APIs. This project, once complete, will provide a service to allow users to register with their phone number, name, town, and county/state. On registration, the user will receive a verification code, which they are required to input into the website. Once verified, users get added to a list of people that each day, will receive a phone call, connected to another user to converse with. Finally, at the end of each day, each user will again receive an automated call requesting feedback regarding the call they’d had on that day.

Clone the Repository

The project uses the Docker containers Nginx, MySQL, PHP, and Ngrok containers for minimal server configuration changes.

Start this tutorial by cloning the existing repository. Cloning can be done by copying the following command into your Terminal, and then changing directory the project directory:

Your Terminal will output two directories:

  • docker where your Docker configurations are found (No need to change anything, unless you’re setting different database credentials)
  • project which is where your fresh Symfony project is stored

Run Docker

In your Terminal, change directory to the docker/ directory and run the following command to build and start your docker containers:

The command above downloads all of the preconfigured Docker containers, adding any custom changes defined in the files found within the docker/ directory. Build then compiles these containers and runs them as services for you to run your web application.

Once the command has completed, you will see a confirmation that the Docker containers are running, as shown in the example below:

Successfully Running Docker

Your docker containers and servers are now ready for development to begin!

The Application

User Registration and Verification

Install Symfony

If you look in the project directory, you’ll see the file structure of an empty Symfony installation the same as below:

Fresh Symfony Installation

Still within the docker/ directory, run the command below to install all of the PHH libraries described in the composer.json file:

The database configs for this Symfony project need to be stored somewhere. So in your IDE, under the project/ directory, create a new file called .env.local. Once created, add the following line to this new file:

A description of a breakdown of the line above is listed below:

  • Database name: befriending
  • Database User: user
  • Database Password: password
  • Database Host and Port: mysql:3306

These are values preset inside the docker/docker-compose.yml file. The current username and password are not secure, for security purposes, if you wish to change these please do so in both project/.env.local and docker/docker-compose.yml, then run:

Create a User Entity

A User entity is needed, which is the class that correctly defines the structure of the user database table. This table is the table where new user registrations get stored.

To do this, Symfony has released a make library, which allows users to use the Command Line Interface (CLI) to create certain classes.

Run the command below and follow the instructions listed to create the properties for the User entity:

  • Class name of the entity to create or update (e.g. DeliciousGnome):
    • User
  • Property 1
    • Name: phoneNumber
    • Type: string
    • Field Length: 255
    • Can Be Null?: no
  • Property 2
    • Name: name
    • Type: string
    • Field Length: 255
    • Can Be Null?: no
  • Property 3
    • Name: town
    • Type: string
    • Field Length: 255
    • Can Be Null?: no
  • Property 4
    • Name: county
    • Type: string
    • Field Length: 255
    • Can Be Null?: no
  • Property 5
    • Name: countryCode
    • Type: string
    • Field Length: 2
    • Can Be Null?: no
  • Property 6
    • Name: verificationRequestId
    • Type: string
    • field Length: 255
    • Can Be Null?: yes
  • Property 7
    • Name: verified
    • Type: boolean
    • Can Be Null?: no
  • Property 8
    • Name: active
    • Type: boolean
    • Can Be Null?: no

On completion of this command, two new files get created:

  • project/src/Entity/User.php
  • project/src/Repository/UserRepository.php

The UserRepository is a repository class where developers will find custom queries for the User entity. Ignore this class for now.

The entity created, is doesn’t currently reflect what’s in the database. To make the database reflect what you’ve defined in the User entity, run the command below to generate a new migration file:

If you wish to see the upcoming database changes, the generated migration files get saved to project/src/Migrations/.

So long as you’re happy with these changes, to persist them to the database, run the command below:

The User entity still needs some further changes that couldn’t happen with the maker CLI. To make these changes open this class, which you can find in project/src/Entity/User.php.

First, a class UniqueEntity is used as an annotation to ensure one of the properties in your User class is Unique (so only one entry with that value). Add this class at the top:

Add the following two lines:

The first alteration you’ve made is adding a hardcoded annotation for the table name. The reason is the word user is a keyword in MySQL. So the system needs the user table name wrapped in quotes to ensure whenever a query is made this change stops the code from mistaking the table name for the keyword.

The second change is adding the property phoneNumber as a UniqueEntity. Using UniqueEntity will stop the chance of multiple users having the same phone number.

Now, update the definition of the phoneNumber property to be a unique field:

The final change to the User entity is to ensure active, and verified fields are defaulted to false so create a __construct() function to do this:

You’ve now created a new User entity, which is how the system creates new users, as well as saving and editing users to the database.

Install Webpack Encore

Bootstrap is used in this tutorial to make the designs of the pages better designed and laid out. Symfony recently released Webpack Encore, which is a much simpler way to integrate Webpack into your Symfony or PHP application. To install Webpack Encore, run the following commands:

Running the yarn install command will set up the project to handle Javascript files too. This command creates an assets directory, and within this directory, there is a js/app.js file and a css/app.css file.

Now Bootstrap is installed with the following command:

The Symfony application doesn’t know Bootstrap exists yet. To include this into the project, open the project/assets/css/app.scss file and add the following line:

Bootstrap JS required jQuery and PopperJs, so install these with the command below:

Again, the Symfony application doesn’t know what jQuery and PopperJs are. So include them into the project by opening the project/assets/js/app.js file and adding the following updates to it:

These CSS and JS files need compiling to get accessed in the templates. To do this run the following command:

The required frontend parts are installed and configured!

Install Vonage SDK and libphonenumber-for-php

This tutorial requires two further PHP libraries:

  • the Vonage SDK,
    • to send verification requests,
    • verify users
    • make phone calls
    • handle DTMF phone call input.
  • Giggsey’s libphonenumber-for-php, which manipulates phone numbers into internationalized or nationalized formats.

To install the Vonage PHP SDK run:

To use environment variables from the file you’ll create in the next step, Symfony’s DotEnv component is needed. To install this component run the following command from your Terminal:

On the Vonage Developer Dashboard, you’ll find “Your API credentials”, make a note of these.

Within the project/ directory, add the following three lines to the .env.local file (replacing the api_key and api_secret with your key and secret):

The VONAGE_BRAND_NAME is the name of the company/brand you’re representing for the verification:

A way to verify telephone numbers are valid before making the verification check is needed. You also need to make sure the phone number is in the correct format for the Verify API to know which region (country code) the number belongs to). You’re going to use Giggsey’s PHP port of Google libphonenumber to make sure phone numbers are formatted correctly.

Run the command below to install libphonenumber-for-php:

You now have the two key libraries needed to:

  • Format phone numbers,
  • Ensure phone numbers are valid,
  • Verify with Vonage Verify API
  • Make phone calls

Build the Verify Util

Create a new directory inside project/src/ called Util, and within this new directory, create a new file called VonageVerifyUtil.php. This new file is the utility class that contains the functionality to verify users when they register. Inside this new file copy the code below:

Add two new methods to this class. The purpose of these two methods is to:

  • convert the telephone number to an internationalized format.
  • convert the telephone number to a nationalized number format.

The next function the system needs is to make the verification request for new registrations. Add the method below to do process the creation of a verification request:

note if you’re interested in a different workflow for the verification process, please look at the Vonage documentation.

The next step to verifying is to take users input of the verification code and send a request to verify with the verification code:

The last function isn’t used in this utility class but will be in several places of the project. This function extracts the requestId from an API response. Add the following:

Create a new user

To create a new Register controller with Symfony’s make library, run the command below:

Where it says: Choose a name for your controller class (e.g. AgreeableGnomeController): type RegisterController and press enter.

You will see the output in your Terminal for two new generated files:

The VonageVerifyUtil needs injecting as a service into the newly created RegisterController so open the file src/Controller/RegisterController.php and inject it in the __construct() function.

In your browser, open the register page with the URL http://localhost:8081/register. You get greeted with:

Register View

It’s time to create the Register page!

Symfony’s make library is proving to be very useful, so far it has created an Entity, a Controller, and now it’s going to create a Form. To create the UserType form, run the following command:

When it asks for a name, enter UserType, and when it asks for an Entity type: User.

You will find the newly created UserType in project/src/Form/UserType.php. Open it, locate the buildForm() function and replace its contents with:

At the top of the class, several new classes will need to be included, ChoiceType, SubmitType, Length, NotBlank, TelType, TextType. Add these to the top of the file, as shown below:

Create Registration Template

The base template needs to include Bootstrap for our Bootstrap CSS classes to be recognised and displayed.

Open project/templates/base.html.twig, the base template that will be included by all other templates in the project, and replace the contents with:

Now that the template has Bootstrap included, time to update the index.html.twig template to display our form and any other information. Open project/templates/register/index.html.twig and replace everything in between {% block body %} and {% endblock %} with:

Complete Registration Process

You’ve now built a template and a form class, but the project currently uses neither of these files. You need to include these into your RegisterController method called index(). Head back to project/src/Controller/RegisterController.php.

Update the contents of the method index() as shown below:

The changes in the example above do the following:

  • Creates a new empty User object,
  • Creates a new instance of the UserType form class using the new User object as the data class with which to structure the form with,
  • Handles the form request to determine whether the form:
    • has been submitted,
    • form content is valid (for example, whether phoneNumber is empty, is not unique)
  • If the form is submitted and valid, then the function:
    • persists the new user to the database
    • flushes these changes
    • Sends a new request to the Verify API,
    • saves the verification request id to the user record in the database
  • If the form hasn’t been submitted or isn’t valid the function renders the template to the user, with the form included into the template.

Several classes need importing into the current class, such as User and UserType, include those at the top as shown below:

To summarise, you’ve just created a new RegisterController, a User entity and UserType form. You’ve updated your template for the index() method inside the RegisterController to display the form fields you’re expecting the user to see.

You can see these changes on http://localhost:8081/register, or as shown in the example below:

Registration Page

Create Verify Form

Create the VerifyType form with the following command:

For the name input: VerifyType
And for which Entity to use, choose User

On submission, you’ll see something as shown in the image below:

Creating a Verify Form

Creating the VerifyType form this way means that the system has automatically inserted all of the User entity properties into this form. None of these fields are required, so update the code with the following:

The above code defines the form that is expected for submitting a verification code. This form contains two fields, the verificationCode and submit. Each field has its unique attributes for validation and display purposes on the form or submission of the form.

At the top of the class, some class imports need importing. So copy the code in the top of the class, as shown below:

Handle Verification

So, you’ve built your registration page, but what next? You need to create a page for the user to input the verification code they receive from the Verify API. Inside the RegisterController create a new method below the index() one called verify(). The example below shows this and the contents of that method:

At the top of the class, include the Response, VerifyType, ParamConverter, and Verification classes as shown below:

The template to display this form is now needed. Create a new file in project/templates/register/ called verify.html and copy the contents of the code block below into this file:

Handling a Successful Verification

Within the RegisterController the project now needs a success page for when the user has successfully verified. Creating a new registerSucess() method, which will display a template with the message output that the registration was successful:

Now create a new file in project/templates/register/ called success.html.twig and output the following:

There are a few small changes needed to finalise the verification part. Redirects are required when each form submission is successful in moving the user on to the next page. So, in RegisterController, under public function index(), find and add the following:

And inside the method public function verify(Request $request, User $user), find and add the following:

Latitude and Longitude

In preparation for finding registered users geographically close to other registered users, the system needs to have the latitude and longitude of each user, on registration. The project will use a service called MapQuest, which takes the Town and County/State of the user and converts it into the required latitude and longitude.

Register for an account on: MapQuest.

Open .env.local and add the following new entries (replacing with your MapQuest API key):

The User entity needs two properties to store the latitude and longitude. To update this entity, run Symfony’s make command as shown below, and follow the instructions:

  • Class name of the entity to create or update (e.g. DeliciousGnome):
    • User
  • Property 1
    • Name: latitude
    • Type: decimal
    • Precision: 20
    • Scale: 16
    • Can Be Null?: yes
  • Property 2
    • Name: longtitude
    • Type: decimal
    • Precision: 20
    • Scale: 16
    • Can Be Null?: yes

Running the command below will generate a new migration file with these database changes:

If you wish to see the upcoming database changes, the generated migration files get saved to project/src/Migrations/.

So long as you’re happy with these changes, to persist them to the database, run the command below:

Guzzle is a PHP HTTP client that allows projects to make requests to other web services, such as POST and GET requests to APIs. Guzzle makes it easy to make a GET request to Map Quest’s API endpoint to receive latitude and longitudes of the user’s location.

The next step is to create a MapQuestUtil to make this GET request and handle the response to only retrieve the required fields. Create a new file in project/src/Util/ called MapQuestUtil.php and copy the following code into the newly created file:

The code above is a new method that takes a User object and makes a request to MapQuest’s API providing the users location which is their Town and their County. There are then several steps following this request to make sure that a location gets returned, and whether the expected fields for the latitude and longitude get included in the response.

Time to call this MapQuestUtil class and getLatLongByAddress() method. Open the RegisterController.

The new Utility class needs injecting as a service. In the __construct() method update it as shown below:

Now that the MapQuestUtil has been injected, you need to call the function and then store the details returned to the User. In the index() function, find the line if ($form->isSubmitted() && $form->isValid()) { and add a new line below. Adding the following:

At this point in the tutorial, you have created a new Symfony project, configured this project with your database. You’ve also created a new User entity, which maps with your user database table. You’ve created two new forms, a UserType and VerifyType, which get used to validate your new users and whether the input verification code is valid. You’ve also created several pages for the user to access, a /register, /verify, and /success.

You’re now at a natural breakpoint, halfway in this tutorial! You can now test the registration process of your project.

Go to the registration page and follow the instructions over the next two pages. Please input a valid phone number. Otherwise, you will not be able to verify your account.


Match Users

Create Match Entity

A new entity is needed to connect two users: gather their feedback, and store records of users who get connected. This new entity will store a relationship to the two callers in the matched object and the name of the conference call (the names of both users concatenated by ‘-‘). To do this, make use of the make library as shown below, and follow its instructions for the three new properties:

  • Name: Match
  • 1 – callerOne – ManyToOne – User – not nullable – rest defaults
  • 2 – callerTwo – ManyToOne – User – not nullable – rest defaults
  • 3 – conferenceName – string – 255 – not nullable

Before you make the migration, another is needed. There is an extensions bundle for Doctrine that allows the code to make use of Doctrine extensions such as Timestampable, to use two lines in our entities for having createdAt and updatedAt fields. To do this install stof/doctrine-extensions-bundle:

The config is needed for this new extensions bundle. Open stof_doctrine_extensions.yaml found in: project/config/packages/ and update it to mirror:

You need to make some further changes in the newly created Match entity. Open this class, which you can find in project/src/Entity/Match.php.

First, include the TimestampableEntity into the project by adding the following import:

Now use the TimestampableTrait in the Match class, as shown below:

Running the command below will generate a new migration file with these database changes:

If you wish to see the upcoming database changes, the generated migration files get saved to project/src/Migrations/.

So long as you’re happy with these changes, to persist them to the database, run the command below:

Configure Ngrok

Ngrok is a cross-platform application that allows users to expose their development server to the Internet without having to configure their router. Ngrok does this by creating a tunnel between your server and the Ngrok subdomain.

Time to configure Ngrok to allow the Vonage Application (which is created in the next step), to have an Ngrok sub-domain to make its webhook requests.

In the directory docker/ngrok/ rename the file ngrok.conf.local to ngrok.conf, and replace <auth_token> with your Ngrok auth token:

** Note ** You can find your auth token in your dashboard on Ngrok

Now, to update your Docker containers with the correct Ngrok subdomain URL, Docker needs to be restarted. You can restart Docker with the following command:

Once you have restarted Docker, go to, and copy the Ngrok subdomain shown on the status page.

Ngrok Status Page

If you now take that Ngrok subdomain and access it: ngroksubdomain/register, replacing ngroksubdomain with your Ngrok subdomain, you’ll see your registration page. For me, the example was:

Please take note of this Ngrok subdomain because it’ll be needed next to configure your Vonage Application.

Create a Vonage Application

Log into your Vonage Dashboard. On the left of the page is a link Your Applications, click this. Once loaded, click the Create a new Application button.

Set the Application Name, for the tutorial, I set the name to Befriending Service, but it can be whatever you want.

Click the button to Generate public and private key. Your browser will have a download for the private.key file. Save this into your project/ directory.

Under Capabilities, toggle on Voice.

Update all three input boxes you now see with the webhook URLs needed for Vonage to make callbacks during phone calls. For example, for the events text box:, remember to replace a551f0297ed8 with your subdomain you created in the step above. Make sure to do this for all three input boxes:

  • Event – https:///webhooks/event
  • Answer – https:///webhooks/answer
  • Fallback – https:///webhooks/fallback

Finally, click Generate new application.

The page that now loads will display your Application ID. Make a note of this!

In your project/ directory, open .env.local and add the following two lines, making sure to replace <application id> with your application id, and replace <ngrok_url> with your Ngrok URL:

You’ve now created a Vonage Application and stored the credentials within your project. Storing these credentials enable the project to now make and receive phone calls.

Vonage Call Util

Create a new file in project/src/Util/ called VonageCallUtil.php.

This Utility class currently does nothing other than inject two new services on creation. These services are the VonageClient, which is the part that makes use of the Vonage Voice API to make and receive calls. The other service injected is the VonageVerifyUtil, which gets called when the project needs specific methods from within this Util.

The Utility class gets used to initiate the call between two users. So the first method needed will be createConferenceBetween()

The code above is a method that takes two users as arguments of that method. The method then generates a new object of the Match entity as a connection between the two users. A Nexmo Call Control Object (NCCO) array is created, which is a set of instructions for Vonage Voice to know what to expect and steps expected to make the call.

This array contains two further array elements. The first is to use the persona Amy to introduce the user to the call and explain what the phone call is regarding. Amy asks the user if they’d like to join a conference call or not, requesting them to enter 1 for yes or 2 for no. The second array is to direct the user to the next webhook endpoint, which is to join the conference.

You may have noticed that there are two calls to a makeCall() method, which you haven’t yet created. Create this new method by copying the following code into your VonageCallUtil:

This method first calls another method, getInternationalizedNumber(), found within the vonageVerifyUtil. It takes the $caller which is an instance of User and converts the country code and phone number into an Internationalized number for Vonage to call.

The next step in the above method is to initialize the call by setting a to, from and inserting the ncco object.

Finally, within this VonageCallUtil several classes need to be included as they get used in the examples above. Copy the additions shown in the example below into your file:

To summarise, the functionality above doesn’t yet get called, but it creates a new Match object, saves two users to it, and then makes two calls for each user. No conference gets made or any way to link the two calls together yet. The conference call is made next!

Matching Users

Because the project is to have daily calls, a command is needed, which will get run as a cronjob. In your Terminal, inside the docker/ directory, make use of the make library to create a new command:

When the library requests a name for your new command enter app:match-users.

Running this command will create a new file called MatchUsersCommand.php inside: project/src/Command/MatchUsersCommand.php.

Open this the newly created MatchUsersCommand.php file.

The command you generated has some default settings, most of which you won’t need for this project. Find protected function configure() and replace the contents of this function with:

Two services need injecting into the class via the __construct() method. The two services required are the EntityManagerInterface, which allows the code to make database queries or have access to the repository methods for your entities. The second service needed is the VonageCallUtil to call our previously created functionality that calls two users and connects them.

Update the following code inside the MatchUsersCommand.php file to match as shown below:

Now it’s time to start building the command functionality!

For the query to work, the project needs some Doctrine Extensions that you’re going to write in the next step. These extensions come from a third party library by Benjamin Eberlei. To install this library, inside your Terminal run the following command:

Now, to enable the required extensions. Open project/config/packages/doctrine.yaml, and with the same indentation of auto_generate_proxy_classes, naming_strategy, auto_mapping, and mappings add:

Note: Please pay attention to how it is indented to make sure that it is correct YAML format.

Back inside your MatchUsersCommand find the execute() function. This function is the location where all of the work happens for the command.

To start with, delete all of the contents of this method.

The first part needed in this method is to have an instance of the Match and User Repositories into variables, and to get all users that are marked as active:

Following this, it’s time to loop through each active user, to initiate calls and connect them with another user. Add the following loop:

Now the system needs to find users geographically close to the current $callerOne to connect them. The query is quite a lengthy one and will belong inside the UserRepository. Open the file project/src/Repository/UserRepository.php

Below the __construct method, add the following new method:

The method in the code block above, takes a user object, an array of the current active users and an integer variable of a predetermined distance.

This method creates a new query which calculates the distance of all users from the given user and ensures they’re all active and verified. The last check is to make sure that the returned users are within the given distance of the passed in user.

Back inside the MatchUsersCommand find the line: foreach ($activeUsers as $activeUser) { and add the following:

The command gets created to find users to match together and initiate the call. However, webhook URLs are needed to progress the call from automated introduction to connecting the users into a conference call.


You now need a WebhooksController, so in your Terminal, make use of the make library to run the following command:

Where it says: Choose a name for your controller class (e.g. AgreeableGnomeController): type WebhooksController and press enter.

You’ve now created two new files:

Open the newly created WebhooksController.php found within project/src/Controller/

This Controller will need to make use of three services, VonageVerifyUtil, VonageCallUtil, and EntityManagerInterface. So start by injecting them into the Controller’s construct, as shown below:

The two methods below do what their naming states. The first one is to find a user by their phone number, while the second finds a Match instance by a User that you created today. Add these to your WebhooksController.

One method called in the above example doesn’t yet exist, this is to find matches by the user and current date. Open project/src/Repository/MatchRepository.php and inside this class, add the method shown below:

At the top of the file add the import for the entity User:

The above query finds a Match entry where the user is either callerOne or callerTwo and the Match was created today. There is a query function that Doctrine doesn’t know of in here. Date(m.createdAt). When calculating the distance between people you installed beberlei/doctrineextensions. This library has functionality to include the Date extension. So open project/config/packages/doctrine.yaml and add the following two lines:

Previously, you created a method called createConferenceBetween(), inside this method was an NCCO array that referenced a URL of /webhooks/joinConference, if you were to run the example now, nothing would happen as the URL doesn’t exist. Create a new joinConference() method inside your WebhooksController with the annotation containing this URL.

The method will have two purposes. The first is to validate the user’s input, ensuring they only enter valid options before proceeding. These inputs are 1 for yes, and 2 for no.

The second purpose of this method is to respect the user’s input. If they choose to progress to the call, then redirect them to the next action, which connects them to the conference call. If the user selects ‘2’, to not join a call with someone, the system responses confirming their request not to join a call and ends the call.

Four classes get used here that we haven’t included into our file, Request, Match, User, and JsonResponse. Include these above the class:

At this point, you now have a user registration form, which takes the user through the verification process. You also have a command that when run will loop through all active and verified users connecting them to other active and verified users within a maximum of a 100-mile radius of each other.

You can now test this! If you have two phone numbers available to you, go ahead and register on your registration page. Be sure to follow the verification process, and make sure that the location you’ve entered for both users are within 30 miles from each other.

Once you’ve registered users, in your Terminal, inside the docker/ directory, run the following command:

Both phone numbers will receive a call and asked if they’d like to connect to a call with another person!

Get Feedback

A second command is needed to retrieve feedback from each user. This command will collect feedback from the calls of the current day from each user. This command will also be a cronjob scheduled to run several hours after the matching call has finished.

But before making this new command, the Match entity needs updating, as when you created the Match entity with the make library, you can also update it with new properties. Run the following command in your Terminal and follow the instructions of each step below:

  • Class name of the entity to create or update (e.g. DeliciousGnome):
    • Match
  • Property 1
    • Name: callerOneFeedbackAccepted
    • Type: boolean
    • Can Be Null?: yes
  • Property 2
    • Name: callerTwoFeedbackAccepted
    • Type: boolean
    • Can Be Null?: yes
  • Property 3
    • Name: callerOneCallSuccessful
    • Type: boolean
    • Can Be Null?: yes
  • Property 4
    • Name: callerTwoCallSuccessful
    • Type: boolean
    • Can Be Null?: yes

The updated entity doesn’t currently reflect what’s in the database. To make the database reflect what you’ve defined in the Match entity, run the command below to generate a new migration file:

If you wish to see the upcoming database changes, the generated migration files get saved to project/src/Migrations/.

So long as you’re happy with these changes, to persist them to the database, run the command below:

First, you need to get a list of today’s matches. So in your MatchRepository add a new function that queries the database to find all matches for the current day. This query will only find matched users where the callerOneCallSuccessful flag is empty:

Now it’s time to create the new command that makes use of the new changes, in your terminal type:

When it asks for a command name enter: app:get-feedback. This command will generate a new command file inside project/src/command/ called GetFeedbackCommand.php Open the newly created file.

This controller needs two services injected into it through the __construct() method. The same two services injected into the MatchUsersCommand. Copy the adjustments shown below into your GetFeedbackCommand:

By carrying out the changes above, your command now has access to the VonageCallUtil and EntityManagerInterface.

Update the contents of configure() with what’s shown in the example below. This example sets the description of the command:

It’s time to implement the functionality to initiate collecting feedback from matched users. Replace the contents of the method execute() with:

The above functionality collects the matches for the day, it then loops through each match initiating a call with caller one and caller two requesting feedback from each on their previous call.

As you can see you’ve made a call to makeFeedbackCall() from the VonageCallUtil, but the method doesn’t exist yet. So open the VonageCallUtil inside project/src/util/VonageCallUtil.php and add a new method:

The example above references to an eventUrl which is your Ngrok URL and /webhooks/userFeedback. This endpoint doesn’t exist in our WebhooksController yet.

The method will have two purposes. The first is to validate the user’s input, ensuring they only enter valid options before proceeding. These inputs are 1 for yes, and 2 for no.

The second purpose of this method is to respect the user’s input. If they choose to progress to give feedback, then redirect them to the next action, which is asking the first feedback question. If the user selects ‘2’, do not provide feedback, the system responses confirming their request not give feedback and ends the call.

So in the WebhooksController create a new method called getUserFeedback() as shown below:

The example above references to an eventUrl which is your Ngrok URL and /webhooks/userFeedbackCallSuccess. This endpoint doesn’t exist in our WebhooksController yet.

The method will have two purposes. The first is to validate the user’s input, ensuring they only enter valid options before proceeding. These inputs are 1 for yes, and 2 for no.

The second purpose of this method is to respect the user’s input. If they choose either ‘1’ or ‘2’, then save the users input to either the property setCallerOneCallSuccessful or the property setCallerTwoCallSuccessful as true or false (whether they enjoyed the call or not).

Following a successful option entry, the code then redirects the user to the next action of the call, which is the endpoint /webhooks/userFeedbackContinue

So in the WebhooksController create a new method called getUserFeedbackCallSuccess() as shown below:

The example above references to an eventUrl which is your Ngrok URL and webhooks/userFeedbackContinue. This endpoint doesn’t exist in our WebhooksController yet.

The method will have two purposes. The first is to validate the user’s input, ensuring they only enter valid options before proceeding. These inputs are 1 for yes, and 2 for no.

The second purpose of this method is to respect the user’s input. If they choose ‘1’, then they continue to be subscribed to the service for a call the following day.

If the user chooses ‘2’, then they are set to inactive and no longer called.

The call ends following a successful option entry.

So in the WebhooksController create a new method called getUserFeedbackContinue() as shown below:

The last addition required to the project is a function that you may have noticed being called isFirstOrSecondCaller() but needs creating. This method takes an instance of Match and a single User object; it then determines whether the user given is callerOne or callerTwo of the Match. Add this to the bottom of your class:


If you have followed this tutorial from start to finish, you have now created a project from a fresh Symfony installation.

This new project has a registration page, which on submission sends a verification request to Vonage Verify. This request triggers an SMS for the owners of the phone number on registration. The verification code in the SMS needs entering into the next page the user sees. On successful verification, the user is redirected a success page letting them know of what time to expect phone calls.

The second part of this project is the Vonage Voice feature. By using commands, the users get matched, called, and joined into a conference call. Later that day, a second command is run to request feedback from each person matched on the current day.

The finished code for this tutorial can be found on the end-tutorial branch on this GitHub repository.

Below are a few other tutorials we’ve written implementing Vonage Verify or Voice into projects:

Don’t forget, if you have any questions, advice or ideas you’d like to share with the community, then please feel free to jump on our Community Slack workspace, or pop a reply below 👇. I’d love to hear back from anyone that has implemented this tutorial and how your project works.

Leave a Reply

Your email address will not be published.

Get the latest posts from Nexmo’s next-generation communications blog delivered to your inbox.

By signing up to our communications blog, you accept our privacy policy , which sets out how we use your data and the rights you have in respect of your data. You can opt out of receiving our updates by clicking the unsubscribe link in the email or by emailing us at