Skip to content

Commit 887f0d2

Browse files
committed
Updated the documentation for the simplified Callback constraint
1 parent 9a715ed commit 887f0d2

File tree

1 file changed

+147
-106
lines changed

1 file changed

+147
-106
lines changed

reference/constraints/Callback.rst

Lines changed: 147 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
Callback
22
========
33

4-
The purpose of the Callback assertion is to let you create completely custom
4+
.. versionadded:: 2.4
5+
The ``Callback`` constraint was simplified in Symfony 2.4. For usage
6+
examples with older Symfony versions, see the corresponding versions of this
7+
documentation page.
8+
9+
The purpose of the Callback constraint is to create completely custom
510
validation rules and to assign any validation errors to specific fields on
611
your object. If you're using validation with forms, this means that you can
712
make these custom errors display next to a specific field, instead of simply
@@ -20,15 +25,15 @@ can do anything, including creating and assigning validation errors.
2025
+----------------+------------------------------------------------------------------------+
2126
| Applies to | :ref:`class<validation-class-target>` |
2227
+----------------+------------------------------------------------------------------------+
23-
| Options | - `methods`_ |
28+
| Options | - `callback`_ |
2429
+----------------+------------------------------------------------------------------------+
2530
| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Callback` |
2631
+----------------+------------------------------------------------------------------------+
2732
| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\CallbackValidator` |
2833
+----------------+------------------------------------------------------------------------+
2934

30-
Setup
31-
-----
35+
Configuration
36+
-------------
3237

3338
.. configuration-block::
3439

@@ -37,21 +42,25 @@ Setup
3742
# src/Acme/BlogBundle/Resources/config/validation.yml
3843
Acme\BlogBundle\Entity\Author:
3944
constraints:
40-
- Callback:
41-
methods: [isAuthorValid]
45+
- Callback: validate
4246
4347
.. code-block:: php-annotations
4448
4549
// src/Acme/BlogBundle/Entity/Author.php
4650
namespace Acme\BlogBundle\Entity;
4751
4852
use Symfony\Component\Validator\Constraints as Assert;
53+
use Symfony\Component\Validator\ExecutionContextInterface;
4954
50-
/**
51-
* @Assert\Callback(methods={"isAuthorValid"})
52-
*/
5355
class Author
5456
{
57+
/**
58+
* @Assert\Callback
59+
*/
60+
public function validate(ExecutionContextInterface $context)
61+
{
62+
// ...
63+
}
5564
}
5665
5766
.. code-block:: xml
@@ -63,11 +72,7 @@ Setup
6372
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
6473
6574
<class name="Acme\BlogBundle\Entity\Author">
66-
<constraint name="Callback">
67-
<option name="methods">
68-
<value>isAuthorValid</value>
69-
</option>
70-
</constraint>
75+
<constraint name="Callback">validate</constraint>
7176
</class>
7277
</constraint-mapping>
7378
@@ -83,9 +88,7 @@ Setup
8388
{
8489
public static function loadValidatorMetadata(ClassMetadata $metadata)
8590
{
86-
$metadata->addConstraint(new Assert\Callback(array(
87-
'methods' => array('isAuthorValid'),
88-
)));
91+
$metadata->addConstraint(new Assert\Callback('validate'));
8992
}
9093
}
9194
@@ -98,133 +101,171 @@ those errors should be attributed::
98101

99102
// ...
100103
use Symfony\Component\Validator\ExecutionContextInterface;
101-
104+
102105
class Author
103106
{
104107
// ...
105108
private $firstName;
106-
107-
public function isAuthorValid(ExecutionContextInterface $context)
109+
110+
public function validate(ExecutionContextInterface $context)
108111
{
109112
// somehow you have an array of "fake names"
110-
$fakeNames = array();
111-
113+
$fakeNames = array(/* ... */);
114+
112115
// check if the name is actually a fake name
113116
if (in_array($this->getFirstName(), $fakeNames)) {
114-
$context->addViolationAt('firstname', 'This name sounds totally fake!', array(), null);
117+
$context->addViolationAt('firstName', 'This name sounds totally fake!', array(), null);
115118
}
116119
}
117120
}
118121

