Skip to content

Commit f15c85e

Browse files
committed
Merge pull request #1403 from Inori/cookbook/custom-validator
[Cookbook] Custom validator entry improvements
2 parents eee4109 + 1e7711a commit f15c85e

File tree

1 file changed

+95
-23
lines changed

1 file changed

+95
-23
lines changed

cookbook/validation/custom_constraint.rst

Lines changed: 95 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,43 @@
22
single: Validation; Custom constraints
33

44
How to create a Custom Validation Constraint
5-
--------------------------------------------
5+
============================================
66

77
You can create a custom constraint by extending the base constraint class,
8-
:class:`Symfony\\Component\\Validator\\Constraint`. Options for your
9-
constraint are represented as public properties on the constraint class. For
10-
example, the :doc:`Url</reference/constraints/Url>` constraint includes
11-
the ``message`` and ``protocols`` properties:
8+
:class:`Symfony\\Component\\Validator\\Constraint`.
9+
As an example we're going to create a simple validator that checks if string
10+
contains only alphanumeric characters.
1211

13-
.. code-block:: php
12+
Creating Constraint class
13+
-------------------------
1414

15-
namespace Symfony\Component\Validator\Constraints;
15+
First you need to create a Constraint class and extend :class:`Symfony\\Component\\Validator\\Constraint`::
16+
17+
namespace Acme\DemoBundle\Validator\Constraints;
1618

1719
use Symfony\Component\Validator\Constraint;
1820

1921
/**
2022
* @Annotation
2123
*/
22-
class Protocol extends Constraint
24+
class ContainsAlphanumeric extends Constraint
2325
{
24-
public $message = 'This value is not a valid protocol';
25-
public $protocols = array('http', 'https', 'ftp', 'ftps');
26+
public $message = 'Missing at least one alphanumeric character in "%string%" string';
2627
}
2728
2829
.. note::
2930

3031
The ``@Annotation`` annotation is necessary for this new constraint in
3132
order to make it available for use in classes via annotations.
33+
Options for your constraint are represented as public properties on the constraint class.
3234

35+
Creating Validator itself
36+
-------------------------
37+
3338
As you can see, a constraint class is fairly minimal. The actual validation is
3439
performed by a another "constraint validator" class. The constraint validator
3540
class is specified by the constraint's ``validatedBy()`` method, which
36-
includes some simple default logic:
37-
38-
.. code-block:: php
41+
includes some simple default logic::
3942

4043
// in the base Symfony\Component\Validator\Constraint class
4144
public function validatedBy()
@@ -47,22 +50,19 @@ In other words, if you create a custom ``Constraint`` (e.g. ``MyConstraint``),
4750
Symfony2 will automatically look for another class, ``MyConstraintValidator``
4851
when actually performing the validation.
4952

50-
The validator class is also simple, and only has one required method: ``isValid``.
51-
Furthering our example, take a look at the ``ProtocolValidator`` as an example:
52-
53-
.. code-block:: php
53+
The validator class is also simple, and only has one required method: ``isValid``::
5454

55-
namespace Symfony\Component\Validator\Constraints;
55+
namespace Acme\DemoBundle\Validator\Constraints;
5656
5757
use Symfony\Component\Validator\Constraint;
5858
use Symfony\Component\Validator\ConstraintValidator;
5959

60-
class ProtocolValidator extends ConstraintValidator
60+
class ContainsAlphanumericValidator extends ConstraintValidator
6161
{
6262
public function isValid($value, Constraint $constraint)
6363
{
64-
if (!in_array($value, $constraint->protocols)) {
65-
$this->setMessage($constraint->message, array('%protocols%' => $constraint->protocols));
64+
if (!preg_match('/^[a-zA-Za0-9]+$/', $value, $matches)) {
65+
$this->setMessage($constraint->message, array('%string%' => $value);
6666

6767
return false;
6868
}
@@ -75,6 +75,77 @@ Furthering our example, take a look at the ``ProtocolValidator`` as an example:
7575

7676
Don't forget to call ``setMessage`` to construct an error message when the
7777
value is invalid.
78+
79+
Using newly created validator
80+
-----------------------------
81+
82+
Using custom validators is very easy, just as the ones provided by Symfony2 itself:
83+
84+
.. configuration-block::
85+
86+
.. code-block:: yaml
87+
88+
# src/Acme/BlogBundle/Resources/config/validation.yml
89+
Acme\DemoBundle\Entity\AcmeEntity:
90+
properties:
91+
name:
92+
- NotBlank: ~
93+
- Acme\DemoBundle\Validator\Constraints\ContainsAlphanumeric: ~
94+
95+
.. code-block:: php-annotations
96+
97+
// src/Acme/DemoBundle/Entity/AcmeEntity.php
98+
99+
use Symfony\Component\Validator\Constraints as Assert;
100+
use Acme\DemoBundle\Validator\Constraints as AcmeAssert;
101+
102+
class AcmeEntity
103+
{
104+
// ...
105+
106+
/**
107+
* @Assert\NotBlank
108+
* @AcmeAssert\ContainsAlphanumeric
109+
*/
110+
protected $name;
111+
112+
// ...
113+
}
114+
115+
.. code-block:: xml
116+
117+
<!-- src/Acme/DemoBundle/Resources/config/validation.xml -->
118+
<?xml version="1.0" encoding="UTF-8" ?>
119+
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
120+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
121+
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
122+
123+
<class name="Acme\DemoBundle\Entity\AcmeEntity">
124+
<property name="name">
125+
<constraint name="NotBlank" />
126+
<constraint name="Acme\DemoBundle\Validator\Constraints\ContainsAlphanumeric" />
127+
</property>
128+
</class>
129+
</constraint-mapping>
130+
131+
.. code-block:: php
132+
133+
// src/Acme/DemoBundle/Entity/AcmeEntity.php
134+
135+
use Symfony\Component\Validator\Mapping\ClassMetadata;
136+
use Symfony\Component\Validator\Constraints\NotBlank;
137+
use Acme\DemoBundle\Validator\Constraints\ContainsAlphanumeric;
138+
139+
class AcmeEntity
140+
{
141+
public $name;
142+
143+
public static function loadValidatorMetadata(ClassMetadata $metadata)
144+
{
145+
$metadata->addPropertyConstraint('name', new NotBlank());
146+
$metadata->addPropertyConstraint('name', new ContainsAlphanumeric());
147+
}
148+
}
78149
79150
Constraint Validators with Dependencies
80151
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -142,8 +213,9 @@ With this, the validator ``isValid()`` method gets an object as its first argume
142213
{
143214
if ($protocol->getFoo() != $protocol->getBar()) {
144215

145-
// bind error message on foo property
146-
$this->context->addViolationAtSubPath('foo', $constraint->getMessage(), array(), null);
216+
$propertyPath = $this->context->getPropertyPath() . 'foo';
217+
$this->context->setPropertyPath($propertyPath);
218+
$this->context->addViolation($constraint->getMessage(), array(), null);
147219

148220
return false;
149221
}

0 commit comments

Comments
 (0)