Search code examples
phpvalidationsymfonydoctrine-ormsymfony-validator

Doctrine entity validation at construct


I'm trying to improve myself with doctrine, and doing best practices. I found a good presentation of best practices : https://ocramius.github.io/doctrine-best-practices/#/50

I try to have a valid object after __construct. (see https://ocramius.github.io/doctrine-best-practices/#/52) But I'm using @Assert annotation for validating my object.

How can I do to validate ? Have to inject the validator service inside my object at __construct ?

My object :

class Person
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="guid")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="UUID")
     * @expose
     */
    private $id;

    /**
     * @var int
     *
     * @ORM\Column(name="name", type="string")
     * @Assert\Email()
     */
    private $email;

    public function __construct($email, ValidatorInterface $validator){

          $this->email = $email;
          $validator->validate($this); // good practice ?

    }

My final goal is to unit test the input validation of this entity.

Thank you

Edit :

Basing on the answer of Yonel, I added this in the end of my constructor :

 $errors = $validator->validate($this);
    if(count($errors) > 0) {
        $errorsString = (string) $errors;
        throw new InvalidArgumentException($errorsString);
    }

Is it a good practices or not ? If not, why ? Thank you!


Solution

  • The presentation take best practice both from and world.

    The principle you are highlighting is about the presentation layer of the application that can use a form component that can validate the user input then this data is used for instantiate the Entity.

    In the example of the presentation the named constructor take as argument a form, so the validation of the email address is done by the form (validating user input).

    The meaning of having an object with a valid state is intended about having an Object of type User that have both name, surname ad email valid (as example not null).

    So you can have the following object:

    class User
    {
    
        private $name;
    
        private $surname;
    
        private $email;
    
        private function __construct(string $name, string $surname, string $email)
        {
            $this->name = $name;
            $this->surname = $surname;
            $this->email = $email;
        }
    
        public static function create(string $name, string $surname, string $email): User
        {
            return new static($name, $surname, $email);
        }
    
        public function fromFormData(FormInterface $form):User
        {
            // The form validate user input (i.e. valid email address)
            return self::create($form->get('name'), $form->get('surname'), $form->get('email'));
        }
    
    }
    

    Another approach could be using DTO or you can take a look at this useful bundle about validate DTOs object.

    Hope this help