119-
Options
120-
-------
122+
Static Callbacks
123+
----------------
121124

122-
methods
123-
~~~~~~~
125+
You can also use the constraint with static methods. Since static methods don't
126+
have access to the object instance, they receive the object as first argument::
124127

125-
**type**: ``array`` **default**: ``array()`` [:ref:`default option<validation-default-option>`]
128+
public static function validate($object, ExecutionContextInterface $context)
129+
{
130+
// somehow you have an array of "fake names"
131+
$fakeNames = array(/* ... */);
126132

127-
This is an array of the methods that should be executed during the validation
128-
process. Each method can be one of the following formats:
133+
// check if the name is actually a fake name
134+
if (in_array($object->getFirstName(), $fakeNames)) {
135+
$context->addViolationAt('firstName', 'This name sounds totally fake!', array(), null);
136+
}
137+
}
129138

130-
1) **String method name**
139+
External Callbacks and Closures
140+
-------------------------------
131141

132-
If the name of a method is a simple string (e.g. ``isAuthorValid``), that
133-
method will be called on the same object that's being validated and the
134-
``ExecutionContextInterface`` will be the only argument (see the above example).
142+
If you want to execute a static callback method that is not located in the class
143+
of the validated object, you can configure the constraint to invoke an array
144+
callable as supported by PHP's :phpfunction:`call_user_func` function. Suppose
145+
your validation function is `Vendor\Package\Validator::validate()`::
135146

136-
2) **Static array callback**
147+
namespace Vendor\Package;
137148

138-
Each method can also be specified as a standard array callback:
149+
use Symfony\Component\Validator\ExecutionContextInterface;
139150

140-
.. configuration-block::
151+
class Validator
152+
{
153+
public function validate($object, ExecutionContextInterface $context)
154+
{
155+
// ...
156+
}
157+
}
141158

142-
.. code-block:: yaml
159+
You can then use the following configuration to invoke this validator::
143160

144-
# src/Acme/BlogBundle/Resources/config/validation.yml
145-
Acme\BlogBundle\Entity\Author:
146-
constraints:
147-
- Callback:
148-
methods:
149-
- [Acme\BlogBundle\MyStaticValidatorClass, isAuthorValid]
161+
.. configuration-block::
150162

151-
.. code-block:: php-annotations
163+
.. code-block:: yaml
152164
153-
// src/Acme/BlogBundle/Entity/Author.php
154-
use Symfony\Component\Validator\Constraints as Assert;
165+
# src/Acme/BlogBundle/Resources/config/validation.yml
166+
Acme\BlogBundle\Entity\Author:
167+
constraints:
168+
- Callback: [Vendor\Package\Validator, validate]
155169
156-
/**
157-
* @Assert\Callback(methods={
158-
* { "Acme\BlogBundle\MyStaticValidatorClass", "isAuthorValid"}
159-
* })
160-
*/
161-
class Author
162-
{
163-
}
170+
.. code-block:: php-annotations
164171
165-
.. code-block:: xml
172+
// src/Acme/BlogBundle/Entity/Author.php
173+
namespace Acme\BlogBundle\Entity;
166174
167-
<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
168-
<?xml version="1.0" encoding="UTF-8" ?>
169-
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
170-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
171-
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
175+
use Symfony\Component\Validator\Constraints as Assert;
176+
use Symfony\Component\Validator\ExecutionContextInterface;
172177
173-
<class name="Acme\BlogBundle\Entity\Author">
174-
<constraint name="Callback">
175-
<option name="methods">
176-
<value>
177-
<value>Acme\BlogBundle\MyStaticValidatorClass</value>
178-
<value>isAuthorValid</value>
179-
</value>
180-
</option>
181-
</constraint>
182-
</class>
183-
</constraint-mapping>
178+
/**
179+
* @Assert\Callback({"Vendor\Package\Validator", "validate"})
180+
*/
181+
class Author
182+
{
183+
}
184184
185-
.. code-block:: php
185+
.. code-block:: xml
186186
187-
// src/Acme/BlogBundle/Entity/Author.php
187+
<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
188+
<?xml version="1.0" encoding="UTF-8" ?>
189+
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
190+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
191+
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
192+
193+
<class name="Acme\BlogBundle\Entity\Author">
194+
<constraint name="Callback">
195+
<value>Vendor\Package\Validator</value>
196+
<value>validate</value>
197+
</constraint>
198+
</class>
199+
</constraint-mapping>
188200
189-
use Symfony\Component\Validator\Mapping\ClassMetadata;
190-
use Symfony\Component\Validator\Constraints\Callback;
201+
.. code-block:: php
191202
192-
class Author
203+
// src/Acme/BlogBundle/Entity/Author.php
204+
namespace Acme\BlogBundle\Entity;
205+
206+
use Symfony\Component\Validator\Mapping\ClassMetadata;
207+
use Symfony\Component\Validator\Constraints as Assert;
208+
209+
class Author
210+
{
211+
public static function loadValidatorMetadata(ClassMetadata $metadata)
193212
{
194-
public $name;
195-
196-
public static function loadValidatorMetadata(ClassMetadata $metadata)
197-
{
198-
$metadata->addConstraint(new Callback(array(
199-
'methods' => array(
200-
array('Acme\BlogBundle\MyStaticValidatorClass', 'isAuthorValid'),
201-
),
202-
)));
203-
}
213+
$metadata->addConstraint(new Assert\Callback(array(
214+
'Vendor\Package\Validator',
215+
'validate'
216+
)));
204217
}
218+
}
205219
206-
In this case, the static method ``isAuthorValid`` will be called on the
207-
``Acme\BlogBundle\MyStaticValidatorClass`` class. It's passed both the original
208-
object being validated (e.g. ``Author``) as well as the ``ExecutionContextInterface``::
220+
.. note::
209221

