Проверка формы Symfony2 на основе двух полей
В настоящее время я занимаюсь разработкой веб-сайта, на котором пользователь может покупать подарочные карты. Я использую трехэтапную форму, используя пакет CraueFormFlow, и все касается шагов. Я могу проверить каждый простой Assert (например, не пустое, электронное письмо, повторные поля и т. Д.), Но я сталкиваюсь с ситуацией, когда пользователь может выбрать 0 подарочных карт и перейти на следующую страницу.
Пользователи могут выбрать количество подарочных карт, которые они хотят купить, используя две отдельные: одну за 25$ подарочные карты и одну за 50$ подарочные карты. Поэтому я не могу просто поставить валидатор, говорящий "значение 0 не разрешено". Валидатор должен запретить пользователю оставлять количество "0" в обеих суммах (25$ и 50$).
Кто-нибудь знает, как сделать пользовательскую проверку, ища значения в двух полях?
Заранее спасибо!
5 ответов
У вас есть много решений для этого.
Самый простой - добавить ограничение Callback в класс вашей модели.
Еще один способ сделать это - создать собственное ограничение и связанный с ним валидатор. У вас есть кулинарная книга, объясняющая, как создать пользовательское ограничение проверки. Это лучший подход для этого.
Поскольку ваше ограничение относится не к свойству, а к классу, вы должны указать его, переопределяя ->getTargets()
метод вашего класса ограничений:
class MyConstraint extends Constraint
{
// ...
public function getTargets()
{
return Constraint::CLASS_CONSTRAINT;
}
}
Таким образом, значение передается как $value
аргумент ->isValid()
Метод будет содержать значения всего класса, а не только одного свойства.
Если у вас нет класса данных, присоединенного к вашей форме, вы можете реализовать зависимые ограничения в формах, подобных этой:
$startRangeCallback = function ($object, ExecutionContextInterface $context) use ($form)
{
$data = $form->getData();
$rangeEnd = $data['range_end'];
if($object && $rangeEnd){
if ($object->getTimestamp() > $rangeEnd->getTimestamp()) {
$context->addViolation('Start date should be before end date!', array(), null);
}
}
};
$form->add('range_start', 'bootstrap_datepicker', array(
'format' => 'dd-MM-yyyy',
'required' => false,
'attr' => array('class' => "col-xs-2"),
'calendar_weeks' => true,
'clear_btn' => true,
'constraints' => array(
new Callback(array($startRangeCallback)),
)
)
);
$form->add('range_end', 'bootstrap_datepicker', array(
'format' => 'dd-MM-yyyy',
'required' => false,
'attr' => array('class' => "col-xs-2"),
'calendar_weeks' => true,
'clear_btn' => true,
)
);
Вот как я сделал это в моих ограничениях проверки, чтобы проверить действительность кредитной карты со свойствами месяца и года истечения срока действия.
В этом классе я проверяю значение свойства expirationYear и сравниваю его со значением свойства expirationMonth, полученного из contextObject.
/**
* Method to validate
*
* @param string $value Property value
* @param \Symfony\Component\Validator\Constraint $constraint All properties
*
* @return boolean
*/
public function validate($value, Constraint $constraint)
{
$date = getdate();
$year = (string) $date['year'];
$month = (string) $date['mon'];
$yearLastDigits = substr($year, 2);
$monthLastDigits = $month;
$otherFieldValue = $this->context->getRoot()->get('expirationMonth')->getData();
if (!empty($otherFieldValue) && ($value <= $yearLastDigits) &&
($otherFieldValue <= $monthLastDigits)) {
$this->context->addViolation(
$constraint->message,
array('%string%' => $value)
);
return false;
}
return true;
}
Конечно, вы должны авторизовать ограничения класса и свойств в вашем методе getTargets, сформировать основной файл ограничений.
/**
* Get class constraints and properties
*
* @return array
*/
public function getTargets()
{
return array(self::CLASS_CONSTRAINT, self::PROPERTY_CONSTRAINT);
}
Дополнительные объяснения и полный учебник здесь: http://creativcoders.wordpress.com/2014/07/19/symfony2-two-fields-comparison-with-custom-validation-constraints/
Используйте Регулярное выражение, чтобы предотвратить Ноль
В своем классе Entity запишите приведенную ниже функцию переопределения и укажите свойство, которое необходимо проверить.
Приведенный ниже пример предназначен для проверки пин-кода. Здесь, в поле пин-кода, я допускаю только комбинации чисел от 0-9 до 10 цифр.
"^ \ d + $" - это регулярное выражение, которое я использовал для предотвращения появления других символов.
Для переопределения этой функции вы должны включить следующие классы
use Symfony\Component\Validator\Mapping\ClassMetadata;// for overriding function loadValidatorMetadata()
use Symfony\Component\Validator\Constraints\NotBlank;// for notblank constrain
use Symfony\Component\Validator\Constraints\Email;//for email constrain
use Symfony\Component\Validator\Constraints\MinLength;// for minimum length
use Symfony\Component\Validator\Constraints\MaxLength; // for maximum length
use Symfony\Component\Validator\Constraints\Choice; // for choice fields
use Symfony\Component\Validator\Constraints\Regex; // for regular expression
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('pincode', new NotBlank(array('message' => 'Does not blank')));
$metadata->addPropertyConstraint('pincode', new Regex(array('pattern'=>'/^\d+$/','message' => 'must be number')));
$metadata->addPropertyConstraint('pincode', new MaxLength(array('limit'=>'6','message' => 'must maximum 6 digits')));
$metadata->addPropertyConstraint('pincode', new MinLength(array('limit'=>'6','message' => 'must minimum 6 digits')));
}
Не забывай это все должны
включен в ваш класс Entity
что вы должны подтвердить. Так что в вашем случае используйте правильное регулярное выражение, которое не допускает '0'.
Удачного кодирования
Я бы предложил использовать ограничение Expression. Это ограничение может быть применено к полю формы или (предпочтительно) в сущности:
/**
* @var int
* @Assert\Type(type="integer")
*/
private $amountGiftCards25;
/**
* @var int
* @Assert\Type(type="integer")
* @Assert\Expression(expression="this.getAmountGiftCards25() > 0 or value > 0", message="Please choose amount of gift cards.")
*/
private $amountGiftCards50;