senenhermida.docs
  • Design Patterns
  • SOLID
  • PHP History
    • Late static binding
  • PHP Questions
  • Symfony
    • Service container
    • Event dispatcher
    • Security
    • Validation
    • Serializer
    • Http Kernel Component
    • Cache component
    • Symfony history
  • Algorithms
  • Leetcode
    • Codility
  • My CV
Powered by GitBook
On this page
  • Validation
  • Validation
  1. Symfony

Validation

Validation

Validation

  • constraint: set of rules

// src/Entity/Author.php
namespace App\Entity;

// ...
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
    #[Assert\NotBlank]
    private string $name;
}

// ...
public function author(ValidatorInterface $validator): Response
{
    $author = new Author();

    // ... do something to the $author object

    $errors = $validator->validate($author);

    if (count($errors) > 0) {
        /*
         * Uses a __toString method on the $errors variable which is a
         * ConstraintViolationList object. This gives us a nice string
         * for debugging.
         */
        $errorsString = (string) $errors;

        return new Response($errorsString);
    }

    return new Response('The author is valid! Yes!');
}
  • Constraints: notblank, blank, isnull, istrue, meila, ip, json, equalto, date, choice..

  • Constraint Configuration

  #[Assert\Choice(
      choices: ['fiction', 'non-fiction'],
      message: 'Choose a valid genre.',
  )]
  private string $genre;
  • Constraints targets

    • properties

      #[Assert\NotBlank]
      #[Assert\Length(min: 3)]
      private string $firstName;
    • getters (get, has, is)

      #[Assert\IsTrue(message: 'The password cannot match your first name')]
      public function isPasswordSafe(): bool
      {
      	return $this->firstName !== $this->password;
      }
    • class: callback constraint

  • Custom constraints

// src/Validator/ContainsAlphanumeric.php
namespace App\Validator;

use Symfony\Component\Validator\Constraint;

#[\Attribute] // add this to use it as attribute in other classes
class ContainsAlphanumeric extends Constraint
{
    public string $message = 'The string "{{ string }}" contains an illegal character: it can only contain letters or numbers.';
    public string $mode = 'strict';

    // all configurable options must be passed to the constructor
    public function __construct(string $mode = null, string $message = null, array $groups = null, $payload = null)
    {
        parent::__construct([], $groups, $payload);

        $this->mode = $mode ?? $this->mode;
        $this->message = $message ?? $this->message;
    }
}
// src/Validator/ContainsAlphanumericValidator.php
namespace App\Validator;

class ContainsAlphanumericValidator extends ConstraintValidator
{
    public function validate(mixed $value, Constraint $constraint): void
    {
        if (!$constraint instanceof ContainsAlphanumeric) {
            throw new UnexpectedTypeException($constraint, ContainsAlphanumeric::class);
        }

        // custom constraints should ignore null and empty values to allow
        // other constraints (NotBlank, NotNull, etc.) to take care of that
        if (null === $value || '' === $value) {
            return;
        }

        if (!is_string($value)) {
            // throw this exception if your validator cannot handle the passed type so that it can be marked as invalid
            throw new UnexpectedValueException($value, 'string');

            // separate multiple types using pipes
            // throw new UnexpectedValueException($value, 'string|int');
        }

        // access your configuration options like this:
        if ('strict' === $constraint->mode) {
            // ...
        }

        if (preg_match('/^[a-zA-Z0-9]+$/', $value, $matches)) {
            return;
        }

        // the argument must be a string or an object implementing __toString()
        $this->context->buildViolation($constraint->message)
            ->setParameter('{{ string }}', $value)
            ->addViolation();
    }
}

Inside validate(), you don't need to return a value. Instead, you add violations to the validator's context property and a value will be considered valid if it causes no violations.

#[AcmeAssert\ContainsAlphanumeric(mode: 'loose')]
protected string $name;
  • validation groups

// src/Entity/User.php
namespace App\Entity;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;

class User implements UserInterface
{
    #[Assert\Email(groups: ['registration'])]
    private string $email;

    #[Assert\NotBlank(groups: ['registration'])]
    #[Assert\Length(min: 7, groups: ['registration'])]
    private string $password;

    #[Assert\Length(min: 2)]
    private string $city;
}
$errors = $validator->validate($author, null, ['registration']);

sequentially apply validation groups: in order to avoid multiple error message

  • validation callback

class Author
{
    #[Assert\Callback]
    public function validate(ExecutionContextInterface $context, mixed $payload): void
    {
        // ...
    }
}
PreviousSecurityNextSerializer