Я работаю над форма сбора который называется цели, пользователь может добавить столько целей, сколько он хочет, эта часть работает нормально, я могу показать / добавить / редактировать / удалить цели просто отлично
Проблема у меня заключается в том, как проверить данные. На форме есть goal target
(целое) поле и 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 мне кажется, что мне нужно добавить ограничение действительный который у меня внутри validation.yml
,
goals:
- Valid:
Проблема 1
Предположим, когда я вхожу saved to date
который больше чем goal target
против первой цели, вместо того, чтобы получать ошибку только против этой цели, я получаю ошибку против обеих целей.
НОТА Второй ошибки быть не должно, так как 8000 меньше 20000
Проблема 2
Предположим, против обеих целей, которые я ставлю saved to date
лучше чем goal target
Я тогда вижу 2 ошибки против каждого поля.
Это мой вид шаблона
{% 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(),
));
}
На данный момент я довольно невежественен, поскольку потратил часы, пытаясь решить эту проблему, я буду очень признателен за любую помощь в этом.
Это ограничение уровня класса, и оно будет срабатывать для каждого экземпляра класса цели, который вы сохраняете в своей форме.
Поскольку вы перебираете все свои объекты в валидаторе (почему?) Для каждого экземпляра вашего класса цели, вы будете проверять все ваши целевые объекты, что не идеально (для 2x сущностей вы будете проверять каждую сущность 2x, для 3x сущность, вы будете проверять каждую сущность 3 раза и т. д.).
Обратите внимание, что здесь значение $ ваш объект класса поэтому нет необходимости смотреть на другие объекты в валидаторе.
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();
}
}
}
}
Прочитайте документацию для валидаторы ограничения класса
Наконец-то я смог решить проблему.
При создании пользовательских проверок а также вам нужен доступ к
Весь класс вам нужно добавить следующий кусок кода в вашем
Constraint
учебный класс. В моем случае это SavedToDate
и я был
добавив его в SavedToDateValidator
что было не так.
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
Чтобы убедиться, что ошибки проверки корректно отображаются на
поля при работе с форма сбора, Мне пришлось
улучшить мой 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
));
Это решение сработало для меня, и я должен признать, что работать над ним было действительно весело, я был в восторге.