Bernhard Schussek
Contributed by Bernhard Schussek in #9133

Sometimes, we make things more complex than they should be; the Callback constraint was such an example, and using it in Symfony 2.4 is much simpler, feels more natural, and is more powerful at the same time.

The Callback constraint is a great way to define custom validation rules without the need to create custom constraints and validator classes. You just need to create one or more methods that does the validation and generates some violations. But before 2.4, the constraint needs to be attached to the class itself instead of the validation method(s), leading to some verbose configuration (in all configuration formats):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use Symfony\Component\Validator\ExecutionContextInterface;

/**
 * @Assert\Callback(methods={
 *     { "Acme\BlogBundle\MyStaticValidatorClass", "isAuthorValid"}
 * })
 */
class Author
{
    // ...

    public function isAuthorValid(ExecutionContextInterface $context)
    {
        // somehow you have an array of "fake names"
        $fakeNames = array();

        // check if the name is actually a fake name
        if (in_array($this->getFirstName(), $fakeNames)) {
            $context->addViolationAt('firstname', 'This name sounds totally fake!', array(), null);
        }
    }
}

As of Symfony 2.4, you can just annotate the method itself (the XML or YAML configuration formats are also much simpler to write):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use Symfony\Component\Validator\ExecutionContextInterface;

class Author
{
    // ...

    /**
     * @Assert\Callback
     */
    public function isAuthorValid(ExecutionContextInterface $context)
    {
        // somehow you have an array of "fake names"
        $fakeNames = array();

        // check if the name is actually a fake name
        if (in_array($this->getFirstName(), $fakeNames)) {
            $context->addViolationAt('firstname', 'This name sounds totally fake!', array(), null);
        }
    }
}

You can now also use an external method to validate your object, in which case, the callback will get the object to validate as a first argument:

1
2
3
4
5
6
7
8
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @Assert\Callback({"Vendor\Package\Validator", "validate"})
 */
class Author
{
}

The Callback constraint documentation page gives you all the information you need to upgrade your existing code and to take advantage of the new features.

Of course, the old way still works to keep backward compatibility, but who would not want to upgrade to the new way?

Published in #Living on the edge