diff --git a/src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php b/src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php index c29ac27be..70fb7ed3a 100644 --- a/src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php +++ b/src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php @@ -26,146 +26,151 @@ use Psr\Cache\CacheItemPoolInterface; /** * 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 * before doing some validation. - * + * * */ class PhonenumberHelper { /** * - * @var Client + * @var Client */ protected $twilioClient; - + /** * * @var LoggerInterface */ protected $logger; - + /** * * @var CacheItemPoolInterface */ protected $cachePool; - + const LOOKUP_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s'; const FORMAT_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s'; - - + + public function __construct( CacheItemPoolInterface $cachePool, - $config, + $config, LoggerInterface $logger ) { $this->logger = $logger; $this->cachePool = $cachePool; - - if (\array_key_exists('twilio_sid', $config) + + if (\array_key_exists('twilio_sid', $config) && !empty($config['twilio_sid']) - && \array_key_exists('twilio_secret', $config) + && \array_key_exists('twilio_secret', $config) && !empty($config['twilio_secret'])) { - + $this->twilioClient = new Client([ 'auth' => [ $config['twilio_sid'], $config['twilio_secret'] ] ]); - } + } } - + /** * Return true if the validation is configured and available. - * + * * @return bool */ public function isPhonenumberValidationConfigured() : bool { 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. - * + * * @param string $phonenumber * @return bool */ public function isValidPhonenumberMobile($phonenumber) : bool { $validation = $this->performTwilioLookup($phonenumber); - + if (NULL === $validation) { return false; } - + 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. - * + * * @param string $phonenumber * @return bool */ public function isValidPhonenumberLandOrVoip($phonenumber) : bool { $validation = $this->performTwilioLookup($phonenumber); - + if (NULL === $validation) { return false; } - + 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. - * + * * @param string $phonenumber * @return bool */ public function isValidPhonenumberAny($phonenumber) : bool { $validation = $this->performTwilioLookup($phonenumber); - + if (NULL === $validation) { return false; } - + return \in_array($validation, [ 'landline', 'voip', 'mobile' ]); } - + + public function type(string $phonenumber): string + { + return $this->performTwilioLookup($phonenumber); + } + public function format($phonenumber) { return $this->performTwilioFormat($phonenumber); } - + protected function performTwilioFormat($phonenumber) { if (FALSE === $this->isPhonenumberValidationConfigured()) { return $phonenumber; } - + // filter only number $filtered = \preg_replace("/[^0-9]/", "", $phonenumber); - + $item = $this->cachePool->getItem('pnum_format_nat_'.$filtered); - + if ($item->isHit()) { return $item->get(); } - + try { $response = $this->twilioClient->get(sprintf(self::FORMAT_URI, '+'.$filtered), [ 'http_errors' => true, ]); - + } catch (ClientException $e) { $response = $e->getResponse(); $this->logger->error("[phonenumber helper] Could not format number " @@ -174,7 +179,7 @@ class PhonenumberHelper "status_code" => $response->getStatusCode(), "phonenumber" => $phonenumber ]); - + return $phonenumber; } catch (ServerException $e) { $response = $e->getResponse(); @@ -184,7 +189,7 @@ class PhonenumberHelper "status_code" => $response->getStatusCode(), "phonenumber" => $phonenumber ]); - + return null; } catch (ConnectException $e) { $this->logger->error("[phonenumber helper] Could not format number " @@ -192,38 +197,38 @@ class PhonenumberHelper "message" => $e->getMessage(), "phonenumber" => $phonenumber ]); - + return null; } - + $format = \json_decode($response->getBody())->national_format; - + $item ->set($format) // expires after 3d ->expiresAfter(3600 * 24 * 3) ; - + $this->cachePool->save($item); - + return $format; } - + protected function performTwilioLookup($phonenumber) { if (FALSE === $this->isPhonenumberValidationConfigured()) { return null; } - + // filter only number $filtered = \preg_replace("/[^0-9]/", "", $phonenumber); - + $item = $this->cachePool->getItem('pnum_'.$filtered); - + if ($item->isHit()) { return $item->get(); } - + try { $response = $this->twilioClient->get(sprintf(self::LOOKUP_URI, '+'.$filtered), [ 'http_errors' => true, @@ -241,7 +246,7 @@ class PhonenumberHelper "status_code" => $response->getStatusCode(), "phonenumber" => $phonenumber ]); - + return null; } catch (ConnectException $e) { $this->logger->error("[phonenumber helper] Could not format number " @@ -249,20 +254,20 @@ class PhonenumberHelper "message" => $e->getMessage(), "phonenumber" => $phonenumber ]); - + return null; } - + $validation = \json_decode($response->getBody())->carrier->type; - + $item ->set($validation) // expires after 12h ->expiresAfter(3600 * 12) ; - + $this->cachePool->save($item); - + return $validation; } } diff --git a/src/Bundle/ChillPersonBundle/Entity/PersonPhone.php b/src/Bundle/ChillPersonBundle/Entity/PersonPhone.php index f413cc0c0..1916404b9 100644 --- a/src/Bundle/ChillPersonBundle/Entity/PersonPhone.php +++ b/src/Bundle/ChillPersonBundle/Entity/PersonPhone.php @@ -14,118 +14,95 @@ use Doctrine\ORM\Mapping as ORM; class PersonPhone { /** - * @var integer - * * @ORM\Id * @ORM\Column(name="id", type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ - private $id; + private ?int $id; /** - * @var Person - * * @ORM\ManyToOne( * targetEntity="Chill\PersonBundle\Entity\Person", * 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) */ - private $phonenumber = ''; + private string $phonenumber = ''; /** - * The description - * @var string - * * @ORM\Column(type="text", nullable=true) */ - private $description = ''; + private ?string $description = null; /** - * @var \DateTime * @ORM\Column(type="datetime", nullable=false) */ - private $date; + private \DateTime $date; public function __construct() { $this->date = new \DateTime(); } - /** - * @return int - */ public function getId(): int { return $this->id; } - /** - * @return \Chill\PersonBundle\Entity\Person - */ public function getPerson(): Person { return $this->person; } - /** - * @param \Chill\PersonBundle\Entity\Person $person - */ public function setPerson(Person $person): void { $this->person = $person; } - /** - * @return string - */ + public function getType(): string + { + return $this->type; + } + + public function setType(string $type): void + { + $this->type = $type; + } + public function getPhonenumber(): string { return $this->phonenumber; } - /** - * @param string $phonenumber - */ public function setPhonenumber(string $phonenumber): void { $this->phonenumber = $phonenumber; } - /** - * @return string - */ - public function getDescription(): string + public function getDescription(): ?string { return $this->description; } - /** - * @param string $description - */ - public function setDescription(string $description): void + public function setDescription(?string $description): void { $this->description = $description; } - /** - * @return \DateTime - */ public function getDate(): \DateTime { return $this->date; } - /** - * @param \DateTime $date - */ public function setDate(\DateTime $date): void { $this->date = $date; diff --git a/src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php b/src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php index 3d3f44813..b96f7b2b0 100644 --- a/src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php +++ b/src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php @@ -2,17 +2,27 @@ namespace Chill\PersonBundle\Form\Type; +use Chill\MainBundle\Phonenumber\PhonenumberHelper; use Chill\PersonBundle\Entity\PersonPhone; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\TelType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; class PersonPhoneType extends AbstractType { + private PhonenumberHelper $phonenumberHelper; + + public function __construct(PhonenumberHelper $phonenumberHelper) + { + $this->phonenumberHelper = $phonenumberHelper; + } + public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('phonenumber', TelType::class, [ @@ -23,17 +33,15 @@ class PersonPhoneType extends AbstractType $builder->add('description', TextType::class, [ '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) { - /* - $resolver - ->setDefault('data_class', PersonPhone::class) - ->setDefault('validation_groups', ['general', 'creation']) - ; - */ - $resolver ->setDefaults([ 'data_class' => PersonPhone::class, diff --git a/src/Bundle/ChillPersonBundle/config/services/form.yaml b/src/Bundle/ChillPersonBundle/config/services/form.yaml index 659421c8a..05991ffe9 100644 --- a/src/Bundle/ChillPersonBundle/config/services/form.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/form.yaml @@ -22,7 +22,7 @@ services: $closingMotiveRepository: '@Chill\PersonBundle\Repository\ClosingMotiveRepository' tags: - { name: form.type, alias: closing_motive } - + Chill\PersonBundle\Form\AccompanyingPeriodType: arguments: $config: "%chill_person.accompanying_period_fields%" @@ -39,10 +39,16 @@ services: - '@Symfony\Component\Translation\TranslatorInterface' tags: - { name: form.type } - + Chill\PersonBundle\Form\Type\PersonAltNameType: arguments: $configHelper: '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper' $translatableStringHelper: '@chill.main.helper.translatable_string' tags: - { name: form.type } + + Chill\PersonBundle\Form\Type\PersonPhoneType: + arguments: + $phonenumberHelper: '@Chill\MainBundle\Phonenumber\PhonenumberHelper' + tags: + - { name: form.type } diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210325141540.php b/src/Bundle/ChillPersonBundle/migrations/Version20210325141540.php new file mode 100644 index 000000000..6e2f105df --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210325141540.php @@ -0,0 +1,31 @@ +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'); + } +}