210-
namespace Acme\BlogBundle;
211-
212-
use Symfony\Component\Validator\ExecutionContextInterface;
213-
use Acme\BlogBundle\Entity\Author;
214-
215-
class MyStaticValidatorClass
222+
The Callback constraint does *not* support global callback functions or
223+
It is *not* possible to specify a global function or a :term:`service`
224+
method as callback. To validate using a service, you should
225+
:doc:`create a custom validation constraint</cookbook/validation/custom_constraint>`
226+
and add that new constraint to your class.
227+
228+
When configuring the constraint via PHP, you can also pass a closure to the
229+
constructor of the Callback constraint::
230+
231+
// src/Acme/BlogBundle/Entity/Author.php
232+
namespace Acme\BlogBundle\Entity;
233+
234+
use Symfony\Component\Validator\Mapping\ClassMetadata;
235+
use Symfony\Component\Validator\Constraints as Assert;
236+
237+
class Author
238+
{
239+
public static function loadValidatorMetadata(ClassMetadata $metadata)
216240
{
217-
public static function isAuthorValid(Author $author, ExecutionContextInterface $context)
218-
{
241+
$callback = function ($object, ExecutionContextInterface $context) {
219242
// ...
220-
}
243+
};
244+
245+
$metadata->addConstraint(new Assert\Callback($callback));
221246
}
247+
}
248+
249+
Options
250+
-------
251+
252+
callback
253+
~~~~~~~~
254+
255+
**type**: ``string``, ``array`` or ``Closure`` [:ref:`default option<validation-default-option>`]
256+
257+
The callback option accepts three different formats for specifying the
258+
callback method:
259+
260+
* A **string** containing the name of a concrete or static method.
261+
262+
* An array callable with the format ``array('<Class>', '<method>')``.
263+
264+
* A closure.
222265

223-
.. tip::
266+
Concrete callbacks receive an :class:`Symfony\\Component\\Validator\\ExecutionContextInterface`
267+
instance as only argument.
224268

225-
If you specify your ``Callback`` constraint via PHP, then you also have
226-
the option to make your callback either a PHP closure or a non-static
227-
callback. It is *not* currently possible, however, to specify a :term:`service`
228-
as a constraint. To validate using a service, you should
229-
:doc:`create a custom validation constraint</cookbook/validation/custom_constraint>`
230-
and add that new constraint to your class.
269+
Static or closure callbacks receive the validated object as first argument
270+
and the :class:`Symfony\\Component\\Validator\\ExecutionContextInterface`
271+
instance as second argument.

0 commit comments

Comments
 (0)