Add other phone number

This commit is contained in:
Jean-Francois Monfort 2021-03-25 16:14:23 +01:00
parent 50d686f086
commit 756ed616b6
5 changed files with 138 additions and 111 deletions

View File

@ -26,146 +26,151 @@ use Psr\Cache\CacheItemPoolInterface;
/** /**
* Helper to some task linked to phonenumber. * Helper to some task linked to phonenumber.
* *
* Currently, only Twilio is supported (https://www.twilio.com/lookup). A method * Currently, only Twilio is supported (https://www.twilio.com/lookup). A method
* allow to check if the helper is configured for validation. This should be used * allow to check if the helper is configured for validation. This should be used
* before doing some validation. * before doing some validation.
* *
* *
*/ */
class PhonenumberHelper class PhonenumberHelper
{ {
/** /**
* *
* @var Client * @var Client
*/ */
protected $twilioClient; protected $twilioClient;
/** /**
* *
* @var LoggerInterface * @var LoggerInterface
*/ */
protected $logger; protected $logger;
/** /**
* *
* @var CacheItemPoolInterface * @var CacheItemPoolInterface
*/ */
protected $cachePool; protected $cachePool;
const LOOKUP_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s'; const LOOKUP_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
const FORMAT_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s'; const FORMAT_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
public function __construct( public function __construct(
CacheItemPoolInterface $cachePool, CacheItemPoolInterface $cachePool,
$config, $config,
LoggerInterface $logger LoggerInterface $logger
) { ) {
$this->logger = $logger; $this->logger = $logger;
$this->cachePool = $cachePool; $this->cachePool = $cachePool;
if (\array_key_exists('twilio_sid', $config) if (\array_key_exists('twilio_sid', $config)
&& !empty($config['twilio_sid']) && !empty($config['twilio_sid'])
&& \array_key_exists('twilio_secret', $config) && \array_key_exists('twilio_secret', $config)
&& !empty($config['twilio_secret'])) { && !empty($config['twilio_secret'])) {
$this->twilioClient = new Client([ $this->twilioClient = new Client([
'auth' => [ $config['twilio_sid'], $config['twilio_secret'] ] 'auth' => [ $config['twilio_sid'], $config['twilio_secret'] ]
]); ]);
} }
} }
/** /**
* Return true if the validation is configured and available. * Return true if the validation is configured and available.
* *
* @return bool * @return bool
*/ */
public function isPhonenumberValidationConfigured() : bool public function isPhonenumberValidationConfigured() : bool
{ {
return NULL !== $this->twilioClient; return NULL !== $this->twilioClient;
} }
/** /**
* REturn true if the phoennumber is a mobile phone. Return always false * REturn true if the phoennumber is a mobile phone. Return always false
* if the validation is not configured. * if the validation is not configured.
* *
* @param string $phonenumber * @param string $phonenumber
* @return bool * @return bool
*/ */
public function isValidPhonenumberMobile($phonenumber) : bool public function isValidPhonenumberMobile($phonenumber) : bool
{ {
$validation = $this->performTwilioLookup($phonenumber); $validation = $this->performTwilioLookup($phonenumber);
if (NULL === $validation) { if (NULL === $validation) {
return false; return false;
} }
return $validation === 'mobile'; return $validation === 'mobile';
} }
/** /**
* Return true if the phonenumber is a landline or voip phone. Return always false * Return true if the phonenumber is a landline or voip phone. Return always false
* if the validation is not configured. * if the validation is not configured.
* *
* @param string $phonenumber * @param string $phonenumber
* @return bool * @return bool
*/ */
public function isValidPhonenumberLandOrVoip($phonenumber) : bool public function isValidPhonenumberLandOrVoip($phonenumber) : bool
{ {
$validation = $this->performTwilioLookup($phonenumber); $validation = $this->performTwilioLookup($phonenumber);
if (NULL === $validation) { if (NULL === $validation) {
return false; return false;
} }
return \in_array($validation, [ 'landline', 'voip' ]); return \in_array($validation, [ 'landline', 'voip' ]);
} }
/** /**
* Return true if the phonenumber is a landline or voip phone. Return always false * Return true if the phonenumber is a landline or voip phone. Return always false
* if the validation is not configured. * if the validation is not configured.
* *
* @param string $phonenumber * @param string $phonenumber
* @return bool * @return bool
*/ */
public function isValidPhonenumberAny($phonenumber) : bool public function isValidPhonenumberAny($phonenumber) : bool
{ {
$validation = $this->performTwilioLookup($phonenumber); $validation = $this->performTwilioLookup($phonenumber);
if (NULL === $validation) { if (NULL === $validation) {
return false; return false;
} }
return \in_array($validation, [ 'landline', 'voip', 'mobile' ]); return \in_array($validation, [ 'landline', 'voip', 'mobile' ]);
} }
public function type(string $phonenumber): string
{
return $this->performTwilioLookup($phonenumber);
}
public function format($phonenumber) public function format($phonenumber)
{ {
return $this->performTwilioFormat($phonenumber); return $this->performTwilioFormat($phonenumber);
} }
protected function performTwilioFormat($phonenumber) protected function performTwilioFormat($phonenumber)
{ {
if (FALSE === $this->isPhonenumberValidationConfigured()) { if (FALSE === $this->isPhonenumberValidationConfigured()) {
return $phonenumber; return $phonenumber;
} }
// filter only number // filter only number
$filtered = \preg_replace("/[^0-9]/", "", $phonenumber); $filtered = \preg_replace("/[^0-9]/", "", $phonenumber);
$item = $this->cachePool->getItem('pnum_format_nat_'.$filtered); $item = $this->cachePool->getItem('pnum_format_nat_'.$filtered);
if ($item->isHit()) { if ($item->isHit()) {
return $item->get(); return $item->get();
} }
try { try {
$response = $this->twilioClient->get(sprintf(self::FORMAT_URI, '+'.$filtered), [ $response = $this->twilioClient->get(sprintf(self::FORMAT_URI, '+'.$filtered), [
'http_errors' => true, 'http_errors' => true,
]); ]);
} catch (ClientException $e) { } catch (ClientException $e) {
$response = $e->getResponse(); $response = $e->getResponse();
$this->logger->error("[phonenumber helper] Could not format number " $this->logger->error("[phonenumber helper] Could not format number "
@ -174,7 +179,7 @@ class PhonenumberHelper
"status_code" => $response->getStatusCode(), "status_code" => $response->getStatusCode(),
"phonenumber" => $phonenumber "phonenumber" => $phonenumber
]); ]);
return $phonenumber; return $phonenumber;
} catch (ServerException $e) { } catch (ServerException $e) {
$response = $e->getResponse(); $response = $e->getResponse();
@ -184,7 +189,7 @@ class PhonenumberHelper
"status_code" => $response->getStatusCode(), "status_code" => $response->getStatusCode(),
"phonenumber" => $phonenumber "phonenumber" => $phonenumber
]); ]);
return null; return null;
} catch (ConnectException $e) { } catch (ConnectException $e) {
$this->logger->error("[phonenumber helper] Could not format number " $this->logger->error("[phonenumber helper] Could not format number "
@ -192,38 +197,38 @@ class PhonenumberHelper
"message" => $e->getMessage(), "message" => $e->getMessage(),
"phonenumber" => $phonenumber "phonenumber" => $phonenumber
]); ]);
return null; return null;
} }
$format = \json_decode($response->getBody())->national_format; $format = \json_decode($response->getBody())->national_format;
$item $item
->set($format) ->set($format)
// expires after 3d // expires after 3d
->expiresAfter(3600 * 24 * 3) ->expiresAfter(3600 * 24 * 3)
; ;
$this->cachePool->save($item); $this->cachePool->save($item);
return $format; return $format;
} }
protected function performTwilioLookup($phonenumber) protected function performTwilioLookup($phonenumber)
{ {
if (FALSE === $this->isPhonenumberValidationConfigured()) { if (FALSE === $this->isPhonenumberValidationConfigured()) {
return null; return null;
} }
// filter only number // filter only number
$filtered = \preg_replace("/[^0-9]/", "", $phonenumber); $filtered = \preg_replace("/[^0-9]/", "", $phonenumber);
$item = $this->cachePool->getItem('pnum_'.$filtered); $item = $this->cachePool->getItem('pnum_'.$filtered);
if ($item->isHit()) { if ($item->isHit()) {
return $item->get(); return $item->get();
} }
try { try {
$response = $this->twilioClient->get(sprintf(self::LOOKUP_URI, '+'.$filtered), [ $response = $this->twilioClient->get(sprintf(self::LOOKUP_URI, '+'.$filtered), [
'http_errors' => true, 'http_errors' => true,
@ -241,7 +246,7 @@ class PhonenumberHelper
"status_code" => $response->getStatusCode(), "status_code" => $response->getStatusCode(),
"phonenumber" => $phonenumber "phonenumber" => $phonenumber
]); ]);
return null; return null;
} catch (ConnectException $e) { } catch (ConnectException $e) {
$this->logger->error("[phonenumber helper] Could not format number " $this->logger->error("[phonenumber helper] Could not format number "
@ -249,20 +254,20 @@ class PhonenumberHelper
"message" => $e->getMessage(), "message" => $e->getMessage(),
"phonenumber" => $phonenumber "phonenumber" => $phonenumber
]); ]);
return null; return null;
} }
$validation = \json_decode($response->getBody())->carrier->type; $validation = \json_decode($response->getBody())->carrier->type;
$item $item
->set($validation) ->set($validation)
// expires after 12h // expires after 12h
->expiresAfter(3600 * 12) ->expiresAfter(3600 * 12)
; ;
$this->cachePool->save($item); $this->cachePool->save($item);
return $validation; return $validation;
} }
} }

