2 Methods to Define Expression for When Constraint in Symfony 8

2 Methods to Define Expression for When Constraint in Symfony 8

The When constraint in Symfony provides a clean way to apply validation rules conditionally. Instead of duplicating validation logic or creating multiple validation groups, we can decide when a constraint should be triggered based on an expression. This tutorial provides 2 methods on how to define expression for When constraint in Symfony 8 application.

To see the When constraint in action, we'll validate an Account entity from a controller. The controller creates an account with a business type. Depending on that value, additional validation rule will be enforced.

src/Controller/TestController.php

<?php

namespace App\Controller;

use App\Entity\Account;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Validator\Validator\ValidatorInterface;

class TestController
{
    #[Route('/')]
    public function index(ValidatorInterface $validator): Response
    {
        $account = new Account();
        $account->setType(Account::TYPE_BUSINESS);

        $errors = $validator->validate($account);
        if (count($errors) > 0) {
            return new Response((string) $errors);
        }

        return new Response('Valid');
    }
}

Method 1 - Expression language

The most common way to define a condition for the When constraint is by using Symfony expression language syntax. The expression is written as a string and evaluated at runtime.

Here, the vatNumber field becomes mandatory only when the account type is set to business. The expression references the current object using this and compares the value against a class constant.

src/Entity/Account.php

<?php

namespace App\Entity;

use Symfony\Component\Validator\Constraints as Assert;

class Account
{
    public const string TYPE_PERSONAL = 'personal';
    public const string TYPE_BUSINESS = 'business';

    private string $type;

    #[Assert\When(
        expression: 'this.getType() === constant("App\\\\Entity\\\\Account::TYPE_BUSINESS")',
        constraints: [new Assert\NotBlank()],
    )]
    private ?string $vatNumber = null;

    public function getType(): string { return $this->type; }
    public function setType(string $type): void { $this->type = $type; }

    public function getVatNumber(): ?string { return $this->vatNumber; }
    public function setVatNumber(?string $vatNumber): void { $this->vatNumber = $vatNumber; }
}

Method 2 - Closure

Since PHP 8.5, we can define the expression as a Closure. Instead of writing an expression string, we can use native PHP logic, which improves readability and enables static analysis. This makes complex logic easier to maintain and avoids escaping strings or constants.

src/Entity/Account.php

<?php

namespace App\Entity;

use Symfony\Component\Validator\Constraints as Assert;

class Account
{
    public const string TYPE_PERSONAL = 'personal';
    public const string TYPE_BUSINESS = 'business';

    private string $type;

    #[Assert\When(
        expression: static function (Account $account) {
            return $account->getType() === self::TYPE_BUSINESS;
        },
        constraints: [new Assert\NotBlank()],
    )]
    private ?string $vatNumber = null;

    public function getType(): string { return $this->type; }
    public function setType(string $type): void { $this->type = $type; }

    public function getVatNumber(): ?string { return $this->vatNumber; }
    public function setVatNumber(?string $vatNumber): void { $this->vatNumber = $vatNumber; }
}

Leave a Comment

Cancel reply

Your email address will not be published.