Skip to content

Updated the documentation for the simplified Callback constraint #3012

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 1 commit into from
Oct 8, 2013
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 147 additions & 106 deletions reference/constraints/Callback.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
Callback
========

The purpose of the Callback assertion is to let you create completely custom
.. versionadded:: 2.4
The ``Callback`` constraint was simplified in Symfony 2.4. For usage
examples with older Symfony versions, see the corresponding versions of this
documentation page.

The purpose of the Callback constraint is to create completely custom
validation rules and to assign any validation errors to specific fields on
your object. If you're using validation with forms, this means that you can
make these custom errors display next to a specific field, instead of simply
Expand All @@ -20,15 +25,15 @@ can do anything, including creating and assigning validation errors.
+----------------+------------------------------------------------------------------------+
| Applies to | :ref:`class<validation-class-target>` |
+----------------+------------------------------------------------------------------------+
| Options | - `methods`_ |
| Options | - `callback`_ |
+----------------+------------------------------------------------------------------------+
| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Callback` |
+----------------+------------------------------------------------------------------------+
| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\CallbackValidator` |
+----------------+------------------------------------------------------------------------+

Setup
-----
Configuration
-------------

.. configuration-block::

Expand All @@ -37,21 +42,25 @@ Setup
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
constraints:
- Callback:
methods: [isAuthorValid]
- Callback: validate

.. code-block:: php-annotations

// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContextInterface;

/**
* @Assert\Callback(methods={"isAuthorValid"})
*/
class Author
{
/**
* @Assert\Callback
*/
public function validate(ExecutionContextInterface $context)
{
// ...
}
}

.. code-block:: xml
Expand All @@ -63,11 +72,7 @@ Setup
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">

<class name="Acme\BlogBundle\Entity\Author">
<constraint name="Callback">
<option name="methods">
<value>isAuthorValid</value>
</option>
</constraint>
<constraint name="Callback">validate</constraint>
</class>
</constraint-mapping>

Expand All @@ -83,9 +88,7 @@ Setup
{
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addConstraint(new Assert\Callback(array(
'methods' => array('isAuthorValid'),
)));
$metadata->addConstraint(new Assert\Callback('validate'));
}
}

Expand All @@ -98,133 +101,171 @@ those errors should be attributed::

// ...
use Symfony\Component\Validator\ExecutionContextInterface;

class Author
{
// ...
private $firstName;
public function isAuthorValid(ExecutionContextInterface $context)

public function validate(ExecutionContextInterface $context)
{
// somehow you have an array of "fake names"
$fakeNames = array();
$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);
$context->addViolationAt('firstName', 'This name sounds totally fake!', array(), null);
}
}
}

Options
-------
Static Callbacks
----------------

methods
~~~~~~~
You can also use the constraint with static methods. Since static methods don't
have access to the object instance, they receive the object as first argument::

**type**: ``array`` **default**: ``array()`` [:ref:`default option<validation-default-option>`]
public static function validate($object, ExecutionContextInterface $context)
{
// somehow you have an array of "fake names"
$fakeNames = array(/* ... */);
Copy link
Member

Choose a reason for hiding this comment

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

what about $fakeNames = array(...); (without the comments)?


This is an array of the methods that should be executed during the validation
process. Each method can be one of the following formats:
// check if the name is actually a fake name
if (in_array($object->getFirstName(), $fakeNames)) {
$context->addViolationAt('firstName', 'This name sounds totally fake!', array(), null);
Copy link
Member

Choose a reason for hiding this comment

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

$context->addViolationAt(
    'firstName',
    'This name sounds totally fake!',
    array(),
    null
);

}
}

1) **String method name**
External Callbacks and Closures
-------------------------------

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

Choose a reason for hiding this comment

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

double backticks should be used here


2) **Static array callback**
namespace Vendor\Package;

Each method can also be specified as a standard array callback:
use Symfony\Component\Validator\ExecutionContextInterface;