View File

@ -14,118 +14,95 @@ use Doctrine\ORM\Mapping as ORM;
class PersonPhone class PersonPhone
{ {
/** /**
* @var integer
*
* @ORM\Id * @ORM\Id
* @ORM\Column(name="id", type="integer") * @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO") * @ORM\GeneratedValue(strategy="AUTO")
*/ */
private $id; private ?int $id;
/** /**
* @var Person
*
* @ORM\ManyToOne( * @ORM\ManyToOne(
* targetEntity="Chill\PersonBundle\Entity\Person", * targetEntity="Chill\PersonBundle\Entity\Person",
* inversedBy="otherPhoneNumbers" * inversedBy="otherPhoneNumbers"
* ) * )
*/ */
private $person; private Person $person;
/**
* @ORM\Column(type="text", length=40, nullable=true)
*/
private ?string $type;
/** /**
* The phonenumber
* @var string
*
* @ORM\Column(type="text", length=40, nullable=false) * @ORM\Column(type="text", length=40, nullable=false)
*/ */
private $phonenumber = ''; private string $phonenumber = '';
/** /**
* The description
* @var string
*
* @ORM\Column(type="text", nullable=true) * @ORM\Column(type="text", nullable=true)
*/ */
private $description = ''; private ?string $description = null;
/** /**
* @var \DateTime
* @ORM\Column(type="datetime", nullable=false) * @ORM\Column(type="datetime", nullable=false)
*/ */
private $date; private \DateTime $date;
public function __construct() public function __construct()
{ {
$this->date = new \DateTime(); $this->date = new \DateTime();
} }
/**
* @return int
*/
public function getId(): int public function getId(): int
{ {
return $this->id; return $this->id;
} }
/**
* @return \Chill\PersonBundle\Entity\Person
*/
public function getPerson(): Person public function getPerson(): Person
{ {
return $this->person; return $this->person;
} }
/**
* @param \Chill\PersonBundle\Entity\Person $person
*/
public function setPerson(Person $person): void public function setPerson(Person $person): void
{ {
$this->person = $person; $this->person = $person;
} }
/** public function getType(): string
* @return string {
*/ return $this->type;
}
public function setType(string $type): void
{
$this->type = $type;
}
public function getPhonenumber(): string public function getPhonenumber(): string
{ {
return $this->phonenumber; return $this->phonenumber;
} }
/**
* @param string $phonenumber
*/
public function setPhonenumber(string $phonenumber): void public function setPhonenumber(string $phonenumber): void
{ {
$this->phonenumber = $phonenumber; $this->phonenumber = $phonenumber;
} }
/** public function getDescription(): ?string
* @return string
*/
public function getDescription(): string
{ {
return $this->description; return $this->description;
} }
/** public function setDescription(?string $description): void
* @param string $description
*/
public function setDescription(string $description): void
{ {
$this->description = $description; $this->description = $description;
} }
/**
* @return \DateTime
*/
public function getDate(): \DateTime public function getDate(): \DateTime
{ {
return $this->date; return $this->date;
} }
/**
* @param \DateTime $date
*/
public function setDate(\DateTime $date): void public function setDate(\DateTime $date): void
{ {
$this->date = $date; $this->date = $date;

View File

@ -2,17 +2,27 @@
namespace Chill\PersonBundle\Form\Type; namespace Chill\PersonBundle\Form\Type;
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
use Chill\PersonBundle\Entity\PersonPhone; use Chill\PersonBundle\Entity\PersonPhone;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TelType; use Symfony\Component\Form\Extension\Core\Type\TelType;
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
class PersonPhoneType extends AbstractType class PersonPhoneType extends AbstractType
{ {
private PhonenumberHelper $phonenumberHelper;
public function __construct(PhonenumberHelper $phonenumberHelper)
{
$this->phonenumberHelper = $phonenumberHelper;
}
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder->add('phonenumber', TelType::class, [ $builder->add('phonenumber', TelType::class, [
@ -23,17 +33,15 @@ class PersonPhoneType extends AbstractType
$builder->add('description', TextType::class, [ $builder->add('description', TextType::class, [
'required' => false, 'required' => false,
]); ]);
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
$type = $this->phonenumberHelper->type($event->getData()->getPhonenumber());
$event->getData()->setType($type);
});
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
/*
$resolver
->setDefault('data_class', PersonPhone::class)
->setDefault('validation_groups', ['general', 'creation'])
;
*/
$resolver $resolver
->setDefaults([ ->setDefaults([
'data_class' => PersonPhone::class, 'data_class' => PersonPhone::class,

View File

@ -22,7 +22,7 @@ services:
$closingMotiveRepository: '@Chill\PersonBundle\Repository\ClosingMotiveRepository' $closingMotiveRepository: '@Chill\PersonBundle\Repository\ClosingMotiveRepository'
tags: tags:
- { name: form.type, alias: closing_motive } - { name: form.type, alias: closing_motive }
Chill\PersonBundle\Form\AccompanyingPeriodType: Chill\PersonBundle\Form\AccompanyingPeriodType:
arguments: arguments:
$config: "%chill_person.accompanying_period_fields%" $config: "%chill_person.accompanying_period_fields%"
@ -39,10 +39,16 @@ services:
- '@Symfony\Component\Translation\TranslatorInterface' - '@Symfony\Component\Translation\TranslatorInterface'
tags: tags:
- { name: form.type } - { name: form.type }
Chill\PersonBundle\Form\Type\PersonAltNameType: Chill\PersonBundle\Form\Type\PersonAltNameType:
arguments: arguments:
$configHelper: '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper' $configHelper: '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
$translatableStringHelper: '@chill.main.helper.translatable_string' $translatableStringHelper: '@chill.main.helper.translatable_string'
tags: tags:
- { name: form.type } - { name: form.type }
Chill\PersonBundle\Form\Type\PersonPhoneType:
arguments:
$phonenumberHelper: '@Chill\MainBundle\Phonenumber\PhonenumberHelper'
tags:
- { name: form.type }

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Person;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20210325141540 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE chill_person_phone ADD type TEXT DEFAULT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your need
$this->addSql('ALTER TABLE chill_person_phone DROP type');
}
}