Skip to content

2.5 Validation API changes #4233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Oct 18, 2014
21 changes: 17 additions & 4 deletions book/validation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -822,9 +822,13 @@ With this configuration, there are three validation groups:
fields only.

To tell the validator to use a specific group, pass one or more group names
as the second argument to the ``validate()`` method::
as the third argument to the ``validate()`` method::

$errors = $validator->validate($author, array('registration'));
// If you're using the new 2.5 validation API (you probably are!)
$errors = $validator->validate($author, null, array('registration'));

// If you're using the old 2.4 validation API
// $errors = $validator->validate($author, array('registration'));

If no groups are specified, all constraints that belong in group ``Default``
will be applied.
Expand Down Expand Up @@ -1189,10 +1193,19 @@ it looks like this::
$emailConstraint->message = 'Invalid email address';

// use the validator to validate the value
// If you're using the new 2.5 validation API (you probably are!)
$errorList = $this->get('validator')->validate(
$email,
$emailConstraint
);

// If you're using the old 2.4 validation API
/*
$errorList = $this->get('validator')->validateValue(
$email,
$emailConstraint
);
*/

if (count($errorList) == 0) {
// this IS a valid email address, do something
Expand All @@ -1206,13 +1219,13 @@ it looks like this::
// ...
}

By calling ``validateValue`` on the validator, you can pass in a raw value and
By calling ``validate`` on the validator, you can pass in a raw value and
the constraint object that you want to validate that value against. A full
list of the available constraints - as well as the full class name for each
constraint - is available in the :doc:`constraints reference </reference/constraints>`
section .

The ``validateValue`` method returns a :class:`Symfony\\Component\\Validator\\ConstraintViolationList`
The ``validate`` method returns a :class:`Symfony\\Component\\Validator\\ConstraintViolationList`
object, which acts just like an array of errors. Each error in the collection
is a :class:`Symfony\\Component\\Validator\\ConstraintViolation` object,
which holds the error message on its ``getMessage`` method.
Expand Down
42 changes: 42 additions & 0 deletions cookbook/bundles/best_practices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,48 @@ semantic configuration described in the cookbook.
If you are defining services, they should also be prefixed with the bundle
alias.

Custom Validation Constraints
-----------------------------

Starting with Symfony 2.5, a new Validation API was introduced. In fact,
there are 3 modes, which the user can configure in their project:

* 2.4: the original 2.4 and earlier validation API;
* 2.5: the new 2.5 and later validation API;
* 2.5-BC: the new 2.5 API with a backwards-compatible layer so that the
2.4 API still works. This is only available in PHP 5.3.9+.

As a bundle author, you'll want to support *both* API's, since some users
may still be using the 2.4 API. Specifically, if your bundle adds a violation
directly to the :class:`Symfony\\Component\\Validator\\Context\\ExecutionContext`
(e.g. like in a custom validation constraint), you'll need to check for which
API is being used. The following code, would work for *all* users::

use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
// ...

class ContainsAlphanumericValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
if ($this->context instanceof ExecutionContextInterface) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a use statement for this class. (and for all 3 classes used in this example actually)

// the 2.5 API
$this->context->buildViolation($constraint->message)
->setParameter('%string%', $value)
->addViolation();
);
} else {
// the 2.4 API
$this->context->addViolation(
$constraint->message,
array('%string%' => $value)
);
}
}
}

Learn more from the Cookbook
----------------------------

Expand Down
2 changes: 1 addition & 1 deletion cookbook/form/unit_testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ on other extensions. You need add those extensions to the factory object::
{
parent::setUp();

$validator = $this->getMock('\Symfony\Component\Validator\ValidatorInterface');
$validator = $this->getMock('\Symfony\Component\Validator\Validator\ValidatorInterface');
$validator->method('validate')->will($this->returnValue(new ConstraintViolationList()));

$this->factory = Forms::createFormFactoryBuilder()
Expand Down
28 changes: 20 additions & 8 deletions cookbook/validation/custom_constraint.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,22 +65,34 @@ The validator class is also simple, and only has one required method ``validate(
public function validate($value, Constraint $constraint)
{
if (!preg_match('/^[a-zA-Za0-9]+$/', $value, $matches)) {
// If you're using the new 2.5 validation API (you probably are!)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we also have to add a comment about the 2.5 BC API?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't think so. If you're using 2.5 BC API, then the 2.5 validation API is available to you and you should be using it. I think the fact that you also have the ability to use the older API is a detail that they don't need to care about (that's just there so their old code doesn't break).

$this->context->buildViolation($constraint->message)
->setParameter('%string%', $value)
->addViolation();
);

// If you're using the old 2.4 validation API
/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to either use either single line or multi line comments, not both:

// If you're using the old 2.4 validation API
// $this->context->addValidation(
//     ...
// );
/* If you're using the old 2.4 validation API
$this->context->addViolation(
    ...
);
*/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started to make this change, but I think it actually looked worse. The first line is a one-line comment, which you can keep even if you commented out the next block of code.

$this->context->addViolation(
$constraint->message,
array('%string%' => $value)
);
*/
}
}
}

