Permalink
Browse files

[Validator] Simplified usage of the Callback constraint

  • Loading branch information...
1 parent 98c0d38 commit cccb1db2b2d9a6a96b7c005737ba266a3d9f2352 @webmozart webmozart committed Sep 26, 2013
View
@@ -425,6 +425,64 @@ UPGRADE FROM 2.x to 3.0
private $property;
```
+ * The option "methods" of the `Callback` constraint was removed. You should
+ use the option "callback" instead. If you have multiple callbacks, add
+ multiple callback constraints instead.
+
+ Before (YAML):
+
+ ```
+ constraints:
+ - Callback: [firstCallback, secondCallback]
+ ```
+
+ After (YAML):
+
+ ```
+ constraints:
+ - Callback: firstCallback
+ - Callback: secondCallback
+ ```
+
+ When using annotations, you can now put the Callback constraint directly on
+ the method that should be executed.
+
+ Before (Annotations):
+
+ ```
+ use Symfony\Component\Validator\Constraints as Assert;
+ use Symfony\Component\Validator\ExecutionContextInterface;
+
+ /**
+ * @Assert\Callback({"callback"})
+ */
+ class MyClass
+ {
+ public function callback(ExecutionContextInterface $context)
+ {
+ // ...
+ }
+ }
+ ```
+
+ After (Annotations):
+
+ ```
+ use Symfony\Component\Validator\Constraints as Assert;
+ use Symfony\Component\Validator\ExecutionContextInterface;
+
+ class MyClass
+ {
+ /**
+ * @Assert\Callback
+ */
+ public function callback(ExecutionContextInterface $context)
+ {
+ // ...
+ }
+ }
+ ```
+
### Yaml
* The ability to pass file names to `Yaml::parse()` has been removed.
@@ -22,26 +22,52 @@
*/
class Callback extends Constraint
{
+ /**
+ * @var string|callable
+ *
+ * @since 2.4
+ */
+ public $callback;
+
+ /**
+ * @var array
+ *
+ * @deprecated Deprecated since version 2.4, to be removed in Symfony 3.0.
+ */
public $methods;
/**
- * {@inheritDoc}
+ * {@inheritdoc}
*/
- public function getRequiredOptions()
+ public function __construct($options = null)
{
- return array('methods');
+ // Invocation through annotations with an array parameter only
+ if (is_array($options) && 1 === count($options) && isset($options['value'])) {
+ $options = $options['value'];
+ }
+
+ if (is_array($options) && !isset($options['callback']) && !isset($options['methods']) && !isset($options['groups'])) {
+ if (is_callable($options)) {
+ $options = array('callback' => $options);
+ } else {
+ // BC with Symfony < 2.4
+ $options = array('methods' => $options);
+ }
+ }
+
+ parent::__construct($options);
}
/**
- * {@inheritDoc}
+ * {@inheritdoc}
*/
public function getDefaultOption()
{
- return 'methods';
+ return 'callback';
}
/**
- * {@inheritDoc}
+ * {@inheritdoc}
*/
public function getTargets()
{
@@ -34,13 +34,20 @@ public function validate($object, Constraint $constraint)
return;
}
+ if (null !== $constraint->callback && null !== $constraint->methods) {
+ throw new ConstraintDefinitionException(
+ 'The Callback constraint supports either the option "callback" ' .
+ 'or "methods", but not both at the same time.'
+ );
+ }
+
// has to be an array so that we can differentiate between callables
// and method names
- if (!is_array($constraint->methods)) {
+ if (null !== $constraint->methods && !is_array($constraint->methods)) {
throw new UnexpectedTypeException($constraint->methods, 'array');
}
- $methods = $constraint->methods;
+ $methods = $constraint->methods ?: array($constraint->callback);
foreach ($methods as $method) {
if (is_array($method) || $method instanceof \Closure) {
@@ -54,7 +61,13 @@ public function validate($object, Constraint $constraint)
throw new ConstraintDefinitionException(sprintf('Method "%s" targeted by Callback constraint does not exist', $method));
}
- $object->$method($this->context);
+ $reflMethod = new \ReflectionMethod($object, $method);
+
+ if ($reflMethod->isStatic()) {
+ $reflMethod->invoke(null, $object, $this->context);
+ } else {
+ $reflMethod->invoke($object, $this->context);
+ }
}
}
}
@@ -12,6 +12,7 @@
namespace Symfony\Component\Validator\Mapping\Loader;
use Doctrine\Common\Annotations\Reader;
+use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Exception\MappingException;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\GroupSequence;
@@ -63,7 +64,12 @@ public function loadClassMetadata(ClassMetadata $metadata)
foreach ($reflClass->getMethods() as $method) {
if ($method->getDeclaringClass()->name == $className) {
foreach ($this->reader->getMethodAnnotations($method) as $constraint) {
- if ($constraint instanceof Constraint) {
+ if ($constraint instanceof Callback) {
+ $constraint->callback = $method->getName();
+ $constraint->methods = null;
+
+ $metadata->addConstraint($constraint);
+ } elseif ($constraint instanceof Constraint) {
if (preg_match('/^(get|is)(.+)$/i', $method->name, $matches)) {
$metadata->addGetterConstraint(lcfirst($matches[2]), $constraint);
} else {
Oops, something went wrong.

0 comments on commit cccb1db

Please sign in to comment.