
Partager:
Ancien développeur éducateur @Vonage. Issu d'une formation PHP, mais pas limité à un seul langage. Joueur passionné et adepte du Raspberry pi. On le trouve souvent en train de faire du bloc dans des salles d'escalade.
Service d'amitié avec Symfony et Vonage
Temps de lecture : 67 minutes
Compte tenu de la situation mondiale actuelle, la plupart des pays sont bloqués sous une forme ou une autre. La distanciation sociale est essentielle pour réduire l'impact de Covid-19. Si certains d'entre nous ont des agendas remplis de rencontres virtuelles, d'autres ne disposent pas d'un tel vivier de personnes à appeler ou à contacter pour passer le temps. Ma grand-mère, par exemple, n'est pas très technique et dépend donc des appels téléphoniques. Bien qu'elle ait la chance d'avoir treize petits-enfants, elle dit souvent qu'elle n'a de nouvelles de personne certains jours et qu'elle aimerait parler à quelqu'un tous les jours. Chez Vonage, nous avons régulièrement l'occasion de construire quelque chose pour notre apprentissage. Dans le cadre de cette opportunité, j'ai choisi de créer un service d'amitié, qui présenterait les utilisateurs vulnérables, solitaires ou souhaitant parler à une personne différente tous les jours. L'idée sous-jacente est de permettre aux gens de se faire de nouveaux amis pendant qu'ils sont enfermés, ou à tout moment.
Conditions préalables
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.
This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.
Pour commencer
Dans ce tutoriel, vous construirez un nouveau projet PHP en utilisant Symfony. Vous utiliserez Verify, Vonage Voice et MapQuest API. Ce projet, une fois achevé, fournira un service permettant aux utilisateurs de s'enregistrer avec leur numéro de téléphone, leur nom, leur ville et leur comté/état. Lors de l'enregistrement, l'utilisateur recevra un code de vérification qu'il devra saisir sur le site web. Une fois vérifié, l'utilisateur est ajouté à une liste de personnes qui, chaque jour, recevront un appel téléphonique les mettant en relation avec un autre utilisateur avec lequel ils pourront converser. Enfin, à la fin de chaque journée, chaque utilisateur recevra à nouveau un appel automatisé lui demandant son avis sur l'appel qu'il a reçu ce jour-là.
Cloner le référentiel
Le projet utilise l'interface Docker de Docker Nginx, MySQL, PHPet Ngrok pour une modification minimale de la configuration du serveur.
Commencez ce tutoriel en clonant le dépôt existant. Le clonage peut être effectué en copiant la commande suivante dans votre Terminal, puis en changeant le répertoire du projet :
Votre terminal affichera deux répertoires :
dockeroù se trouvent vos configurations Docker (il n'est pas nécessaire de changer quoi que ce soit, sauf si vous définissez des informations d'identification différentes pour la base de données).projectqui est l'endroit où votre nouveau Symfony est stocké
Exécuter Docker
Dans votre Terminal, changez de répertoire pour le répertoire docker/ et exécutez la commande suivante pour construire et démarrer vos conteneurs Docker :
La commande ci-dessus télécharge tous les conteneurs préconfigurés, en ajoutant toutes les modifications personnalisées définies dans les fichiers trouvés dans la base de données. Docker en y ajoutant toutes les modifications personnalisées définies dans les fichiers du répertoire docker/ . Build compile ensuite ces conteneurs et les exécute en tant que services pour que vous puissiez exécuter votre application web.
Une fois la commande terminée, vous verrez une confirmation que les conteneurs sont en cours d'exécution. Docker sont en cours d'exécution, comme le montre l'exemple ci-dessous :

Vos conteneurs et serveurs Docker sont maintenant prêts pour le développement !
L'Applications
Enregistrement et vérification de l'utilisateur
Installer Symfony
Si vous regardez dans le répertoire project vous verrez la structure des fichiers d'une installation Symfony vide comme ci-dessous :

