Symfony - 对Collection表单中的多个元素进行验证

时间:2014-12-09 15:18:19

标签: php forms symfony entity

因此,我有一个包含集合的表单,用户可以向此集合添加新项目。我在这个集合中的实体有一个id和一个整数。我想验证用户不能添加一个具有比第一个更低的整数的项目。

例如,表单为空,用户通过单击“添加”按钮将记录添加到集合中。他在输入字段中输入100并添加另一条记录并在输入字段中输入50。当他试图提交时,我希望第二条记录出错,因为50低于100但是他在列表中添加了它。

以下是我的FormTypes和Entites

class ItemType extends AbstractType
{    
    public function buildForm(FormBuilderInterface $builder, array $options)
    {        
        $builder->add('value');
    }
}

class ItemCollectionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {   
        $builder->add('items', 'collection', array(
            'type' => new ItemType(),
            'allow_add'    => true,
            'allow_delete' => true,
            'by_reference' => false,
            'label' => false,
        ));

        $builder->add('save');
    }
}

class Item
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="integer", nullable=true)
     */
    protected $value;
}

class List
{
    /**
     * @ORM\OneToMany(targetEntity="Item", cascade={"persist"})
     * @Assert\Valid
     **/
    private $items;
}

我想我可以在Controller中进行验证,但我想在我的实体中使用验证或自定义验证。

** 编辑 **

我也可以做DonCallisto所建议的但是我试图在问题的特定行上得到错误而不仅仅像他建议的那样在全局类中出现问题。

我找不到如何将propertyPath添加到问题中的项目,我试图使用下面的代码,但似乎它仍然绑定了类上的错误,而不是像我想的那样绑定项目本身。

$context->addViolationAt(
    $context->getPropertyPath().'.items.data.value',
    'Your violation message'
);

它也没有考虑当前元素。

以下代码也无效。在这里,我只是为了测试而硬编码数组的密钥。

$context->addViolationAt(
    $context->getPropertyPath().'.items[0].data.value',
    'Your violation message'
);

有人能告诉我如何正确设置路径以访问集合中的子项值吗?

2 个答案:

答案 0 :(得分:2)

我找到了问题的答案,我使用了DonCallisto的部分答案,我将代码从List实体移到了Item,并在我调用的验证器中我CLASS_CONSTRAINT实体上的Item

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

/**
 * @Annotation
 */
class ItemsOrderedValidator extends ConstraintValidator  {

    public function validate($value, Constraint $constraint) {
        foreach ($value->getList()->getItems() as $item) {
            $currentValue = $item->getValue();

            if (isset($prevValue) && $prevValue >= $currentValue && $value === $item) {
                $this->context->buildViolation($constraint->message)
                    ->atPath('value')
                    ->addViolation();
            }
            $prevValue = $currentValue;
        }
    }
}

答案 1 :(得分:1)

如果我正确理解了您的问题,您可以修改您的List实体,如下所示

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

/**
 * //Whatever you have here to declare this as an entity
 * @Assert\Callback(methods={"areItemsOrdered"})
 */
class List
{
  /**
   * @ORM\OneToMany(targetEntity="Item", cascade={"persist"})
   * @Assert\Valid
   **/
   private $items;

   //whatever methods you have here

   public function areItemsOrdered(ExecutionContext $context)
   {
     foreach ($this->items as $item) {
       $current_value = $item->getValue();

       if (!isset($prev_value)) {
         $prev_value = $current_value;
        } else { 
          if ($prev_value > $current_value) {
            $context->addViolation('Your violation message');
          }
        }
     }
   }
}

有关实体回调方法的更多信息:http://symfony.com/doc/2.3/reference/constraints/Callback.html

有关ExecutionContext的更多信息:http://api.symfony.com/2.4/Symfony/Component/Validator/ExecutionContextInterface.html

顺便说一句

通知用户有关错误的正确行为吗?我的意思是,你不能自己分类添加物品吗? 如果是这样,请更新您的答案,我将很乐意为此目的更新我的代码。