Symfony2 Form Validator - 在刷新之前比较旧值和新值

And*_*ndy 16 php validation entity symfony doctrine-orm

我想知道是否有一种方法可以在刷新之前比较实体内的验证器中的旧值和新值.

我有一个Server实体,可以呈现一个形式.该实体具有对关系status(N-> 1),其中,当状态从UnrackedRacked,需要检查SSH和FTP对服务器的访问.如果未实现访问,则验证器应该失败.

我已映射验证程序回调方法isServerValid()的内Server如这里所描述的实体 http://symfony.com/doc/current/reference/constraints/Callback.html.我可以通过显然访问'新'值$this->status,但我怎样才能获得原始值?

在伪代码中,这样的东西:

public function isAuthorValid(ExecutionContextInterface $context)
{
    $original = ... ; // get old values
    if( $this->status !== $original->status && $this->status === 'Racked' && $original->status === 'Unracked' )
    {
        // check ftp and ssh connection
        // $context->addViolationAt('status', 'Unable to connect etc etc');
    }
}
Run Code Online (Sandbox Code Playgroud)

提前致谢!

小智 33

Symfony 2.5的完整示例(http://symfony.com/doc/current/cookbook/validation/custom_constraint.html)

在此示例中,实体"NoDecreasingInteger"的字段"integerField"的新值必须高于存储值.

创建约束:

// src/Acme/AcmeBundle/Validator/Constraints/IncrementOnly.php;
<?php
namespace Acme\AcmeBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class IncrementOnly extends Constraint
{
  public $message = 'The new value %new% is least than the old %old%';

  public function getTargets()
  {
    return self::CLASS_CONSTRAINT;
  }

  public function validatedBy()
  {
    return 'increment_only';
  }
}
Run Code Online (Sandbox Code Playgroud)

创建约束验证器:

// src/Acme/AcmeBundle/Validator/Constraints/IncrementOnlyValidator.php
<?php
namespace Acme\AcmeBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

use Doctrine\ORM\EntityManager;

class IncrementOnlyValidator extends ConstraintValidator
{
  protected $em;

  public function __construct(EntityManager $em)
  {
    $this->em = $em;
  }

  public function validate($object, Constraint $constraint)
  {
    $new_value = $object->getIntegerField();

    $old_data = $this->em
      ->getUnitOfWork()
      ->getOriginalEntityData($object);

    // $old_data is empty if we create a new NoDecreasingInteger object.
    if (is_array($old_data) and !empty($old_data))
      {
        $old_value = $old_data['integerField'];

        if ($new_value < $old_value)
          {
            $this->context->buildViolation($constraint->message)
              ->setParameter("%new%", $new_value)
              ->setParameter('%old%', $old_value)
              ->addViolation();
          }
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

将验证器绑定到实体:

// src/Acme/AcmeBundle/Resources/config/validator.yml
Acme\AcmeBundle\Entity\NoDecreasingInteger:
  constraints:
     - Acme\AcmeBundle\Validator\Constraints\IncrementOnly: ~
Run Code Online (Sandbox Code Playgroud)

将EntityManager注入IncrementOnlyValidator:

// src/Acme/AcmeBundle/Resources/config/services.yml
services:
   validator.increment_only:
        class: Acme\AcmeBundle\Validator\Constraints\IncrementOnlyValidator
        arguments: ["@doctrine.orm.entity_manager"]
        tags:
            - { name: validator.constraint_validator, alias: increment_only }
Run Code Online (Sandbox Code Playgroud)

  • 好主意,但它不适用于集合,因为 $object 属性中的集合与 $old_data 中的集合相同(相同的引用)。因此,如果元素在表单中被删除,您将不会获得旧值。 (2认同)

Nic*_*ich 6

访问symfony2中自定义验证程序内的EntityManager

您可以检查控制器操作中的先前值...但这不是一个干净的解决方案!

normal form-validation只能访问绑定到表单的数据...默认情况下不能访问"previous"数据.

您尝试使用的回调约束无法访问容器或任何其他服务...因此您无法轻松访问实体管理器(或任何以前的数据提供程序)以检查以前的值.

你需要的是一个自定义的验证器一流水平.需要类级别,因为如果要获取实体,则需要访问整个对象而不仅仅是单个值.

验证器本身可能如下所示:

namespace Vendor\YourBundle\Validation\Constraints;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class StatusValidator extends ConstraintValidator
{
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function validate($status, Constraint $constraint)
    {

        $em = $this->container->get('doctrine')->getEntityManager('default');

        $previousStatus = $em->getRepository('YourBundle:Status')->findOneBy(array('id' => $status->getId()));

        // ... do something with the previous status here

        if ( $previousStatus->getValue() != $status->getValue() ) {
            $this->context->addViolationAt('whatever', $constraint->message, array(), null);
        }
    }

    public function getTargets()
    {
        return self::CLASS_CONSTRAINT;
    }

    public function validatedBy()
    {
       return 'previous_value';
    }
}
Run Code Online (Sandbox Code Playgroud)

...然后将验证器注册为服务并将其标记为验证器

services:
    validator.previous_value:
        class: Vendor\YourBundle\Validation\Constraints\StatusValidator

        # example! better inject only the services you need ... 
        # i.e. ... @doctrine.orm.entity_manager

        arguments: [ @service_container ]         
        tags:
            - { name: validator.constraint_validator, alias: previous_value }
Run Code Online (Sandbox Code Playgroud)

最后使用约束为您的状态实体(即使用注释)

use Vendor\YourBundle\Validation\Constraints as MyValidation;

/**
 * @MyValidation\StatusValidator
 */
class Status 
{
Run Code Online (Sandbox Code Playgroud)

  • 快速注意:请注意,由于内部对象缓存,Doctrine可能会为`$ previousState`提供完全相同的对象.有我的问题.:[ (4认同)