Symfony не может проверить форму сбора

1

Я работаю над формой коллекции, которая называется целями, пользователь может добавить столько целей, сколько захочет, эта часть работает нормально, я могу показать/добавить/изменить/удалить цели просто отлично

Изображение 174551

Проблема заключается в том, как проверить данные. В форме есть поле goal target (целое) и saved to date поле saved to date (целое).

Правило - это значение, saved to date не может быть больше goal target а для этого я создал пользовательскую проверку и этот класс выбирается при отправке формы.

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 кажется, что мне нужно добавить ограничение Valid, которое у меня есть в validation.yml.

goals:
    - Valid:

Проблема 1

Предположим, что когда я ввожу saved to date которая больше goal target против первой цели, вместо получения ошибки только против этой цели я получаю ошибку против обеих целей.

ПРИМЕЧАНИЕ. Вторая ошибка не должна быть там, где 8000 меньше 20000 Изображение 174551

Проблема 2

Предположим, что против обеих целей я saved to date больше goal target а затем вижу 2 ошибки против каждого поля.

Изображение 174551

Это мой шаблон просмотра

{% 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(),
    ));
}

На данный момент я довольно неуловим, поскольку я потратил часы, пытаясь решить это, я буду очень признателен за любую помощь в этом.

Теги:
symfony-forms
symfony-2.7
formcollection

2 ответа

0
Лучший ответ

Наконец, я смог решить проблему.

  1. При создании пользовательских проверок, и вам нужен доступ ко всему классу, вам нужно добавить следующий фрагмент кода в свой класс Constraint. В моем случае это SavedToDate и я добавлял его в SavedToDateValidator что было неправильно.

    public function getTargets()
    {
        return self::CLASS_CONSTRAINT;
    }
    
  2. Чтобы убедиться, что ошибки проверки корректно отображаются в поле во время работы с формой сбора, мне пришлось улучшить функцию validate() пользовательского Validator SavedToDateValidator, благодаря @Richard для подсказки.

    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() ошибку в поле, где происходит нарушение, у меня не было этого раньше, и это привело к отображению сообщений об ошибках по всем полям скорее, только против поля, в котором действительно была ошибка. Параметр внутри atpath('fieldname') - это имя свойства, с которым вы хотите связать ошибку. Но для того, чтобы заставить это работать, вам также необходимо отключить функцию error_bubbling, чтобы ошибки не передавались в родительскую форму.

        $builder 
            ->add('goals', 'collection', array(
                  'type' => new GoalType(),
                  'allow_add' => true,
                  'by_reference' => false,
                  'error_bubbling' => false
                    ));
    

Это решение сработало для меня, и я должен признать, что это было действительно забавно работать над этим, все взволновало меня.

  • 1
    Хорошие вещи, рад, что вы получили это отсортировано!
2

Это ограничение уровня класса, и оно срабатывает для каждого экземпляра вашего целевого класса, который вы сохраняете из своей формы.

Поскольку вы повторяете все свои объекты в валидаторе (почему?) Для каждого экземпляра вашего целевого класса, вы будете проверять все ваши объекты цели, что не является идеальным (для 2x объекта вы будете проверять каждый объект 2x, для 3x сущность, вы проверите каждый объект 3x и т.д.).

Обратите внимание, что значение $ здесь - это ваш объект класса, поэтому нет необходимости смотреть на другие объекты в валидаторе.

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();
            }
        }
    }
}

Прочтите документацию для валидаторов ограничения классов

  • 0
    Пожалуйста, измените пример кода, потому что ConstraintValidator не имеет функции getTargets() . Только ограничение делает.
  • 1
    @DelphiLynx отметил и изменил

Ещё вопросы

Сообщество Overcoder
Наверх
Меню