move validation on person into annotations

This commit is contained in:
Julien Fastré 2021-09-01 16:06:20 +02:00
parent 7faddbe3fe
commit 41d76542b4
9 changed files with 165 additions and 97 deletions

View File

@ -43,26 +43,26 @@ class Configuration implements ConfigurationInterface
->arrayNode('validation') ->arrayNode('validation')
->canBeDisabled() ->canBeDisabled()
->children() ->children()
->scalarNode('birthdate_not_after')
->info($this->validationBirthdateNotAfterInfos)
->defaultValue('P1D')
->validate()
->ifTrue(function($period) {
try {
$interval = new \DateInterval($period);
} catch (\Exception $ex) {
return true;
}
return false;
})
->thenInvalid('Invalid period for birthdate validation : "%s" '
. 'The parameter should match duration as defined by ISO8601 : '
. 'https://en.wikipedia.org/wiki/ISO_8601#Durations')
->end() // birthdate_not_after, parent = children of validation
->booleanNode('center_required') ->booleanNode('center_required')
->info('Enable a center for each person entity. If disabled, you must provide your own center provider') ->info('Enable a center for each person entity. If disabled, you must provide your own center provider')
->defaultValue(true) ->defaultValue(true)
->end() ->end()
->scalarNode('birthdate_not_after')
->info($this->validationBirthdateNotAfterInfos)
->defaultValue('P1D')
->validate()
->ifTrue(function($period) {
try {
$interval = new \DateInterval($period);
} catch (\Exception $ex) {
return true;
}
return false;
})
->thenInvalid('Invalid period for birthdate validation : "%s" '
. 'The parameter should match duration as defined by ISO8601 : '
. 'https://en.wikipedia.org/wiki/ISO_8601#Durations')
->end() // birthdate_not_after, parent = children of validation
->end() // children for 'validation', parent = validation ->end() // children for 'validation', parent = validation
->end() //validation, parent = children of root ->end() //validation, parent = children of root
->end() // children of root, parent = root ->end() // children of root, parent = root

View File