.. configuration-block::
class Validator
{
public function validate($object, ExecutionContextInterface $context)
{
// ...
}
}

.. code-block:: yaml
You can then use the following configuration to invoke this validator::
Copy link
Member

Choose a reason for hiding this comment

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

use only one colon here


# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
constraints:
- Callback:
methods:
- [Acme\BlogBundle\MyStaticValidatorClass, isAuthorValid]
.. configuration-block::

.. code-block:: php-annotations
.. code-block:: yaml

// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
constraints:
- Callback: [Vendor\Package\Validator, validate]

/**
* @Assert\Callback(methods={
* { "Acme\BlogBundle\MyStaticValidatorClass", "isAuthorValid"}
* })
*/
class Author
{
}
.. code-block:: php-annotations

.. code-block:: xml
// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;

<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContextInterface;
Copy link
Member

Choose a reason for hiding this comment

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

the ExecutionContextInterface interface is not needed in this example, is it?


<class name="Acme\BlogBundle\Entity\Author">
<constraint name="Callback">
<option name="methods">
<value>
<value>Acme\BlogBundle\MyStaticValidatorClass</value>
<value>isAuthorValid</value>
</value>
</option>
</constraint>
</class>
</constraint-mapping>
/**
* @Assert\Callback({"Vendor\Package\Validator", "validate"})
*/
class Author
{
}

.. code-block:: php
.. code-block:: xml

// src/Acme/BlogBundle/Entity/Author.php
<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">

<class name="Acme\BlogBundle\Entity\Author">
<constraint name="Callback">
<value>Vendor\Package\Validator</value>
<value>validate</value>
</constraint>
</class>
</constraint-mapping>

use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\Callback;
.. code-block:: php

class Author
// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;

use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
public $name;

public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addConstraint(new Callback(array(
'methods' => array(
array('Acme\BlogBundle\MyStaticValidatorClass', 'isAuthorValid'),
),
)));
}
$metadata->addConstraint(new Assert\Callback(array(
'Vendor\Package\Validator',
'validate'
Copy link
Member

Choose a reason for hiding this comment

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

there's a comma missing

)));
}
}

In this case, the static method ``isAuthorValid`` will be called on the
``Acme\BlogBundle\MyStaticValidatorClass`` class. It's passed both the original
object being validated (e.g. ``Author``) as well as the ``ExecutionContextInterface``::
.. note::

namespace Acme\BlogBundle;

use Symfony\Component\Validator\ExecutionContextInterface;
use Acme\BlogBundle\Entity\Author;

class MyStaticValidatorClass
The Callback constraint does *not* support global callback functions or
Copy link
Member

Choose a reason for hiding this comment

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

I guess, this line needs to be removed or is to be completed.

It is *not* possible to specify a global function or a :term:`service`
method as callback. To validate using a service, you should
:doc:`create a custom validation constraint</cookbook/validation/custom_constraint>`
Copy link
Member

Choose a reason for hiding this comment

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

a space needs to be added after constraint

and add that new constraint to your class.

When configuring the constraint via PHP, you can also pass a closure to the
constructor of the Callback constraint::

// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;

use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
public static function isAuthorValid(Author $author, ExecutionContextInterface $context)
{
$callback = function ($object, ExecutionContextInterface $context) {
// ...
}
};

$metadata->addConstraint(new Assert\Callback($callback));
}
}

Options
-------

callback
~~~~~~~~

**type**: ``string``, ``array`` or ``Closure`` [:ref:`default option<validation-default-option>`]
Copy link
Member

Choose a reason for hiding this comment

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

add space after option


The callback option accepts three different formats for specifying the
callback method:

* A **string** containing the name of a concrete or static method.
Copy link
Member

Choose a reason for hiding this comment

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

I think we usually use semicolons instead of full stops instead of all but the last list items.


* An array callable with the format ``array('<Class>', '<method>')``.

* A closure.

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

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