.. note::

The ``validate`` method does not return a value; instead, it adds violations
to the validator's ``context`` property with an ``addViolation`` method
call if there are validation failures. Therefore, a value could be considered
as being valid if it causes no violations to be added to the context.
The first parameter of the ``addViolation`` call is the error message to
use for that violation.
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. The ``buildViolation`` method takes the error
message as its argument and returns an instance of
:class:`Symfony\\Component\\Validator\\Violation\\ConstraintViolationBuilderInterface`.
The ``addViolation`` method call finally adds the violation to the context.

.. versionadded:: 2.5
The ``buildViolation`` method was added in Symfony 2.5. For usage examples
with older Symfony versions, see the corresponding versions of this documentation
page.

Using the new Validator
-----------------------
Expand Down
42 changes: 32 additions & 10 deletions reference/constraints/Callback.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ Configuration
namespace Acme\BlogBundle\Entity;

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContextInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
// if you're using the older 2.4 validation API, you'll need this instead
// use Symfony\Component\Validator\ExecutionContextInterface;

class Author
{
Expand Down Expand Up @@ -100,7 +102,9 @@ can set "violations" directly on this object and determine to which field
those errors should be attributed::

// ...
use Symfony\Component\Validator\ExecutionContextInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
// if you're using the older 2.4 validation API, you'll need this instead
// use Symfony\Component\Validator\ExecutionContextInterface;

class Author
{
Expand All @@ -114,16 +118,27 @@ those errors should be attributed::

// check if the name is actually a fake name
if (in_array($this->getFirstName(), $fakeNames)) {
// If you're using the new 2.5 validation API (you probably are!)
$context->buildViolation('This name sounds totally fake!')
->atPath('firstName')
->addViolation();

// If you're using the old 2.4 validation API
/*
$context->addViolationAt(
'firstName',
'This name sounds totally fake!',
array(),
null
'This name sounds totally fake!'
);
*/
}
}
}

.. versionadded:: 2.5
The ``buildViolation`` method was added in Symfony 2.5. For usage examples
with older Symfony versions, see the corresponding versions of this documentation
page.

Static Callbacks
----------------

Expand All @@ -137,11 +152,16 @@ have access to the object instance, they receive the object as the first argumen

// check if the name is actually a fake name
if (in_array($object->getFirstName(), $fakeNames)) {
// If you're using the new 2.5 validation API (you probably are!)
$context->buildViolation('This name sounds totally fake!')
->atPath('firstName')
->addViolation()
;

// If you're using the old 2.4 validation API
$context->addViolationAt(
'firstName',
'This name sounds totally fake!',
array(),
null
'This name sounds totally fake!'
);
}
}
Expand All @@ -156,7 +176,9 @@ your validation function is ``Vendor\Package\Validator::validate()``::

namespace Vendor\Package;

use Symfony\Component\Validator\ExecutionContextInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
// if you're using the older 2.4 validation API, you'll need this instead
// use Symfony\Component\Validator\ExecutionContextInterface;

class Validator
{
Expand Down Expand Up @@ -274,7 +296,7 @@ callback method:

* A closure.

Concrete callbacks receive an :class:`Symfony\\Component\\Validator\\ExecutionContextInterface`
Concrete callbacks receive an :class:`Symfony\\Component\\Validator\\Context\\ExecutionContextInterface`
instance as only argument.

Static or closure callbacks receive the validated object as the first argument
Expand Down