Skip to content

Commit

Permalink
[Validator] Renamed Condition to Expression and added possibility to …
Browse files Browse the repository at this point in the history
…set it onto properties
  • Loading branch information
webmozart authored and fabpot committed Sep 19, 2013
1 parent a3b3a78 commit d4ebbfd
Show file tree
Hide file tree
Showing 13 changed files with 405 additions and 65 deletions.
Expand Up @@ -48,6 +48,9 @@ public function load(array $configs, ContainerBuilder $container)
// will be used and everything will still work as expected.
$loader->load('translation.xml');

// Property access is used by both the Form and the Validator component
$loader->load('property_access.xml');

$loader->load('debug_prod.xml');

if ($container->getParameter('kernel.debug')) {
Expand Down
4 changes: 0 additions & 4 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
Expand Up @@ -10,7 +10,6 @@
<parameter key="form.factory.class">Symfony\Component\Form\FormFactory</parameter>
<parameter key="form.extension.class">Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension</parameter>
<parameter key="form.type_guesser.validator.class">Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser</parameter>
<parameter key="property_accessor.class">Symfony\Component\PropertyAccess\PropertyAccessor</parameter>
</parameters>

<services>
Expand Down Expand Up @@ -54,9 +53,6 @@
<argument type="service" id="validator.mapping.class_metadata_factory" />
</service>

<!-- PropertyAccessor -->
<service id="property_accessor" class="%property_accessor.class%" />

<!-- CoreExtension -->
<service id="form.type.form" class="Symfony\Component\Form\Extension\Core\Type\FormType">
<argument type="service" id="property_accessor"/>
Expand Down
@@ -0,0 +1,14 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<parameters>
<parameter key="property_accessor.class">Symfony\Component\PropertyAccess\PropertyAccessor</parameter>
</parameters>

<services>
<service id="property_accessor" class="%property_accessor.class%" />
</services>
</container>
Expand Up @@ -17,6 +17,7 @@
<parameter key="validator.validator_factory.class">Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory</parameter>
<parameter key="validator.mapping.loader.xml_files_loader.mapping_files" type="collection" />
<parameter key="validator.mapping.loader.yaml_files_loader.mapping_files" type="collection" />
<parameter key="validator.expression.class">Symfony\Component\Validator\Constraints\ExpressionValidator</parameter>
</parameters>

<services>
Expand Down Expand Up @@ -63,5 +64,10 @@
<service id="validator.mapping.loader.yaml_files_loader" class="%validator.mapping.loader.yaml_files_loader.class%" public="false">
<argument>%validator.mapping.loader.yaml_files_loader.mapping_files%</argument>
</service>

<service id="validator.expression" class="%validator.expression.class%">
<argument type="service" id="property_accessor" />
<tag name="validator.constraint_validator" alias="validator.expression" />
</service>
</services>
</container>
32 changes: 28 additions & 4 deletions src/Symfony/Component/Validator/ConstraintValidatorFactory.php
Expand Up @@ -11,29 +11,53 @@

namespace Symfony\Component\Validator;

use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Validator\Constraints\ExpressionValidator;

/**
* Default implementation of the ConstraintValidatorFactoryInterface.
*
* This enforces the convention that the validatedBy() method on any
* Constrain will return the class name of the ConstraintValidator that
* should validate the Constraint.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface
{
protected $validators = array();

/**
* @var PropertyAccessorInterface
*/
private $propertyAccessor;

public function __construct(PropertyAccessorInterface $propertyAccessor = null)
{
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
}

/**
* {@inheritDoc}
*/
public function getInstance(Constraint $constraint)
{
$className = $constraint->validatedBy();

if (!isset($this->validators[$className]) || $className === 'Symfony\Component\Validator\Constraints\CollectionValidator') {
$this->validators[$className] = new $className();
// The second condition is a hack that is needed when CollectionValidator
// calls itself recursively (Collection constraints can be nested).
// Since the context of the validator is overwritten when initialize()
// is called for the nested constraint, the outer validator is
// acting on the wrong context when the nested validation terminates.
//
// A better solution - which should be approached in Symfony 3.0 - is to
// remove the initialize() method and pass the context as last argument
// to validate() instead.
if (!isset($this->validators[$className]) || 'Symfony\Component\Validator\Constraints\CollectionValidator' === $className) {
$this->validators[$className] = 'validator.expression' === $className
? new ExpressionValidator($this->propertyAccessor)
: new $className();
}

return $this->validators[$className];
Expand Down
50 changes: 0 additions & 50 deletions src/Symfony/Component/Validator/Constraints/ConditionValidator.php

This file was deleted.

Expand Up @@ -17,32 +17,42 @@
* @Annotation
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class Condition extends Constraint
class Expression extends Constraint
{
public $message = 'This value is not valid.';
public $condition;
public $expression;

/**
* {@inheritDoc}
*/
public function getDefaultOption()
{
return 'condition';
return 'expression';
}

/**
* {@inheritDoc}
*/
public function getRequiredOptions()
{
return array('condition');
return array('expression');
}

/**
* {@inheritDoc}
*/
public function getTargets()
{
return self::CLASS_CONSTRAINT;
return array(self::CLASS_CONSTRAINT, self::PROPERTY_CONSTRAINT);
}

/**
* {@inheritDoc}
*/
public function validatedBy()
{
return 'validator.expression';
}
}
@@ -0,0 +1,82 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Validator\Constraints;

use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\Validator\Exception\RuntimeException;

/**
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bschussek@symfony.com>
*/
class ExpressionValidator extends ConstraintValidator
{
/**
* @var PropertyAccessorInterface
*/
private $propertyAccessor;

/**
* @var ExpressionLanguage
*/
private $expressionLanguage;

public function __construct(PropertyAccessorInterface $propertyAccessor)
{
$this->propertyAccessor = $propertyAccessor;
}

/**
* {@inheritDoc}
*/
public function validate($value, Constraint $constraint)
{
if (null === $value || '' === $value) {
return;
}

$variables = array();

if (null === $this->context->getPropertyName()) {
$variables['this'] = $value;
} else {
// Extract the object that the property belongs to from the object
// graph
$path = new PropertyPath($this->context->getPropertyPath());
$parentPath = $path->getParent();
$root = $this->context->getRoot();

$variables['value'] = $value;
$variables['this'] = $parentPath ? $this->propertyAccessor->getValue($root, $parentPath) : $root;
}

if (!$this->getExpressionLanguage()->evaluate($constraint->expression, $variables)) {
$this->context->addViolation($constraint->message);
}
}

private function getExpressionLanguage()
{
if (null === $this->expressionLanguage) {
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
}
$this->expressionLanguage = new ExpressionLanguage();
}

return $this->expressionLanguage;
}
}
21 changes: 21 additions & 0 deletions src/Symfony/Component/Validator/Exception/RuntimeException.php
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Validator\Exception;

/**
* Base RuntimeException for the Validator component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

0 comments on commit d4ebbfd

Please sign in to comment.