Symfony无法验证收集表单

时间:2015-11-16 17:56:36

标签: php symfony symfony-forms formcollection symfony-2.7

我正在研究一个名为目标的collection form,用户可以根据需要添加任意数量的目标,这部分工作正常,我可以很好地显示/添加/编辑/删除目标< / p>

enter image description here

我遇到的问题是如何验证数据。在表单上有一个goal target(整数)字段和saved to date(整数)字段。

规则是saved to date的值不能超过goal target,为此,我创建了custom validation,并且在提交表单时正在挑选该类。

SavedToDate.php

namespace MyBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
class SavedToDate extends Constraint
{
    public $message = '"%string%" Saved to date cannot be greater than target date.';
}

SavedToDateValidator.php

namespace MyBundle\Validator\Constraints;

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

class SavedToDateValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
        $values = $this->context->getRoot()->getdata()->getGoals()->getValues();
        foreach($values as $item ){
            $target = $item->getTarget();
            $savedToDate = $item->getReached();
           if ($savedToDate > $target) {
                $this->context->buildViolation($constraint->message)
                    ->setParameter('%string%', $value)
                    ->addViolation();
            }
        }
    }

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

通过阅读symfony文档,我似乎需要在validation.yml中添加约束Valid

goals:
    - Valid:
  

问题1

假设我输入的saved to date大于goal target对抗第一个目标,而不是仅针对该目标获取错误,我会针对这两个目标获得错误。

注意第二个错误不应该存在,因为8000小于20000 enter image description here

  

问题2

假设我同时针对两个目标saved to date大于goal target,那么我会看到针对每个字段的2个错误。

enter image description here

这是我的视图模板

{% for goals in form.goals %}      
        <div class="container-fluid">
            <div class="row">
                <div class="col-lg-12">
                    {% if(form_errors(goals.target))  %}
                        <div class="alert alert-danger" role="alert">{{ form_errors(goals.target) }}</div>
                    {% endif %}
                    {% if(form_errors(goals.reached))  %}
                        <div class="alert alert-danger" role="alert">{{ form_errors(goals.reached) }}</div>
                    {% endif %}
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-2" style="padding-top: 5%">
                <label class="" for="exampleInputEmail2">Goal target</label>
                <div class="form-group input-group">
                    {{ form_widget(goals.target, {'attr': {'class': 'form-control'}}) }}
                </div>


            </div>
            <div class="col-xs-2" style="padding-top: 5%">
                <label class="" for="exampleInputEmail2">Saved to date</label>

                <div class="form-group input-group">
                    {{ form_widget(goals.reached, {'attr': {'class': 'form-control'}}) }}
                </div>
            </div>
            <div class="col-xs-2" style="padding-top: 5%">
                <label class="" for="exampleInputEmail2">Goal deadline</label>

                <div class="form-group input-group">
                    {{ form_widget(goals.deadline, {'attr': {'class': 'form-control dp'}}) }}
                </div>
            </div>
            <div class="col-xs-2" style="padding-top: 5%">
                <label class="" for="exampleInputEmail2">Savings</label>

                <div class="form-group input-group">
                    {{ form_widget(goals.allocated, {'attr': {'class': 'form-control'}}) }}
                </div>

            </div>
        </div>
{% endfor %}

这是我的行动

public function prioritiseGoalsAction(Request $request)
{

    $em = $this->getDoctrine()->getManager();
    //get user id of currently logged in user
    $userId = $this->getUser()->getId();

    //get survey object of currently logged in user
    $userGoalsInfo = $em->getRepository('MyBundle:survey')->findOneByuserID($userId);

    //create the form
    $form = $this->createForm(new GoalsType(), $userGoalsInfo);
    $form->handleRequest($request);

    if ($request->isMethod('POST')) {
        if ($form->isValid()) {
            $em->persist($userGoalsInfo);
            $em->flush();
            $this->get('session')->getFlashBag()->add(
                'notice',
                'Your Goals information has been saved'
            );
            return $this->render('MyBundle:Default/dashboard:prioritise-my-goals.html.twig', array(
                'form' => $form->createView(),
            ));
        }
    }


    return $this->render('MyBundle:Default/dashboard:prioritise-my-goals.html.twig', array(
        'form' => $form->createView(),
    ));
}

此时我很无能为力,因为我花了好几个小时试图解决这个问题,我将非常感谢你们的帮助。

2 个答案:

答案 0 :(得分:2)

这是一个类级别限制,它将针对您从表单中保留的每个目标类实例触发。

因为您为了目标类的每个实例而遍历验证器中的所有对象(为什么?),您将检查所有目标实体,这不是理想的(对于2x实体,您将检查每个实体2x,对于3x实体,您将检查每个实体3x等)。

请注意,此处<$>的$ value是您的类对象,因此无需查看验证程序中的其他实体。

public function validate($value, Constraint $constraint)

您应该编写类似的验证器(我没有检查语法):

class SavedToDateValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
            // value should already be an instance of Goal but you could put in a sanity check like

            if (!$value instanceof Goal) {

                // throw an exception or whatever
            }                

            $target = $value->getTarget();
            $savedToDate = $value->getReached();
            if ($savedToDate > $target) {
                $this->context->buildViolation($constraint->message)
                    ->setParameter('%string%', $value)
                    ->addViolation();
            }
        }
    }
}

阅读class constraint validators

的文档

答案 1 :(得分:0)

最后我能够解决问题。

  1. 创建自定义验证时,您需要访问 您需要在整个课程中添加以下代码 Constraint课程。在我的情况下,这是SavedToDate我是 在SavedToDateValidator中添加它是错误的。

    public function getTargets()
    {
        return self::CLASS_CONSTRAINT;
    }
    
  2. 确保验证错误正确显示 我必须在使用http://www.parallelcodes.com/connect-android-to-ms-sql-database-2/时使用字段 改进自定义validate()的{​​{1}}功能 Validator,感谢@Richard提示。

    SavedToDateValidator

    上述功能的一个重要部分是public function validate($value, Constraint $constraint) { if ($value instanceof Goals) { $target = $value->getTarget(); $savedToDate = $value->getReached(); if ($savedToDate > $target) { $this->context->buildViolation($constraint->message) ->setParameter('%goalname%', $value->getName()) ->setParameter('%reached%', $value->getReached()) ->setParameter('%targetamount%', $value->getTarget()) ->atPath('reached') ->addViolation(); } } } ->atPath('reached')将错误粘贴到违规的字段 是的,我之前没有这个,这导致了显示 对所有字段的错误消息而不是唯一的 错误实际属于的字段。 atPath()中的参数是您要将错误链接到的属性名称。但为了得到这个 工作你还需要关闭collection form     所以错误不会传递给父表单。

    atpath('fieldname')
  3. 这个解决方案对我有用,我必须承认在它上面工作非常有趣,让我很兴奋。