@ -43,6 +43,11 @@ use Doctrine\Common\Collections\Criteria;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
use Symfony\Component\Validator\Constraints as Assert;
use Chill\PersonBundle\Validator\Constraints\Person\Birthdate;
use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
use Chill\PersonBundle\Validator\Constraints\Person\PersonHasCenter;
use Chill\PersonBundle\Validator\Constraints\Household\HouseholdMembershipSequential;
/** /**
* Person Class * Person Class
@ -57,6 +62,12 @@ use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
* @DiscriminatorMap(typeProperty="type", mapping={ * @DiscriminatorMap(typeProperty="type", mapping={
* "person"=Person::class * "person"=Person::class
* }) * })
* @PersonHasCenter(
* groups={"general", "creation"}
* )
* @HouseholdMembershipSequential(
* groups={"household_memberships"}
* )
*/ */
class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateInterface class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateInterface
{ {
@ -75,6 +86,13 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* @var string * @var string
* *
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
* @Assert\NotBlank(
* groups={"general", "creation"}
* )
* @Assert\Length(
* max=255,
* groups={"general", "creation"}
* )
*/ */
private $firstName; private $firstName;
@ -83,6 +101,13 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* @var string * @var string
* *
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
* @Assert\NotBlank(
* groups={"general", "creation"}
* )
* @Assert\Length(
* max=255,
* groups={"general", "creation"}
* )
*/ */
private $lastName; private $lastName;
@ -102,6 +127,12 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* @var \DateTime * @var \DateTime
* *
* @ORM\Column(type="date", nullable=true) * @ORM\Column(type="date", nullable=true)
* @Assert\Date(
* groups={"general", "creation"}
* )
* @Birthdate(
* groups={"general", "creation"}
* )
*/ */
private $birthdate; private $birthdate;
@ -110,6 +141,9 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* @var \DateTimeImmutable * @var \DateTimeImmutable
* *
* @ORM\Column(type="date_immutable", nullable=true) * @ORM\Column(type="date_immutable", nullable=true)
* @Assert\Date(
* groups={"general", "creation"}
* )
*/ */
private ?\DateTimeImmutable $deathdate; private ?\DateTimeImmutable $deathdate;
@ -150,6 +184,9 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* @var string * @var string
* *
* @ORM\Column(type="string", length=9, nullable=true) * @ORM\Column(type="string", length=9, nullable=true)
* @Assert\NotNull(
* groups={"general", "creation"}
* )
*/ */
private $gender; private $gender;
@ -179,8 +216,11 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* @var \DateTime * @var \DateTime
* *
* @ORM\Column(type="date", nullable=true) * @ORM\Column(type="date", nullable=true)
* @Assert\Date(
* groups={"general", "creation"}
* )
*/ */
private $maritalStatusDate; private ?\DateTime $maritalStatusDate;
/** /**
* Comment on marital status * Comment on marital status
@ -202,6 +242,10 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* @var string * @var string
* *
* @ORM\Column(type="text", nullable=true) * @ORM\Column(type="text", nullable=true)
* @Assert\Email(
* checkMX=true,
* groups={"general", "creation"}
* )
*/ */
private $email = ''; private $email = '';
@ -210,6 +254,14 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* @var string * @var string
* *
* @ORM\Column(type="text", length=40, nullable=true) * @ORM\Column(type="text", length=40, nullable=true)
* @Assert\Regex(
* pattern="/^([\+{1}])([0-9\s*]{4,20})$/",
* groups={"general", "creation"}
* )
* @PhonenumberConstraint(
* type="landline",
* groups={"general", "creation"}
* )
*/ */
private $phonenumber = ''; private $phonenumber = '';
@ -218,6 +270,14 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* @var string * @var string
* *
* @ORM\Column(type="text", length=40, nullable=true) * @ORM\Column(type="text", length=40, nullable=true)
* @Assert\Regex(
* pattern="/^([\+{1}])([0-9\s*]{4,20})$/",
* groups={"general", "creation"}
* )
* @PhonenumberConstraint(
* type="mobile",
* groups={"general", "creation"}
* )
*/ */
private $mobilenumber = ''; private $mobilenumber = '';
@ -230,12 +290,13 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* cascade={"persist", "remove", "merge", "detach"}, * cascade={"persist", "remove", "merge", "detach"},
* orphanRemoval=true * orphanRemoval=true
* ) * )
* @Assert\Valid(
* traverse=true,
* groups={"general", "creation"}
* )
*/ */
private $otherPhoneNumbers; private $otherPhoneNumbers;
//TO-ADD caseOpeningDate
//TO-ADD nativeLanguag
/** /**
* The person's spoken languages * The person's spoken languages
* @var ArrayCollection * @var ArrayCollection
@ -352,6 +413,8 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
private $addresses; private $addresses;
/** /**
* fullname canonical. Read-only field, which is calculated by
* the database.
* @var string * @var string
* *
* @ORM\Column(type="text", nullable=true) * @ORM\Column(type="text", nullable=true)
@ -372,6 +435,8 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
private array $currentHouseholdAt = []; private array $currentHouseholdAt = [];
/** /**
* Read-only field, computed by the database
*
* @ORM\OneToMany( * @ORM\OneToMany(
* targetEntity=PersonHouseholdAddress::class, * targetEntity=PersonHouseholdAddress::class,
* mappedBy="person" * mappedBy="person"
@ -389,8 +454,6 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
/** /**
* Person constructor. * Person constructor.
*
* @param \DateTime|null $opening
*/ */
public function __construct() public function __construct()
{ {
@ -403,6 +466,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
$this->householdAddresses = new ArrayCollection(); $this->householdAddresses = new ArrayCollection();
$this->genderComment = new CommentEmbeddable(); $this->genderComment = new CommentEmbeddable();
$this->maritalStatusComment = new CommentEmbeddable(); $this->maritalStatusComment = new CommentEmbeddable();
$this->periodLocatedOn = new ArrayCollection();
} }
/** /**
@ -1200,6 +1264,10 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* Validation callback that checks if the accompanying periods are valid * Validation callback that checks if the accompanying periods are valid
* *
* This method add violation errors. * This method add violation errors.
*
* @Assert\Callback(
* groups={"accompanying_period_consistent"}
* )
*/ */
public function isAccompanyingPeriodValid(ExecutionContextInterface $context) public function isAccompanyingPeriodValid(ExecutionContextInterface $context)
{ {
@ -1245,6 +1313,10 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* two addresses with the same validFrom date) * two addresses with the same validFrom date)
* *
* This method add violation errors. * This method add violation errors.
*
* @Assert\Callback(
* groups={"addresses_consistent"}
* )
*/ */
public function isAddressesValid(ExecutionContextInterface $context) public function isAddressesValid(ExecutionContextInterface $context)
{ {

View File

@ -38,6 +38,7 @@ class BirthdateValidatorTest extends ConstraintValidatorTestCase
$this->validator->validate($bornToday, $this->createConstraint()); $this->validator->validate($bornToday, $this->createConstraint());
$this->buildViolation('msg') $this->buildViolation('msg')
->setParameter('%date%', (new \DateTime('yesterday'))->format('d-m-Y')) ->setParameter('%date%', (new \DateTime('yesterday'))->format('d-m-Y'))
->setCode('3f42fd96-0b2d-11ec-8cf3-0f3b1b1ca1c4')
->assertRaised(); ->assertRaised();
} }
@ -54,6 +55,7 @@ class BirthdateValidatorTest extends ConstraintValidatorTestCase
$this->validator->validate($bornAfter, $this->createConstraint()); $this->validator->validate($bornAfter, $this->createConstraint());
$this->buildViolation('msg') $this->buildViolation('msg')
->setParameter('%date%', (new \DateTime('yesterday'))->format('d-m-Y')) ->setParameter('%date%', (new \DateTime('yesterday'))->format('d-m-Y'))
->setCode('3f42fd96-0b2d-11ec-8cf3-0f3b1b1ca1c4')
->assertRaised(); ->assertRaised();
} }