Toujours dans le répertoire docker/, exécutez la commande ci-dessous pour installer toutes les bibliothèques PHH décrites dans le fichier composer.json :
Les configurations de la base de données pour ce projet Symfony doivent être stockées quelque part. Donc dans votre IDE, sous le répertoire project/ créez un nouveau fichier appelé .env.local. Une fois créé, ajoutez la ligne suivante à ce nouveau fichier :
Une description de la répartition de la ligne ci-dessus est présentée ci-dessous :
Nom de la base de données :
befriendingUtilisateur de la base de données :
userMot de passe de la base de données :
passwordHôte et port de la base de données :
mysql:3306
Il s'agit de valeurs prédéfinies dans le fichier docker/docker-compose.yml . Le nom d'utilisateur et le mot de passe actuels ne sont pas sécurisés, pour des raisons de sécurité, si vous souhaitez les modifier, veuillez le faire dans les champs project/.env.local et docker/docker-compose.ymlpuis exécutez :
Créer une entité utilisateur
A User entité est nécessaire, c'est-à-dire la classe qui définit correctement la structure de la table de la base de données. user de la base de données. C'est dans cette table que sont stockés les nouveaux enregistrements d'utilisateurs.
Pour ce faire, Symfony a publié une bibliothèque make qui permet aux utilisateurs d'utiliser l'interface en ligne de commande (CLI) pour créer certaines classes.
Exécutez la commande ci-dessous et suivez les instructions pour créer les propriétés de l'entité User entité :
Nom de classe de l'entité à créer ou à mettre à jour (par exemple DeliciousGnome) :
User
Bien 1
Nom :
phoneNumberType :
stringLongueur du champ :
255Peut être nul ?
no
Bien 2
Nom :
nameType :
stringLongueur du champ :
255Peut être nul ?
no
Propriété 3
Nom :
townType :
stringLongueur du champ :
255Peut être nul ?
no
Propriété 4
Nom :
countyType :
stringLongueur du champ :
255Peut être nul ?
no
Bien 5
Nom :
countryCodeType :
stringLongueur du champ :
2Peut être nul ?
no
Bien 6
Nom :
verificationRequestIdType :
stringLongueur du champ :
255Peut être nul ?
yes
Bien 7
Nom :
verifiedType :
booleanPeut être nul ?
no
Propriété 8
Nom :
activeType :
booleanPeut être nul ?
no
À l'issue de cette commande, deux nouveaux fichiers sont créés :
project/src/Entity/User.phpproject/src/Repository/UserRepository.php
La classe UserRepository est une classe de référentiel dans laquelle les développeurs trouveront des requêtes personnalisées pour l'entité User pour l'entité Ignorez cette classe pour l'instant.
L'entité créée ne reflète pas actuellement ce qui se trouve dans la base de données. Pour que la base de données reflète ce que vous avez défini dans l'entité User exécutez la commande ci-dessous pour générer un nouveau fichier de migration :
Si vous souhaitez voir les changements à venir dans la base de données, les fichiers de migration générés sont enregistrés dans le fichier project/src/Migrations/.
Si vous êtes satisfait de ces modifications, pour les conserver dans la base de données, exécutez la commande ci-dessous :
L'entité User a encore besoin de quelques modifications qui ne peuvent pas être effectuées avec l'interface utilisateur. maker CLI. Pour effectuer ces changements, ouvrez cette classe, que vous trouverez dans le fichier project/src/Entity/User.php.
Tout d'abord, une classe UniqueEntity est utilisée comme annotation pour s'assurer que l'une des propriétés de votre classe User est Unique (c'est-à-dire qu'il n'y a qu'une seule entrée avec cette valeur). Ajoutez cette classe en haut de la page :
use Doctrine\ORM\Mapping as ORM;
+ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;Ajouter les deux lignes suivantes :
/**
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
+ * @ORM\Table(name="`user`")
+ * @UniqueEntity("phoneNumber")
*/La première modification que vous avez apportée consiste à ajouter une annotation codée en dur pour le nom de la table. La raison en est que le mot user est un mot-clé dans MySQL. Le système a donc besoin que le nom de la table user entre guillemets pour s'assurer qu'à chaque fois qu'une requête est effectuée, cette modification empêche le code de confondre le nom de la table avec le mot-clé.
La deuxième modification consiste à ajouter la propriété phoneNumber en tant que UniqueEntity. L'utilisation de UniqueEntity permet d'éviter que plusieurs utilisateurs aient le même numéro de téléphone.
Maintenant, mettez à jour la définition de la propriété phoneNumber pour en faire un champ unique :
/**
- * @ORM\Column(type="string", length=255)
+ * @ORM\Column(type="string", length=255, unique=true)
*/
private $phoneNumber;La dernière modification apportée à l'entité User est de s'assurer que activeet verified soient mis par défaut à false, il faut donc créer une fonction __construct() pour ce faire :
public function __construct()
{
$this->setActive(false);
$this->setVerified(false);
}
Vous avez maintenant créé une nouvelle entité Utilisateur, qui permet au système de créer de nouveaux utilisateurs, ainsi que d'enregistrer et de modifier des utilisateurs dans la base de données.
Installer Webpack Encore
Bootstrap est utilisé dans ce tutoriel pour améliorer le design des pages. Symfony a récemment publié Webpack Encore, qui est un moyen beaucoup plus simple d'intégrer Webpack dans votre application Symfony ou PHP. Pour installer Webpack Encore, exécutez les commandes suivantes :
L'exécution de la commande yarn install permet de configurer le projet pour qu'il gère également les fichiers Javascript. Cette commande crée un répertoire assets et dans ce répertoire, il y a un fichier js/app.js et un fichier css/app.css fichier.
Bootstrap est maintenant installé avec la commande suivante :
L'application Symfony ne sait pas encore que Bootstrap existe. Pour l'inclure dans le projet, ouvrez le fichier project/assets/css/app.scss et ajoutez la ligne suivante :
@import "../../node_modules/bootstrap";Bootstrap JS nécessite jQuery et PopperJs, donc installez-les avec la commande ci-dessous :
Encore une fois, l'application Symfony ne sait pas ce que sont jQuery et PopperJs. Il faut donc les inclure dans le projet en ouvrant le fichier project/assets/js/app.js et en y ajoutant les mises à jour suivantes :
import '../css/app.css';
+ import $ from 'jquery';
+ require('bootstrap');Ces fichiers CSS et JS doivent être compilés pour être accessibles dans les modèles. Pour ce faire, exécutez la commande suivante :
Les parties requises du frontend sont installées et configurées !
Installer Vonage SDK et libphonenumber-for-php
Ce tutoriel nécessite deux autres bibliothèques PHP :
le SDK de Vonage,
pour envoyer des demandes de vérification,
Verify les utilisateurs
passer des appels téléphoniques
gérer les appels téléphoniques DTMF.
libphonenumber-for-php de Giggsey, qui manipule les numéros de téléphone dans des formats internationalisés ou nationalisés.
Pour installer le SDK PHP de Vonage, exécutez :
Pour utiliser les variables d'environnement du fichier que vous allez créer à l'étape suivante, le composant DotEnv de Symfony est nécessaire. Pour installer ce composant, lancez la commande suivante depuis votre Terminal :
Sur le tableau de bord du développeur Vonagevous trouverez "Your API credentials", notez-les.
Dans le répertoire project/ ajoutez les trois lignes suivantes au fichier .env.local (en remplaçant api_key et api_secret par votre clé et votre secret) :
Le nom de l'entreprise ou de la marque que vous représentez pour la vérification VONAGE_BRAND_NAME est le nom de l'entreprise ou de la marque que vous représentez pour la vérification :
VONAGE_API_KEY=<api_key>
VONAGE_API_SECRET=<api_secret>
VONAGE_BRAND_NAME=BefriendingIl est nécessaire de trouver un moyen de vérifier que les numéros de téléphone sont valides avant de procéder à la vérification. Vous devez également vous assurer que le numéro de téléphone est dans le bon format pour que l'API Verify sache à quelle région (code pays) le numéro appartient). Vous allez utiliser le portage PHP de Giggsey de la librairie Google libphonenumber de Giggsey pour vous assurer que les numéros de téléphone sont formatés correctement.
Exécutez la commande ci-dessous pour installer libphonenumber-for-php:
Vous disposez maintenant des deux bibliothèques clés nécessaires pour :
Formater les numéros de téléphone,
S'assurer que les numéros de téléphone sont valides,
Verify avec Vonage Verify API
Passer des appels téléphoniques
Construire l'utilitaire Verify
Créer un nouveau répertoire à l'intérieur de project/src/ appelé Utilet dans ce nouveau répertoire, créez un nouveau fichier appelé VonageVerifyUtil.php. Ce nouveau fichier est la classe utilitaire qui contient la fonctionnalité permettant de vérifier les utilisateurs lorsqu'ils s'inscrivent. Dans ce nouveau fichier, copiez le code ci-dessous :
<?php
namespace App\Util;
use App\Entity\User;
use Nexmo\Client as VonageClient;
use Nexmo\Client\Credentials\Basic;
use Nexmo\Verify\Verification;
class VonageVerifyUtil
{
/** @var VonageClient */
protected $client;
public function __construct()
{
$this->client = new VonageClient(
new Basic(
$_ENV['VONAGE_API_KEY'],
$_ENV['VONAGE_API_SECRET']
)
);
}
}Ajoutez deux nouvelles méthodes à cette classe. L'objectif de ces deux méthodes est de :
convertir le numéro de téléphone dans un format internationalisé.
convertir le numéro de téléphone en un format de numéro nationalisé.
public function getInternationalizedNumber(User $user): ?string
{
$phoneNumberUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$phoneNumberObject = $phoneNumberUtil->parse(
$user->getPhoneNumber(),
$user->getCountryCode()
);
if (!$phoneNumberUtil->isValidNumberForRegion(
$phoneNumberObject,
$user->getCountryCode())
) {
return null;
}
return $phoneNumberUtil->format(
$phoneNumberObject,
\libphonenumber\PhoneNumberFormat::INTERNATIONAL
);
}
public function getNationalizedNumber(string $phoneNumber)
{
$phoneNumberUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$phoneNumberObject = $phoneNumberUtil->parse($phoneNumber);
return '0' . $phoneNumberObject->getNationalNumber();
}La fonction suivante dont le système a besoin est de faire une demande de vérification pour les nouveaux enregistrements. Ajoutez la méthode ci-dessous pour traiter la création d'une demande de vérification :
public function sendVerification(User $user)
{
$internationalizedNumber = $this->getInternationalizedNumber($user);
if (!$internationalizedNumber) {
return null;
}
$verification = new Verification(
$internationalizedNumber,
$_ENV['VONAGE_BRAND_NAME'],
['workflow_id' => 2]
);
return $this->client->verify()->start($verification);
}Remarque si vous êtes intéressé par un flux de travail différent pour le processus de vérification, veuillez consulter la documentation de documentation de Vonage.
L'étape suivante de la vérification consiste à prendre le code de vérification saisi par l'utilisateur et à envoyer une demande de vérification avec le code de vérification :
public function verify(string $requestId, string $verificationCode)
{
$verification = new Verification($requestId);
return $this->client->verify()->check($verification, $verificationCode);
}
La dernière fonction n'est pas utilisée dans cette classe utilitaire mais le sera à plusieurs endroits du projet. Cette fonction extrait le requestId d'une réponse API. Ajoutez ce qui suit :
public function getRequestId(Verification $verification): ?string
{
$responseData = $verification->getResponseData();
if (empty($responseData)) {
return null;
}
return $responseData['request_id'];
}
Créer un nouvel utilisateur
Pour créer un nouveau Register avec la bibliothèque make de Symfony, exécutez la commande ci-dessous :
Où il est dit : Choose a name for your controller class (e.g. AgreeableGnomeController): tapez RegisterController et appuyez sur la touche Entrée.
Vous verrez la sortie dans votre Terminal pour deux nouveaux fichiers générés :
created: src/Controller/RegisterController.php
created: templates/registration/index.html.twigLes VonageVerifyUtil doit être injecté en tant que service dans l'espace nouvellement créé. RegisterController donc ouvrir le fichier src/Controller/RegisterController.php et l'injecter dans la fonction __construct() fonction.
+ use App\Util\VonageVerifyUtil;
class RegisterController extends AbstractController
{
+ /** @var VonageVerifyUtil */
+ protected $vonageVerifyUtil;
+ public function __construct(VonageVerifyUtil $vonageVerifyUtil)
+ {
+ $this->vonageVerifyUtil = $vonageVerifyUtil;
+ }
Dans votre navigateur, ouvrez la page register avec l'URL http://localhost:8081/register. Vous êtes accueilli par :

Il est temps de créer la page d'inscription !
La bibliothèque make s'avère être très utile, jusqu'à présent, elle a permis de créer un Entity, a Controlleret maintenant elle va créer un Form. Pour créer le formulaire UserType lancez la commande suivante :
docker-compose exec php bin/console make:formLorsqu'il vous demande un nom, entrez UserTypeet lorsqu'il demande un Entity type : User.
Vous trouverez le nouveau fichier UserType dans project/src/Form/UserType.php. Ouvrez-le, localisez la fonction buildForm() et remplacez son contenu par :
$builder
->add('phoneNumber', TelType::class, [
'attr' => [
'class' => 'form-control form-control-lg'
]
])
->add('name', TextType::class, [
'constraints' => [
new NotBlank(),
new Length(['min' => 3]),
],
'attr' => [
'class' => 'form-control form-control-lg'
],
])
->add('town', TextType::class, [
'constraints' => [
new NotBlank(),
new Length(['min' => 3]),
],
'attr' => [
'class' => 'form-control form-control-lg'
],
])
->add('county', TextType::class, [
'constraints' => [
new NotBlank(),
new Length(['min' => 3]),
],
'attr' => [
'class' => 'form-control form-control-lg'
],
])
->add('countryCode', ChoiceType::class, [
'label' => false,
'attr' => [
'class' => 'form-control form-control-lg'
],
'choices' => [
"United Kingdom" => "GB",
"United States" => "US"
]
])
->add('submit', SubmitType::class, [
'label' => 'Sign up',
'attr' => [
'class' => 'btn btn-info btn-lg btn-block'
],
])
;
Au sommet de la classe, plusieurs nouvelles classes devront être incluses, ChoiceType, SubmitType, Length, NotBlank, TelType, TextType. Ajoutez-les au début du fichier, comme indiqué ci-dessous :
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+use Symfony\Component\Form\Extension\Core\Type\TelType;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Validator\Constraints\Length;
+use Symfony\Component\Validator\Constraints\NotBlank; Créer un modèle d'enregistrement
Le modèle de base doit inclure Bootstrap pour que nos classes CSS Bootstrap soient reconnues et affichées.
Ouvrez project/templates/base.html.twig, le modèle de base qui sera repris par tous les autres modèles du projet, et remplacez le contenu par :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}
{{ encore_entry_link_tags('app') }}
{% endblock %}
</head>
<body>
<nav class="navbar navbar-dark bg-dark navbar-expand-lg">
<a class="navbar-brand" href="#">Welcome</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</nav>
<div class="container-fluid h-100">
{% block body %}{% endblock %}
</div>
{% block javascripts %}
{{ encore_entry_script_tags('app') }}
{% endblock %}
</body>
</html>
Maintenant que le modèle contient Bootstrap, il est temps de mettre à jour le modèle index.html.twig pour afficher notre formulaire et toute autre information. Ouvrez project/templates/register/index.html.twig et remplacez tout ce qui se trouve entre {% block body %} et {% endblock %} par :
<div class="row justify-content-center align-items-center h-100">
<div class="col col-sm-6 col-md-6 col-lg-4 col-xl-3">
{{ form_start(form) }}
<h1 class="h3 mb-3 font-weight-normal">Sign up</h1>
{{ form_end(form) }}
</div>
</div> Processus d'enregistrement complet
Vous avez maintenant créé un modèle et une classe de formulaire, mais le projet n'utilise actuellement aucun de ces fichiers. Vous devez les inclure dans votre méthode RegisterController appelée index(). Retournez à project/src/Controller/RegisterController.php.
Mettre à jour le contenu de la méthode index() comme indiqué ci-dessous :
/**
* @Route("/register", name="register")
*/
-public function index()
+public function index(Request $request)
{
+ $user = new User();
+
+ $form = $this->createForm(
+ UserType::class,
+ $user
+ );
+ $form->handleRequest($request);
+
+ if ($form->isSubmitted() && $form->isValid()) {
+ $entityManager = $this->getDoctrine()->getManager();
+ $entityManager->persist($user);
+ $entityManager->flush();
+
+ $verification = $this->vonageVerifyUtil->sendVerification($user);
+ $requestId = $this->vonageVerifyUtil->getRequestId($verification);
+
+ if ($requestId) {
+ $user->setVerificationRequestId($requestId);
+ $entityManager->flush();
+ }
+ }
+
return $this->render('register/index.html.twig', [
+ 'controller_name' => 'RegisterController',
+ 'form' => $form->createView(),
]);
}
Les modifications apportées dans l'exemple ci-dessus sont les suivantes :
Crée un nouvel objet
Uservide,Crée une nouvelle instance de la classe
UserTypeen utilisant le nouvel objet User comme classe de données pour structurer le formulaire,Traite la demande de formulaire pour déterminer si le formulaire :
a été soumise,
le contenu du formulaire est valide (par exemple, si
phoneNumberest vide, n'est pas unique)
Si le formulaire est soumis et valide, la fonction :
persiste le nouvel utilisateur dans la base de données
rince ces changements
Envoie une nouvelle demande à l
VerifyAPI,enregistre l'identifiant de la demande de vérification dans l'enregistrement de l'utilisateur dans la base de données
Si le formulaire n'a pas été soumis ou n'est pas valide, la fonction rend le modèle à l'utilisateur, avec le formulaire inclus dans le modèle.
Plusieurs classes doivent être importées dans la classe actuelle, telles que User et UserTypeParmi ces classes, il y a celles qui se trouvent en haut de la liste, comme indiqué ci-dessous :
+use App\Entity\User;
+use App\Form\UserType;
use App\Util\VonageVerifyUtil;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;En résumé, vous venez de créer un nouveau RegisterController, a User une entité et UserType formulaire. Vous avez mis à jour votre modèle pour la méthode index() à l'intérieur de la méthode RegisterController pour afficher les champs du formulaire que vous attendez de l'utilisateur.
Vous pouvez voir ces changements sur http://localhost:8081/registerou comme indiqué dans l'exemple ci-dessous :

Créer un formulaire de Verify
Créez le formulaire VerifyType avec la commande suivante :
pour la saisie du nom : VerifyType Et pour l'entité à utiliser, choisissez User
Lors de la soumission, vous verrez apparaître un message tel que celui illustré dans l'image ci-dessous :

En créant le VerifyType En créant le formulaire de cette manière, le système a automatiquement inséré toutes les propriétés de l'entité dans ce formulaire. User dans ce formulaire. Aucun de ces champs n'étant obligatoire, il convient de mettre à jour le code avec ce qui suit :
$builder
- ->add('phoneNumber')
- ->add('name')
- ->add('town')
- ->add('county')
- ->add('countryCode')
- ->add('verificationRequestId')
- ->add('verified')
- ->add('active')
+->add('verificationCode', TextType::class, [
+ 'mapped' => false,
+ 'attr' => [
+ 'class' => 'form-control form-control-lg'
+ ],
+ 'constraints' => [
+ new NotBlank([
+ 'message' => 'Please enter a verification code',
+ ]),
+ new Length([
+ 'min' => 4,
+ 'max' => 4,
+ 'minMessage' => 'The verification code is a 4 digit number.',
+ ]),
+ ],
+])
+->add('submit', SubmitType::class, [
+ 'label' => 'Verify',
+ 'attr' => [
+ 'class' => 'btn btn-info btn-lg btn-block'
+ ],
+])
;
Le code ci-dessus définit le formulaire prévu pour l'envoi d'un code de vérification. Ce formulaire contient deux champs, les champs verificationCode et submit. Chaque champ possède ses propres attributs à des fins de validation et d'affichage sur le formulaire ou de soumission du formulaire.
Au début de la classe, certaines importations de classe doivent être importées. Copiez donc le code en haut de la classe, comme indiqué ci-dessous :
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\Validator\Constraints\Length;
+use Symfony\Component\Validator\Constraints\NotBlank; Vérification de la poignée
Vous avez donc créé votre page d'inscription, mais quelle est la prochaine étape ? Vous devez créer une page permettant à l'utilisateur de saisir le code de vérification qu'il reçoit de l Verify API. À l'intérieur de la page RegisterController créez une nouvelle méthode sous la méthode index() appelée verify(). L'exemple ci-dessous illustre cette méthode et son contenu :
/**
* @Route("/register/verify/{user}", name="app_register_verify")
* @ParamConverter("user", class="App:User")
*/
public function verify(Request $request, User $user): Response
{
$form = $this->createForm(VerifyType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$verify = $this->vonageVerifyUtil->verify(
$user->getVerificationRequestId(),
$form->get('verificationCode')->getData()
);
if ($verify instanceof Verification) {
$user->setVerificationRequestId(null);
$user->setVerified(true);
$user->setActive(true);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->flush();
}
}
return $this->render('register/verify.html.twig', [
'form' => $form->createView(),
]);
}
Au début de la classe, incluez les éléments suivants Response, VerifyType, ParamConverter, et Verification comme indiqué ci-dessous :
use App\Entity\User;
use App\Form\UserType;
use App\Util\VonageVerifyUtil;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
+use Nexmo\Verify\Verification;
+use Symfony\Component\HttpFoundation\Response;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
+use App\Form\VerifyType;Le modèle pour afficher ce formulaire est maintenant nécessaire. Créez un nouveau fichier dans project/templates/register/ appelé verify.html et copiez le contenu du bloc de code ci-dessous dans ce fichier :
{% extends 'base.html.twig' %}
{% block title %}Verify{% endblock %}
{% block body %}
<div class="row justify-content-center align-items-center h-100">
<div class="col col-sm-6 col-md-6 col-lg-4 col-xl-3">
<h1 class="h3 mb-3 font-weight-normal">Verify</h1>
{{ form_start(form) }}
<div class="form-group">
{{ form_row(form.verificationCode) }}
</div>
{{ form_end(form) }}
</div>
<div>
{% endblock %}
Gestion d'une vérification réussie
Dans le projet RegisterController le projet a maintenant besoin d'une page de réussite pour indiquer que l'utilisateur s'est vérifié avec succès. La création d'une nouvelle méthode registerSucess() qui affichera un modèle avec le message indiquant que l'enregistrement a réussi :
/**
* @Route("/register/success", name="app_register_success")
*/
public function registerSuccess(): Response
{
return $this->render('register/success.html.twig');
}
Créez maintenant un nouveau fichier dans project/templates/register/ appelé success.html.twig et produisez ce qui suit :
{% extends 'base.html.twig' %}
{% block title %}Registration Successful!{% endblock %}
{% block body %}
<div class="row justify-content-center align-items-center h-100">
<div class="col col-sm-6 col-md-6 col-lg-4 col-xl-3">
<h1 class="h3 mb-3 font-weight-normal">Sign up Successful!</h1>
<p>Thank you for verifying your phone number.</p>
<p>Your details have been added to the database to participate in the Befriending Service.</p>
<p>You will be automatically contacted at 2pm every day and connected with another user in the system.</p>
</div>
</div>
{% endblock %}Quelques petites modifications sont nécessaires pour finaliser la partie vérification. Des redirections sont nécessaires lorsque chaque soumission de formulaire réussit à faire passer l'utilisateur à la page suivante. Ainsi, dans RegisterController, sous public function index()trouvez et ajoutez ce qui suit :
if ($requestId) {
$user->setVerificationRequestId($requestId);
$entityManager->flush();
+ return $this->redirectToRoute('app_register_verify', ['user' => $user->getId()]);
}
Et à l'intérieur de la méthode public function verify(Request $request, User $user)trouvez et ajoutez ce qui suit :
if ($verify instanceof Verification) {
$user->setVerificationRequestId(null);
$user->setVerified(true);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->flush();
+ return $this->redirectToRoute('app_register_success');
}
Latitude et longitude
Afin de trouver des utilisateurs enregistrés géographiquement proches d'autres utilisateurs enregistrés, le système doit disposer de la latitude et de la longitude de chaque utilisateur, au moment de l'enregistrement. Le projet utilisera un service appelé MapQuestqui prend la ville et le comté/état de l'utilisateur et les convertit en latitude et longitude requises.
Créez un Account sur : MapQuest.
Ouvrez .env.local et ajoutez les nouvelles entrées suivantes (en remplaçant <api_key> par votre clé API MapQuest) :
MAP_QUEST_API_KEY=<api_key>
MAP_QUEST_API_URL=https://www.mapquestapi.com/geocoding/v1/L'entité User a besoin de deux propriétés pour stocker la latitude et la longitude. Pour mettre à jour cette entité, lancez la commande Symfony make de Symfony, comme indiqué ci-dessous, et suivez les instructions :
Nom de classe de l'entité à créer ou à mettre à jour (par exemple DeliciousGnome) :
User
Bien 1
Nom :
latitudeType :
decimalPrécision : 20
Échelle : 16
Peut être nul ?
yes
Bien 2
Nom :
longtitudeType :
decimalPrécision : 20
Échelle : 16
Peut être nul ?
yes
L'exécution de la commande ci-dessous génère un nouveau fichier de migration contenant les modifications apportées à la base de données :
Si vous souhaitez voir les changements à venir dans la base de données, les fichiers de migration générés sont enregistrés dans le fichier project/src/Migrations/.
Si vous êtes satisfait de ces modifications, pour les conserver dans la base de données, exécutez la commande ci-dessous :
Guzzle est un client HTTP PHP qui permet aux projets de faire des requêtes à d'autres services web, telles que des requêtes POST et GET à des API. Guzzle facilite l'envoi d'une requête GET à l'API de Map Quest pour recevoir la latitude et la longitude de l'emplacement de l'utilisateur.
L'étape suivante consiste à créer un fichier MapQuestUtil pour effectuer cette requête GET et gérer la réponse afin de ne récupérer que les champs obligatoires. Créez un nouveau fichier dans project/src/Util/ appelé MapQuestUtil.php et copiez le code suivant dans le fichier nouvellement créé :
<?php
namespace App\Util;
use App\Entity\User;
use App\Util\MapQuestUtil;
use GuzzleHttp\Client as GuzzleClient;
class MapQuestUtil
{
public function getLatLongByAddress(User $user): ?array
{
$client = new GuzzleClient(
['base_uri' => $_ENV['MAP_QUEST_API_URL']]
);
$response = $client->request(
'GET',
'address', [
'query' => [
'key' => $_ENV['MAP_QUEST_API_KEY'],
'inFormat' => 'kvp',
'outFormat' => 'json',
'location' => $user->getTown() . ',' . $user->getCounty(),
'thumbMaps' => 'false'
]
]
);
if ($response->getStatusCode() !== 200) {
return null;
}
$body = json_decode($response->getBody()->getContents(), true);
if (!is_array($body) || empty($body)) {
return null;
}
if (!array_key_exists('results', $body) || empty($body['results'])) {
return null;
}
return $body['results'][0]['locations'][0]['latLng'];
}
}Le code ci-dessus est une nouvelle méthode qui prend un objet User et fait une demande à l'API de MapQuest en fournissant à l'utilisateur location qui est leur Town et leur County. Plusieurs étapes suivent cette demande pour s'assurer qu'un lieu est renvoyé et que les champs prévus pour la latitude et la longitude sont inclus dans la réponse.
Il est temps d'appeler cette MapQuestUtil classe et getLatLongByAddress() méthode. Ouvrez le RegisterController.
La nouvelle classe Utility doit être injectée en tant que service. Dans la méthode __construct() la mettre à jour comme indiqué ci-dessous :
use App\Form\VerifyType;
+use App\Util\MapQuestUtil;
class RegisterController extends AbstractController
{
/** @var VonageVerifyUtil */
protected $vonageVerifyUtil;
+ /** @var MapQuestUtil */
+ protected $mapQuestUtil;
- public function __construct(VonageVerifyUtil $vonageVerifyUtil)
- {
+ public function __construct(
+ VonageVerifyUtil $vonageVerifyUtil,
+ MapQuestUtil $mapQuestUtil
+ ) {
$this->vonageVerifyUtil = $vonageVerifyUtil;
+ $this->mapQuestUtil = $mapQuestUtil;
}
Maintenant que la fonction MapQuestUtil a été injecté, vous devez appeler la fonction et stocker les détails renvoyés dans le fichier User. Dans la fonction index() trouvez la ligne if ($form->isSubmitted() && $form->isValid()) { et ajoutez une nouvelle ligne en dessous. Ajoutez ce qui suit :
if ($form->isSubmitted() && $form->isValid()) {
+ $latLng = $this->mapQuestUtil->getLatLongByAddress($user);
+
+ if (null !== $latLng) {
+ $user
+ ->setLatitude($latLng['lat'])
+ ->setLongitude($latLng['lng']);
+ }
A ce stade du tutoriel, vous avez créé un nouveau projet Symfony, configuré ce projet avec votre base de données. Vous avez également créé une nouvelle entité User qui correspond à la table de votre base de données user avec la table de votre base de données. Vous avez créé deux nouveaux formulaires, un UserType et VerifyTypequi sont utilisés pour valider vos nouveaux utilisateurs et vérifier si le code de vérification des entrées est valide. Vous avez également créé plusieurs pages auxquelles l'utilisateur peut accéder, a /register, /verify, et /success.
Vous êtes maintenant à un point d'arrêt naturel, à la moitié de ce tutoriel ! Vous pouvez maintenant tester le processus d'enregistrement de votre projet.
Accédez à la la page d'inscription et suivez les instructions des deux pages suivantes. Veuillez saisir un numéro de téléphone valide. Sinon, vous ne pourrez pas vérifier votre Account.
Rencontres d'amitié
Utilisateurs de l'application
Créer une entité de correspondance
Une nouvelle entité est nécessaire pour mettre en relation deux utilisateurs, recueillir leurs réactions et stocker les enregistrements des utilisateurs mis en relation. Cette nouvelle entité stockera une relation avec les deux appelants dans l'objet apparié et le nom de la conférence téléphonique (les noms des deux utilisateurs concaténés par "-"). Pour ce faire, utilisez la bibliothèque make comme indiqué ci-dessous, et suivez ses instructions pour les trois nouvelles propriétés :
Nom : Match
1 - callerOne - ManyToOne - User - not nullable - rest defaults
2 - callerTwo - ManyToOne - User - not nullable - rest defaults
3 - conferenceName - string - 255 - non nullable
Avant de procéder à la migration, une autre est nécessaire. Il existe un paquet d'extensions pour Doctrine qui permet au code d'utiliser les extensions de Doctrine telles que TimestampablePar exemple, pour utiliser deux lignes dans nos entités afin d'avoir createdAt et updatedAt dans nos entités. Pour ce faire, il faut installer stof/doctrine-extensions-bundle:
La configuration est nécessaire pour ce nouveau paquet d'extensions. Ouvrir stof_doctrine_extensions.yaml trouvé dans : project/config/packages/ et mettez-le à jour en miroir :
stof_doctrine_extensions:
default_locale: en_GB
orm:
default:
timestampable: falseVous devez apporter d'autres modifications à l'entité nouvellement créée. Match nouvellement créée. Ouvrez cette classe, que vous trouverez dans project/src/Entity/Match.php.
Tout d'abord, incluez le fichier TimestampableEntity dans le projet en ajoutant l'importation suivante :
use Doctrine\ORM\Mapping as ORM;
+use Gedmo\Timestampable\Traits\TimestampableEntity;Utilisez maintenant le TimestampableTrait dans la classe Match comme indiqué ci-dessous :
class Match
{
+ use TimestampableEntity;L'exécution de la commande ci-dessous génère un nouveau fichier de migration contenant les modifications apportées à la base de données :
Si vous souhaitez voir les changements à venir dans la base de données, les fichiers de migration générés sont enregistrés dans le fichier project/src/Migrations/.
Si vous êtes satisfait de ces modifications, pour les conserver dans la base de données, exécutez la commande ci-dessous :
Configurer Ngrok
Ngrok est une application multiplateforme qui permet aux utilisateurs d'exposer leur serveur de développement à l'Internet sans devoir configurer leur routeur. Pour ce faire, Ngrok crée un tunnel entre votre serveur et le sous-domaine de Ngrok.
Il est temps de configurer Ngrok pour permettre à l'Application Vonage (qui est créée dans l'étape suivante), d'avoir un sous-domaine Ngrok pour faire ses demandes de webhook.
Dans le répertoire docker/ngrok/ renommez le fichier ngrok.conf.local en ngrok.confet remplacez <auth_token> par votre jeton d'authentification Ngrok :
Remarque Vous trouverez votre auth token dans votre tableau de bord sur Ngrok
authtoken: <auth_token>Maintenant, pour mettre à jour vos conteneurs Docker avec l'URL correcte du sous-domaine Ngrok, Docker doit être redémarré. Vous pouvez redémarrer Docker avec la commande suivante :
Une fois que vous avez redémarré Docker, allez sur https://0.0.0.0:4040et copiez le sous-domaine Ngrok indiqué sur la page d'état.

Si vous prenez maintenant ce sous-domaine Ngrok et y accédez : ngroksubdomain/registeren remplaçant ngroksubdomain par votre sous-domaine Ngrok, vous verrez votre page d'enregistrement. Pour moi, l'exemple était : https://a551f0297ed8.ngrok.io/register.
Prenez note de ce sous-domaine Ngrok car il sera nécessaire ensuite pour configurer votre application Vonage.
Créer une application Vonage
Connectez-vous à votre tableau de bord Vonage. Sur la gauche de la page se trouve un lien Your Applicationscliquez dessus. Une fois chargé, cliquez sur le bouton Create a new Application bouton.
Définir le Application NamePour ce tutoriel, j'ai défini le nom comme suit Befriending Servicemais il peut être ce que vous voulez.
Cliquez sur le bouton pour Generate public and private key. Votre navigateur propose de télécharger le fichier private.key fichier. Enregistrez-le dans votre project/ répertoire.
Sous Capabilities, basculer sur Voice.
Mettez à jour les trois champs de saisie que vous voyez maintenant avec les URL des webhooks nécessaires pour que Vonage effectue des rappels pendant les appels téléphoniques. Par exemple, pour la zone de texte des événements : https://a551f0297ed8.ngrok.io/webhooks/eventsn'oubliez pas de remplacer a551f0297ed8 par le sous-domaine que vous avez créé à l'étape précédente. Veillez à procéder de la même manière pour les trois champs de saisie :
Événement -
https://<ngrok url>/webhooks/eventRéponse -
https://<ngrok url>/webhooks/answerRepli -
https://<ngrok url>/webhooks/fallback
Enfin, cliquez sur Generate new application.
La page qui se charge maintenant affiche votre Application ID. Notez-le bien !
Dans votre project/ ouvrez .env.local et ajoutez les deux lignes suivantes, en veillant à remplacer <application id> par l'identifiant de votre application, et remplacez <ngrok_url> par l'URL de votre Ngrok :
VONAGE_APPLICATION_ID=<application id>
VONAGE_APPLICATION_PRIVATE_KEY_PATH=/var/www/symfony/private.key
VONAGE_NUMBER=<your virtual Vonage number>
NGROK_URL=<ngrok_url>Vous avez maintenant créé une Application Vonage et stocké les informations d'identification dans votre projet. Le stockage de ces informations d'identification permet au projet de passer et de recevoir des appels téléphoniques.
Vonage Call Util
Créer un nouveau fichier dans project/src/Util/ appelé VonageCallUtil.php.
<?php
namespace App\Util;
use App\Util\VonageVerifyUtil;
use Nexmo\Client as VonageClient;
use Nexmo\Client\Credentials\Keypair;
class VonageCallUtil
{
/** @var VonageClient */
protected $client;
/** @var VonageVerifyUtil */
protected $vonageVerifyUtil;
public function __construct(VonageVerifyUtil $vonageVerifyUtil)
{
$keypair = new Keypair(
file_get_contents($_ENV['VONAGE_APPLICATION_PRIVATE_KEY_PATH']),
$_ENV['VONAGE_APPLICATION_ID']
);
$this->client = new VonageClient($keypair);
$this->vonageVerifyUtil = $vonageVerifyUtil;
}
}Cette classe utilitaire ne fait actuellement rien d'autre que d'injecter deux nouveaux services lors de sa création. Ces services sont les suivants VonageClientqui est la partie qui utilise l'API Voice Voice de Vonage pour passer et recevoir des appels. L'autre service injecté est le service VonageVerifyUtilqui est appelé lorsque le projet a besoin de méthodes spécifiques à l'intérieur de cette classe Util.
La classe Utility est utilisée pour initier l'appel entre deux utilisateurs. La première méthode nécessaire sera donc createConferenceBetween()
public function createConferenceBetween(User $callerOne, User $callerTwo): Match
{
$conferenceName = $callerOne->getName() . '_' . $callerTwo->getName();
// Save conference name to the database, along with the callerOne and callerTwo.
$match = (new Match())
->setCallerOne($callerOne)
->setCallerTwo($callerTwo)
->setConferenceName($conferenceName)
->setCreatedAt((new \DateTime()))
->setUpdatedAt((new \DateTime()));
$ncco = [
[
'action' => 'talk',
'voiceName' => 'Amy',
'text' => 'You are on a Befriending service call, I will connect you to someone random from my database that shouldn\'t be far from your town. Please enjoy your call. If you would like to join now, enter 1 for yes, or 2 for no.'
],
[
'action' => 'input',
'maxDigits' => 1,
'eventUrl' => [
$_ENV['NGROK_URL'] . '/webhooks/joinConference'
],
'timeOut' => 10
]
];
$this->makeCall($callerOne, $ncco);
$this->makeCall($callerTwo, $ncco);
return $match;
}Le code ci-dessus est une méthode qui prend deux utilisateurs comme arguments de cette méthode. La méthode génère ensuite un nouvel objet de l'entité Match comme connexion entre les deux utilisateurs. Un tableau Nexmo Call Control Object (NCCO) est créé, qui est un ensemble d'instructions permettant à Vonage Voice de savoir à quoi s'attendre et quelles sont les étapes prévues pour passer l'appel.
Ce tableau contient deux autres éléments. Le premier consiste à utiliser le persona Amy pour présenter l'appel à l'utilisateur et lui expliquer en quoi consiste l'appel téléphonique. Amy Le deuxième élément demande à l'utilisateur s'il souhaite ou non participer à une conférence téléphonique, en lui demandant d'entrer 1 pour oui ou 2 pour non. Le deuxième tableau permet de diriger l'utilisateur vers le point de terminaison du webhook suivant, qui permet de rejoindre la conférence.
Vous avez peut-être remarqué qu'il y a deux appels à une méthode makeCall() que vous n'avez pas encore créée. Créez cette nouvelle méthode en copiant le code suivant dans votre fichier VonageCallUtil:
public function makeCall(User $caller, array $ncco)
{
try {
$number = $this->vonageVerifyUtil->getInternationalizedNumber($caller);
$call = $this->client->calls()->create([
'to' => [[
'type' => 'phone',
'number' => preg_replace('/\s+/', '', $number)
]],
'from' => [
'type' => 'phone',
'number' => $_ENV['VONAGE_NUMBER']
],
'ncco' => $ncco
]);
} catch (\Exception $e) {
}
}
Cette méthode appelle d'abord une autre méthode, getInternationalizedNumber()qui se trouve à l'intérieur de l'élément vonageVerifyUtil. Elle prend l'instance $caller qui est une instance de User et convertit l'indicatif de pays et le numéro de téléphone en un numéro de Internationalized numéro que Vonage appellera.
L'étape suivante de la méthode ci-dessus consiste à initialiser l'appel en définissant a to, from et en insérant l'objet ncco et en insérant l'objet
Enfin, dans ce VonageCallUtil plusieurs classes doivent être incluses car elles sont utilisées dans les exemples ci-dessus. Copiez les ajouts indiqués dans l'exemple ci-dessous dans votre fichier :
namespace App\Util;
+use App\Entity\Match;
+use App\Entity\User;
use App\Util\VonageVerifyUtil;
use Nexmo\Client as VonageClient;
use Nexmo\Client\Credentials\Keypair;En résumé, la fonctionnalité ci-dessus n'est pas encore appelée, mais elle crée un nouvel objet Match, y enregistre deux utilisateurs, puis passe deux appels pour chaque utilisateur. Il n'y a pas encore de conférence téléphonique ni de moyen de relier les deux appels entre eux. La conférence téléphonique a lieu ensuite !
Correspondance entre les utilisateurs
Parce que le projet doit avoir des appels quotidiens, une commande est nécessaire, qui sera exécutée comme un cronjob. Dans votre Terminal, dans le répertoire docker/ utilisez la bibliothèque make pour créer une nouvelle commande :
Lorsque la bibliothèque demande un nom pour votre nouvelle commande, entrez app:match-users.
L'exécution de cette commande créera un nouveau fichier appelé MatchUsersCommand.php à l'intérieur : project/src/Command/MatchUsersCommand.php.
Ouvrez le fichier nouvellement créé MatchUsersCommand.php nouvellement créé.
La commande que vous avez générée comporte des paramètres par défaut, dont la plupart ne seront pas nécessaires pour ce projet. Trouvez protected function configure() et remplacez le contenu de cette fonction par :
$this
->setDescription('Match users together for a phone call')
;
Deux services doivent être injectés dans la classe via la méthode __construct() dans la classe. Les deux services requis sont EntityManagerInterfacequi permet au code d'effectuer des requêtes dans la base de données ou d'accéder aux méthodes du référentiel pour vos entités. Le second service nécessaire est le service VonageCallUtil pour appeler notre fonctionnalité créée précédemment qui appelle deux utilisateurs et les connecte.
Mettez à jour le code suivant dans le fichier MatchUsersCommand.php pour qu'il corresponde à ce qui est indiqué ci-dessous :
+use App\Entity\Match;
+use App\Entity\User;
+use App\Util\VonageCallUtil;
+use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class MatchUsersCommand extends Command
{
protected static $defaultName = 'app:match-users';
+ /** @var VonageCallUtil */
+ protected $vonageCallUtil;
+
+ /** @var EntityManagerInterface */
+ protected $entityManager;
+
+ public function __construct(
+ VonageCallUtil $vonageCallUtil,
+ EntityManagerInterface $entityManager
+ ) {
+ $this->vonageCallUtil = $vonageCallUtil;
+ $this->entityManager = $entityManager;
+
+ parent::__construct();
+ }
Il est maintenant temps de commencer à construire la fonctionnalité de commande !
Pour que la requête fonctionne, le projet a besoin de certaines extensions Doctrine que vous allez écrire dans l'étape suivante. Ces extensions proviennent d'une bibliothèque tierce de Benjamin Eberlei. Pour installer cette bibliothèque, exécutez la commande suivante dans votre terminal :
Il s'agit maintenant d'activer les extensions nécessaires. Ouvrez project/config/packages/doctrine.yamlet avec la même indentation de auto_generate_proxy_classes, naming_strategy, auto_mappinget mappings ajouter :
Note : Veuillez prêter attention à la façon dont le texte est indenté : Veuillez prêter attention à la façon dont il est indenté pour vous assurer qu'il s'agit d'un format YAML correct.
dql:
numeric_functions:
acos: DoctrineExtensions\Query\Mysql\Acos
cos: DoctrineExtensions\Query\Mysql\Cos
radians: DoctrineExtensions\Query\Mysql\Radians
sin: DoctrineExtensions\Query\Mysql\SinDe retour à l'intérieur de votre MatchUsersCommand trouvez la fonction execute() (fonction). Cette fonction est l'endroit où tout le travail se fait pour la commande.
Pour commencer, supprimez tout le contenu de cette méthode.
La première chose à faire dans cette méthode est d'avoir une instance de la classe Match et User dans les variables, et d'obtenir tous les utilisateurs qui sont marqués comme actifs :
$matchRepository = $this->entityManager->getRepository(Match::class);
$userRepository = $this->entityManager->getRepository(User::class);
$activeUsers = $userRepository->findByActive(true);
Ensuite, il est temps de faire une boucle sur chaque utilisateur actif, pour initier des appels et les mettre en relation avec un autre utilisateur. Ajoutez la boucle suivante :
foreach ($activeUsers as $key => $callerOne) {
}
Le système doit maintenant trouver les utilisateurs géographiquement proches de l'endroit où se trouve le courant pour les connecter. $callerOne pour les mettre en relation. La requête est assez longue et s'inscrit dans le cadre de l'application UserRepository. Ouvrez le fichier project/src/Repository/UserRepository.php
Sous la méthode __construct ajouter la nouvelle méthode suivante :
public function findPossibleMatchesByDistance(User $user, array $activeUsers, int $distance)
{
$queryBuilder = $this->createQueryBuilder('u');
return $this->createQueryBuilder('u')
->addSelect(
'( 3959 * acos(cos(radians(:latitude))' .
'* cos(radians(u.latitude))' .
'* cos(radians(u.longitude)' .
'- radians(:longitude))' .
'+ sin(radians(:latitude))' .
'* sin(radians(u.latitude)))) as distance'
)
->andWhere($queryBuilder->expr()->eq('u.active', ':isActive'))
->andWhere($queryBuilder->expr()->eq('u.verified', ':isVerified'))
->andWhere($queryBuilder->expr()->neq('u', ':user'))
->having($queryBuilder->expr()->lt('distance', ':distance'))
->setParameters([
'latitude' => $user->getLatitude(),
'longitude' => $user->getLongitude(),
'user' => $user,
'distance' => $distance,
'isActive' => true,
'isVerified' => true
])
->getQuery()
->getResult();
}
La méthode décrite dans le bloc de code ci-dessus prend un objet utilisateur, un tableau des utilisateurs actifs actuels et une variable entière d'une distance prédéterminée.
Cette méthode crée une nouvelle requête qui calcule la distance entre tous les utilisateurs et l'utilisateur donné et s'assure qu'ils sont tous actifs et vérifiés. La dernière vérification consiste à s'assurer que les utilisateurs renvoyés se trouvent à la distance donnée de l'utilisateur passé.
De retour à l'intérieur de la MatchUsersCommand trouver la ligne : foreach ($activeUsers as $activeUser) { et ajoutez ce qui suit :
// Retrieve all active users within a 30 mile radius of current callerOne
$matches = $userRepository->findPossibleMatchesByDistance($callerOne, $activeUsers, 30);
// If there are less than 5 users returned, increase the search to 100 mile radius.
if (count($matches) < 5) {
$matches = $userRepository->findPossibleMatchesByDistance($callerOne, $activeUsers, 100);
}
// If there are no users within a 100 mile radius return 0
if (count($matches) === 0) {
unset($activeUsers[$key]);
continue;
}
// Shuffle returned matches.
shuffle($matches);
// Remove callerOne from list of active users
unset($activeUsers[$key]);
$callerTwo = $matches[0][0];
// Remove callerTwo from list of active users
$matchKey = array_search($callerTwo, $activeUsers);
unset($activeUsers[$matchKey]);
// Make a call to createConferenceBetween() inside VonageCallUtil to connect the two users via phone call.
$match = $this->vonageCallUtil->createConferenceBetween($callerOne, $callerTwo);
// If successful, save the Match to the database.
if ($match instanceof Match) {
$this->entityManager->persist($match);
$this->entityManager->flush();
}
La commande est créée pour trouver les utilisateurs à mettre en relation et lancer l'appel. Cependant, des URL de webhook sont nécessaires pour faire progresser l'appel depuis l'introduction automatisée jusqu'à la connexion des utilisateurs à une conférence téléphonique.
Crochets Web
Vous avez maintenant besoin d'un WebhooksControllerDans votre terminal, utilisez donc la bibliothèque make pour exécuter la commande suivante :
Où il est dit : Choose a name for your controller class (e.g. AgreeableGnomeController): tapez WebhooksController et appuyez sur la touche Entrée.
Vous avez créé deux nouveaux fichiers :
created: src/Controller/WebhooksController.php
created: templates/webhooks/index.html.twigOuvrez le fichier nouvellement créé WebhooksController.php nouvellement créée. project/src/Controller/
Ce contrôleur devra utiliser trois services, VonageVerifyUtil, VonageCallUtilet EntityManagerInterface. Commencez donc par les injecter dans la construction du contrôleur, comme indiqué ci-dessous :
+
+ use App\Util\VonageVerifyUtil;
+ use App\Util\VonageCallUtil;
+ use Doctrine\ORM\EntityManagerInterface;
class WebhooksController extends AbstractController
{
+ /** @var VonageVerifyUtil */
+ protected $vonageVerifyUtil;
+
+ /** @var VonageCallUtil */
+ protected $vonageCallUtil;
+
+ /** @var EntityManagerInterface */
+ protected $entityManager;
+
+ public function __construct(
+ VonageCallUtil $vonageCallUtil,
+ VonageVerifyUtil $vonageVerifyUtil,
+ EntityManagerInterface $entityManager
+ ) {
+ $this->vonageCallUtil = $vonageCallUtil;
+ $this->vonageVerifyUtil = $vonageVerifyUtil;
+ $this->entityManager = $entityManager;
+ }
}
Les deux méthodes ci-dessous font ce que leur nom indique. La première permet de trouver un utilisateur par son numéro de téléphone, tandis que la seconde trouve une Match par un User que vous avez créée aujourd'hui. Ajoutez-les à votre fichier WebhooksController.
private function findUserByNumber(string $phoneNumber)
{
return $this->entityManager->getRepository(User::class)->findOneByPhoneNumber(
$this->vonageVerifyUtil->getNationalizedNumber('+' . $phoneNumber)
);
}
private function findMatchByUser(User $user)
{
return $this->entityManager
->getRepository(Match::class)
->findByDateUser($user, (new \DateTime()));
}
Une méthode appelée dans l'exemple ci-dessus n'existe pas encore. Elle permet de trouver des correspondances en fonction de l'utilisateur et de la date du jour. Ouvrez project/src/Repository/MatchRepository.php et à l'intérieur de cette classe, ajoutez la méthode ci-dessous :
public function findByDateUser(User $user, DateTime $date)
{
$queryBuilder = $this->createQueryBuilder('m');
return $this->createQueryBuilder('m')
->andWhere(
$queryBuilder->expr()->orX(
$queryBuilder->expr()->eq('m.callerOne', ':user'),
$queryBuilder->expr()->eq('m.callerTwo', ':user')
)
)
->andWhere(
$queryBuilder->expr()->like('Date(m.createdAt)', ':date')
)
->setParameters([
'user' => $user,
'date' => $date->format('Y-m-d') . '%'
])
->getQuery()
->getResult();
}
Au début du fichier, ajoutez l'importation pour l'entité User:
use App\Entity\Match;
+use App\Entity\User;La requête ci-dessus permet de trouver une entrée Match où l'utilisateur est soit callerOne ou callerTwo et l'entrée Match a été créée aujourd'hui. Il y a une fonction de requête que Doctrine ne connaît pas ici. Date(m.createdAt). Lorsque vous calculez la distance entre des personnes, vous installez beberlei/doctrineextensions. Cette bibliothèque a la fonctionnalité d'inclure l'extension Date extension. Ouvrez donc project/config/packages/doctrine.yaml et ajoutez les deux lignes suivantes :
dql:
numeric_functions:
acos: DoctrineExtensions\Query\Mysql\Acos
cos: DoctrineExtensions\Query\Mysql\Cos
radians: DoctrineExtensions\Query\Mysql\Radians
sin: DoctrineExtensions\Query\Mysql\Sin
+ datetime_functions:
+ Date: DoctrineExtensions\Query\Mysql\DatePrécédemment, vous avez créé une méthode appelée createConferenceBetween()à l'intérieur de laquelle se trouvait un tableau NCCO qui fait référence à une URL de /webhooks/joinConferenceSi vous exécutez l'exemple maintenant, il ne se passera rien car l'URL n'existe pas. Créez une nouvelle méthode joinConference() à l'intérieur de votre méthode WebhooksController avec l'annotation contenant cette URL.
Cette méthode a deux objectifs. Le premier est de valider la saisie de l'utilisateur, en s'assurant qu'il n'entre que des options valides avant de continuer. Ces entrées sont 1 pour oui et 2 pour non.
Le deuxième objectif de cette méthode est de respecter l'entrée de l'utilisateur. S'il choisit de passer à l'appel, le système le redirige vers l'action suivante, qui le connecte à la conférence téléphonique. Si l'utilisateur sélectionne "2", pour ne pas rejoindre un appel avec quelqu'un, le système répond en confirmant sa demande de ne pas rejoindre un appel et met fin à l'appel.
/**
* @Route("/webhooks/joinConference", name="match_join")
*/
public function joinConference(Request $request)
{
$content = json_decode($request->getContent(), true);
if (!in_array($content['dtmf'], ['1', '2'])) {
$ncco = [
[
'action' => 'talk',
'text' => 'Please only enter 1 or 2. Would you like to join a call with someone?',
'voiceName' => 'Amy',
],
[
'action' => 'input',
'maxDigits' => 1,
'eventUrl' => [
$_ENV['NGROK_URL'] . '/webhooks/joinConference'
],
'timeOut' => 10
]
];
} elseif ($content['dtmf'] === '1') {
// Find user by number.
$user = $this->findUserByNumber($content['to']);
// Find match by user and today.
$match = $this->findMatchByUser($user)[0];
// Make next request.
$ncco = [
[
'action' => 'talk',
'text' => 'Thank you. I will now connect you.',
'voiceName' => 'Amy',
],
[
'action' => 'conversation',
'name' => $match->getConferenceName()
]
];
} else {
$ncco = [
[
'action' => 'talk',
'text' => 'Ok, we will not put you in a call with someone at this time. Goodbye.',
'voiceName' => 'Amy',
]
];
}
return new JsonResponse($ncco);
}
Quatre classes sont utilisées ici, que nous n'avons pas incluses dans notre fichier, Request, Match, User, et JsonResponse. Incluez-les au-dessus de la classe :
+use App\Entity\Match;
+use App\Entity\User;
use App\Util\VonageCallUtil;
use App\Util\VonageVerifyUtil;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Doctrine\ORM\EntityManagerInterface;À ce stade, vous disposez d'un formulaire d'enregistrement de l'utilisateur, qui le guide tout au long du processus de vérification. Vous disposez également d'une commande qui, lorsqu'elle est exécutée, passe en revue tous les utilisateurs actifs et vérifiés et les connecte à d'autres utilisateurs actifs et vérifiés dans un rayon maximum de 100 miles les uns des autres.
Vous pouvez maintenant le tester ! Si vous disposez de deux numéros de téléphone, enregistrez-vous sur votre page d'inscription. Veillez à suivre la procédure de vérification et assurez-vous que les lieux que vous avez indiqués pour les deux utilisateurs sont situés à moins de 30 miles l'un de l'autre.
Une fois les utilisateurs enregistrés, dans votre Terminal, dans le répertoire docker/ lancez la commande suivante :
Les deux numéros de téléphone recevront un appel et il leur sera demandé s'ils souhaitent se connecter à un appel avec une autre personne !
Obtenir un retour d'information
Une deuxième commande est nécessaire pour recueillir les commentaires de chaque utilisateur. Cette commande recueillera les commentaires de chaque utilisateur sur les appels de la journée en cours. Cette commande sera également une tâche cron programmée pour s'exécuter plusieurs heures après la fin de l'appel correspondant.
Mais avant d'effectuer cette nouvelle commande, l'entité Match doit être mise à jour, car lorsque vous avez créé l'entité Match avec la bibliothèque make vous pouvez également la mettre à jour avec de nouvelles propriétés. Exécutez la commande suivante dans votre terminal et suivez les instructions de chaque étape ci-dessous :
Nom de classe de l'entité à créer ou à mettre à jour (par exemple DeliciousGnome) :
Match
Bien 1
Nom :
callerOneFeedbackAcceptedType :
booleanPeut être nul ?
yes
Bien 2
Nom :
callerTwoFeedbackAcceptedType :
booleanPeut être nul ?
yes
Propriété 3
Nom :
callerOneCallSuccessfulType :
booleanPeut être nul ?
yes
Propriété 4
Nom :
callerTwoCallSuccessfulType :
booleanPeut être nul ?
yes
L'entité mise à jour ne reflète pas actuellement ce qui se trouve dans la base de données. Pour que la base de données reflète ce que vous avez défini dans l'entité Match exécutez la commande ci-dessous pour générer un nouveau fichier de migration :
Si vous souhaitez voir les changements à venir dans la base de données, les fichiers de migration générés sont enregistrés dans le fichier project/src/Migrations/.
Si vous êtes satisfait de ces modifications, pour les conserver dans la base de données, exécutez la commande ci-dessous :
Tout d'abord, vous devez obtenir une liste des matches du jour. Ainsi, dans votre MatchRepository ajoutez une nouvelle fonction qui interroge la base de données pour trouver toutes les correspondances de la journée en cours. Cette requête ne trouvera que les utilisateurs pour lesquels l'indicateur callerOneCallSuccessful est vide :
public function getTodaysMatches()
{
$queryBuilder = $this->createQueryBuilder('m');
return $this->createQueryBuilder('m')
->andWhere(
$queryBuilder->expr()->isNull('m.callerOneCallSuccessful'),
$queryBuilder->expr()->like('Date(m.createdAt)', ':date')
)
->setParameter('date', (new \DateTime())->format('Y-m-d') . '%')
->getQuery()
->getResult();
}
Il est maintenant temps de créer la nouvelle commande qui utilise les nouveaux changements, dans votre terminal :
Lorsqu'il demande un nom de commande, entrez : app:get-feedback. Cette commande générera un nouveau fichier de commande à l'intérieur de project/src/command/ appelé GetFeedbackCommand.php Ouvrez le fichier nouvellement créé.
Ce contrôleur a besoin de deux services qui lui sont injectés par le biais de la méthode __construct() . Les deux mêmes services sont injectés dans le contrôleur MatchUsersCommand. Copiez les ajustements montrés ci-dessous dans votre GetFeedbackCommand:
+use App\Entity\Match;
+use App\Util\VonageCallUtil;
+use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class GetFeedbackCommand extends Command
{
protected static $defaultName = 'app:get-feedback';
+ /** @var VonageCallUtil */
+ protected $vonageCallUtil;
+
+ /** @var EntityManagerInterface */
+ protected $entityManager;
+
+ public function __construct(
+ VonageCallUtil $vonageCallUtil,
+ EntityManagerInterface $entityManager
+ ) {
+ $this->vonageCallUtil = $vonageCallUtil;
+ $this->entityManager = $entityManager;
+
+ parent::__construct();
+ }
En effectuant les changements ci-dessus, votre commande a maintenant accès aux fichiers VonageCallUtil et EntityManagerInterface.
Mettez à jour le contenu de configure() avec ce qui est montré dans l'exemple ci-dessous. Cet exemple définit la description de la commande :
$this
->setDescription('Contact all previous matches to get their feedback.');
Il est temps d'implémenter la fonctionnalité permettant de commencer à recueillir les commentaires des utilisateurs appariés. Remplacez le contenu de la méthode execute() par :
$matchRepository = $this->entityManager->getRepository(Match::class);
// Get matches that haven't had any feedback.
$matches = $matchRepository->getTodaysMatches();
if (empty($matches)) {
return 0;
}
// Loop through all retrieved matches
foreach ($matches as $match) {
if (null === $match->getCallerOneCallSuccessful()) {
// Call callerOne as we do not have feedback from them.
$this->vonageCallUtil->makeFeedbackCall(
$match->getCallerOne()
);
}
if (null === $match->getCallerTwoCallSuccessful()) {
// Call callerTwo as we do not have feedback from them.
$this->vonageCallUtil->makeFeedbackCall(
$match->getCallerTwo()
);
}
// Save changes to the database.
$this->entityManager->flush();
$this->entityManager->clear();
}
return 0;
La fonctionnalité ci-dessus recueille les correspondances de la journée, puis passe en revue chacune d'entre elles en lançant un appel à l'appelant un et à l'appelant deux pour leur demander un retour d'information sur l'appel précédent.
Comme vous pouvez le voir, vous avez fait un appel à makeFeedbackCall() à partir de l'élément VonageCallUtilmais la méthode n'existe pas encore. Ouvrez donc l'élément VonageCallUtil à l'intérieur de project/src/util/VonageCallUtil.php et ajoutez une nouvelle méthode :
public function makeFeedbackCall(User $user)
{
$ncco = [
[
'action' => 'talk',
'text' => 'Thank you for using the Befriending service. Could you please provide feedback for your call today? Enter 1 for yes, or two for no.',
'voiceName' => 'Amy',
],
[
'action' => 'input',
'maxDigits' => 1,
'eventUrl' => [
$_ENV['NGROK_URL'] . '/webhooks/userFeedback'
],
'timeOut' => 10
]
];
$this->makeCall($user, $ncco);
}
L'exemple ci-dessus fait référence à un eventUrl qui est votre URL Ngrok et /webhooks/userFeedback. Ce point de terminaison n'existe pas encore dans notre WebhooksController pour l'instant.
Cette méthode a deux objectifs. Le premier est de valider la saisie de l'utilisateur, en s'assurant qu'il n'entre que des options valides avant de continuer. Ces entrées sont 1 pour oui et 2 pour non.
Le deuxième objectif de cette méthode est de respecter l'avis de l'utilisateur. S'il choisit de progresser pour donner un retour d'information, le système le redirige vers l'action suivante, qui consiste à poser la première question de retour d'information. Si l'utilisateur sélectionne "2", ne pas fournir de retour d'information, le système répond en confirmant sa demande de ne pas fournir de retour d'information et met fin à l'appel.
Ainsi, dans le WebhooksController créer une nouvelle méthode appelée getUserFeedback() comme indiqué ci-dessous :
/**
* @Route("/webhooks/userFeedback", name="match_feedback")
*/
public function getUserFeedback(Request $request)
{
$content = json_decode($request->getContent(), true);
if (!in_array($content['dtmf'], ['1', '2'])) {
$ncco = [
[
'action' => 'talk',
'text' => 'Please only enter 1 or 2. Would you like to provide feedback for the service?',
'voiceName' => 'Amy',
],
[
'action' => 'input',
'maxDigits' => 1,
'eventUrl' => [
$request->getScheme().'://'.$request->getHost().'/webhooks/userFeedback'
],
'timeOut' => 10
]
];
} else {
// Find user by number.
$user = $this->findUserByNumber($content['to']);
// Find match by user and today.
$match = $this->findMatchByUser($user)[0];
// Determine if user is first or second caller.
$isFirstOrSecond = $this->isFirstOrSecondCaller($match, $user);
// Save entry to database.
if ($isFirstOrSecond === 1) {
$method = 'setCallerOneFeedbackAccepted';
} elseif ($isFirstOrSecond === 2) {
$method = 'setCallerTwoFeedbackAccepted';
} else {
return new JsonResponse([]);
}
$mapResponse = [
'1' => true,
'2' => false
];
$match->$method($mapResponse[$content['dtmf']]);
$this->entityManager->flush();
// Make next request.
$ncco = [
[
'action' => 'talk',
'text' => 'Thank you. Was the call successful? Please enter 1 for yes, or 2 for no.',
'voiceName' => 'Amy',
],
[
'action' => 'input',
'maxDigits' => 1,
'eventUrl' => [
$_ENV['NGROK_URL'] . '/webhooks/userFeedbackCallSuccess'
],
'timeOut' => 10
]
];
}
return new JsonResponse($ncco);
}
L'exemple ci-dessus fait référence à un eventUrl qui est votre URL Ngrok et /webhooks/userFeedbackCallSuccess. Ce point de terminaison n'existe pas encore dans notre WebhooksController pour l'instant.
Cette méthode a deux objectifs. Le premier est de valider la saisie de l'utilisateur, en s'assurant qu'il n'entre que des options valides avant de continuer. Ces entrées sont 1 pour oui et 2 pour non.
Le deuxième objectif de cette méthode est de respecter la saisie de l'utilisateur. S'il choisit '1' ou '2', alors il faut sauvegarder l'entrée de l'utilisateur dans la propriété setCallerOneCallSuccessful ou la propriété setCallerTwoCallSuccessful comme vrai ou faux (qu'il ait apprécié l'appel ou non).
Une fois l'option saisie avec succès, le code redirige l'utilisateur vers l'action suivante de l'appel, à savoir le point de terminaison. /webhooks/userFeedbackContinue
Ainsi, dans le WebhooksController créer une nouvelle méthode appelée getUserFeedbackCallSuccess() comme indiqué ci-dessous :
/**
* @Route("/webhooks/userFeedbackCallSuccess", name="match_call_success")
*/
public function getUserFeedbackCallSuccess(Request $request)
{
$content = json_decode($request->getContent(), true);
if (!in_array($content['dtmf'], ["1", "2"])) {
$ncco = [
[
'action' => 'talk',
'text' => 'Please only enter 1 or 2. Was the call successful? Please enter 1 for yes or 2 for no.',
'voiceName' => 'Amy',
],
[
'action' => 'input',
'maxDigits' => 1,
'eventUrl' => [
$request->getScheme().'://'.$request->getHost().'/webhooks/userFeedbackCallSuccess'
],
'timeOut' => 10
]
];
} else {
// Find user by number.
$user = $this->findUserByNumber($content['to']);
// Find match by user and today.
$match = $this->findMatchByUser($user)[0];
// Determine if user is first or second caller.
$isFirstOrSecond = $this->isFirstOrSecondCaller($match, $user);
// Save entry to database.
if ($isFirstOrSecond === 1) {
$method = 'setCallerOneCallSuccessful';
} elseif ($isFirstOrSecond === 2) {
$method = 'setCallerTwoCallSuccessful';
} else {
return new JsonResponse([]);
}
$mapResponse = [
'1' => true,
'2' => false
];
$match->$method($mapResponse[$content['dtmf']]);
$this->entityManager->flush();
$ncco = [
[
'action' => 'talk',
'text' => 'Would you like to have another call tomorrow? Please enter 1 for yes or 2 for no.',
'voiceName' => 'Amy',
],
[
'action' => 'input',
'maxDigits' => 1,
'eventUrl' => [
$_ENV['NGROK_URL'] . '/webhooks/userFeedbackContinue'
],
'timeOut' => 10
]
];
}
return new JsonResponse($ncco);
}
L'exemple ci-dessus fait référence à un eventUrl qui est votre URL Ngrok et webhooks/userFeedbackContinue. Ce point de terminaison n'existe pas encore dans notre WebhooksController pour l'instant.
Cette méthode a deux objectifs. Le premier est de valider la saisie de l'utilisateur, en s'assurant qu'il n'entre que des options valides avant de continuer. Ces entrées sont 1 pour oui et 2 pour non.
Le deuxième objectif de cette méthode est de respecter l'entrée de l'utilisateur. S'il choisit "1", il reste abonné au service pour un appel le lendemain.
Si l'utilisateur choisit "2", il est considéré comme inactif et n'est plus appelé.
L'appel se termine après une entrée d'option réussie.
Ainsi, dans le WebhooksController créer une nouvelle méthode appelée getUserFeedbackContinue() comme indiqué ci-dessous :
/**
* @Route("/webhooks/userFeedbackContinue", name="match_user_continue")
*/
public function getUserFeedbackContinue(Request $request)
{
$content = json_decode($request->getContent(), true);
if (!in_array($content['dtmf'], ["1", "2"])) {
$ncco = [
[
'action' => 'talk',
'text' => 'Please only enter 1 or 2. Would you like to have another call tomorrow? Please enter 1 for yes or 2 for no.',
'voiceName' => 'Amy',
],
[
'action' => 'input',
'maxDigits' => 1,
'eventUrl' => [
$_ENV['NGROK_URL'] . '/webhooks/userFeedbackContinue'
],
'timeOut' => 10
]
];
} else {
// Find user by number.
$user = $this->findUserByNumber($content['to']);
// Find match by user and today.
$match = $this->findMatchByUser($user);
if (!$match) {
return new JsonResponse([]);
}
$mapResponse = [
'1' => true,
'2' => false
];
if ($content['dtmf'] === "2") {
$user->setActive(false);
$this->entityManager->flush();
$ncco = [
[
'action' => 'talk',
'text' => 'Thank you for your feedback. Your number has been removed from the list.',
'voiceName' => 'Amy',
]
];
} else {
$ncco = [
[
'action' => 'talk',
'text' => 'Thank you for your feedback. Goodbye.',
'voiceName' => 'Amy'
],
];
}
}
return new JsonResponse($ncco);
}
Le dernier ajout nécessaire au projet est une fonction dont vous avez peut-être remarqué qu'elle est appelée isFirstOrSecondCaller() mais qui doit être créée. Cette méthode prend une instance de Match et un objet User Elle détermine alors si l'utilisateur donné est callerOne ou callerTwo de l'objet Match. Ajoutez ceci à la fin de votre classe :
private function isFirstOrSecondCaller(Match $match, User $user): ?int
{
if ($match->getCallerOne() === $user) {
return 1;
}
if ($match->getCallerTwo() === $user) {
return 2;
}
return null;
}
Conclusion
Si vous avez suivi ce tutoriel du début à la fin, vous avez maintenant créé un projet à partir d'une nouvelle installation de Symfony.
Ce nouveau projet comporte une page d'enregistrement qui, une fois soumise, envoie une demande de vérification à Vonage Verify. Cette demande déclenche l'envoi d'un SMS aux propriétaires du numéro de téléphone enregistré. Le code de vérification contenu dans le SMS doit être saisi dans la page suivante que l'utilisateur voit. Si la vérification est réussie, l'utilisateur est redirigé vers une page de succès qui lui indique l'heure à laquelle il peut s'attendre à recevoir des appels téléphoniques.
La deuxième partie de ce projet est la fonction Voice de Vonage. À l'aide de commandes, les utilisateurs sont mis en relation, appelés et réunis dans une conférence téléphonique. Plus tard dans la journée, une deuxième commande est exécutée pour demander un retour d'information à chaque personne appariée le jour même.
Le code fini de ce tutoriel peut être trouvé sur la branche end-tutorial de ce dépôt dépôt GitHub.
Vous trouverez ci-dessous quelques autres tutoriels que nous avons rédigés et qui mettent en œuvre Verify ou Voice de Vonage dans des projets :
Ajouter la vérification par SMS dans une application React Native à l'aide de Node.js et Express
Télécharger les enregistrements de l'API Voice de Vonage avec Golang
Vérification des nombres en Python avec AWS Lambda et Vonage
N'oubliez pas, si vous avez des questions, des conseils ou des idées à partager avec la communauté, n'hésitez pas à vous rendre sur notre espace de travail Slack de la communauté. Je serais ravi d'entendre les commentaires de tous ceux qui ont mis en œuvre ce tutoriel et de savoir comment leur projet fonctionne.