View File

@ -0,0 +1,55 @@
<?php
namespace Validator\Person;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Validator\Constraints\Person\Birthdate;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class PersonValidationTest extends KernelTestCase
{
private ValidatorInterface $validator;
protected function setUp()
{
self::bootKernel();
$this->validator = self::$container->get(ValidatorInterface::class);
}
public function testFirstnameValidation()
{
$person = (new Person())
->setFirstname(\str_repeat('a', 500));
$errors = $this->validator->validate($person, null, ["creation"]);
foreach ($errors->getIterator() as $error) {
if (Length::TOO_LONG_ERROR === $error->getCode()) {
$this->assertTrue(true,
"error code for firstname too long is present");
return;
}
}
$this->assertTrue(false,
"error code for fistname too long is present");
}
public function testBirthdateInFuture()
{
$person = (new Person())
->setBirthdate(new \Datetime('+2 months'));
$errors = $this->validator->validate($person, null, ["creation"]);
foreach ($errors->getIterator() as $error) {
if (Birthdate::BIRTHDATE_INVALID_CODE === $error->getCode()) {
$this->assertTrue(true,
"error code for birthdate invalid is present");
return;
}
}
$this->assertTrue(false,
"error code for birthdate invalid is present");
}
}

View File

@ -29,9 +29,11 @@ use Symfony\Component\Validator\Constraint;
* (this interval_spec itself is based on ISO8601 : * (this interval_spec itself is based on ISO8601 :
* https://en.wikipedia.org/wiki/ISO_8601#Durations) * https://en.wikipedia.org/wiki/ISO_8601#Durations)
* *
* @author Julien Fastré <julien.fastre@champs-libres.coop> * @Annotation
*/ */
class Birthdate extends Constraint class Birthdate extends Constraint
{ {
public const BIRTHDATE_INVALID_CODE = '3f42fd96-0b2d-11ec-8cf3-0f3b1b1ca1c4';
public $message = "The birthdate must be before %date%"; public $message = "The birthdate must be before %date%";
} }

View File

@ -53,6 +53,7 @@ class BirthdateValidator extends ConstraintValidator
if ($limitDate < $value) { if ($limitDate < $value) {
$this->context->buildViolation($constraint->message) $this->context->buildViolation($constraint->message)
->setParameter('%date%', $limitDate->format('d-m-Y')) ->setParameter('%date%', $limitDate->format('d-m-Y'))
->setCode(Birthdate::BIRTHDATE_INVALID_CODE)
->addViolation(); ->addViolation();
} }

View File

@ -2,6 +2,9 @@
namespace Chill\PersonBundle\Validator\Constraints\Person; namespace Chill\PersonBundle\Validator\Constraints\Person;
/**
* @Annotation
*/
class PersonHasCenter extends \Symfony\Component\Validator\Constraint class PersonHasCenter extends \Symfony\Component\Validator\Constraint
{ {
public string $message = "A center is required"; public string $message = "A center is required";

View File

@ -62,18 +62,18 @@ services:
- { name: security.voter } - { name: security.voter }
- { name: chill.role } - { name: chill.role }
chill.person.birthdate_validation: Chill\PersonBundle\Validator\Constraints\:
class: Chill\PersonBundle\Validator\Constraints\BirthdateValidator autowire: true
autoconfigure: true
resource: '../Validator/Constraints/'
# override default config, must be loaded after resource
Chill\PersonBundle\Validator\Constraints\BirthdateValidator:
arguments: arguments:
- "%chill_person.validation.birtdate_not_before%" - "%chill_person.validation.birtdate_not_before%"
tags: tags:
- { name: validator.constraint_validator, alias: birthdate_not_before } - { name: validator.constraint_validator, alias: birthdate_not_before }
Chill\PersonBundle\Validator\Constraints\Household\HouseholdMembershipSequentialValidator:
autowire: true
tags:
- { name: validator.constraint_validator }
Chill\PersonBundle\Repository\: Chill\PersonBundle\Repository\:
autowire: true autowire: true
autoconfigure: true autoconfigure: true

View File

@ -1,70 +1,3 @@
Chill\PersonBundle\Entity\Person:
properties:
firstName:
- NotBlank:
groups: [general, creation]
- Length:
min: 2
max: 255
minMessage: 'This name is too short. It must containt {{ limit }} chars'
maxMessage: 'This name is too long. It must containt {{ limit }} chars'
groups: [general, creation]
lastName:
- NotBlank:
groups: [general, creation]
- Length:
min: 2
max: 255
minMessage: 'This name is too short. It must containt {{ limit }} chars'
maxMessage: 'This name is too long. It must containt {{ limit }} chars'
groups: [general, creation]
birthdate:
- Date:
message: 'Birthdate not valid'
groups: [general, creation]
- Chill\PersonBundle\Validator\Constraints\Birthdate:
groups: [general, creation]
gender:
- NotNull:
groups: [general, creation]
#accompanyingPeriods:
# - Valid:
# traverse: true
email:
- Email:
groups: [general, creation]
message: 'The email is not valid'
checkMX: true
phonenumber:
- Regex:
pattern: '/^([\+{1}])([0-9\s*]{4,20})$/'
groups: [general, creation]
message: 'Invalid phone number: it should begin with the international prefix starting with "+", hold only digits and be smaller than 20 characters. Ex: +33123456789'
- Chill\MainBundle\Validation\Constraint\PhonenumberConstraint:
type: landline
groups: [ general, creation ]
mobilenumber:
- Regex:
pattern: '/^([\+{1}])([0-9\s*]{4,20})$/'
groups: [general, creation]
message: 'Invalid phone number: it should begin with the international prefix starting with "+", hold only digits and be smaller than 20 characters. Ex: +33623456789'
- Chill\MainBundle\Validation\Constraint\PhonenumberConstraint:
type: mobile
groups: [ general, creation ]
otherPhoneNumbers:
- Valid:
traverse: true
constraints:
- Callback:
callback: isAccompanyingPeriodValid
groups: [accompanying_period_consistent]
- Callback:
callback: isAddressesValid
groups: [addresses_consistent]
- Chill\PersonBundle\Validator\Constraints\Household\HouseholdMembershipSequential:
groups: [ 'household_memberships' ]
Chill\PersonBundle\Entity\AccompanyingPeriod: Chill\PersonBundle\Entity\AccompanyingPeriod:
properties: properties:
openingDate: openingDate: