В настоящее время я провожу некоторые эксперименты с формами Zend и пользовательским рендерингом, и мне было интересно, как обрабатывать ошибки форм.
Конкретный пример:
скажем, я хочу визуализировать набор полей с радиоэлементом, который имеет 3 варианта, и только один является правильным. Идея состоит в том, чтобы обернуть каждую опцию в некоторый div для применения классов начальной загрузки, а затем выделить опцию (добавив класс has-error), когда (пользовательская) проверка не удалась.
Рендеринг может быть что-то вроде этого (здесь непосредственно в сценарии представления):
foreach($form->getFieldsets() as $fieldset) {
echo "<fieldset><legend>".$fieldset->getName()."</legend>";
foreach ($fieldset as $element) {
$opt=$element->getOption('value_options');
foreach ($opt as $key=>$val) {
if($element->getValue()==$key){$check="checked='checked'";}else{$check="";}
echo"<div class='form-group (HERE ADD `has-error` CLASS IF VALIDATION FAILS)'>".
"<label class='custom-control'>".
"<input type='radio' class='custom-control-input' name='".$element->getName()."' value='".$key."' ".$check.">".
"<span class='custom-control-indicator'></span>".
"<span class='custom-control-description'>*TEXT*</span>".
"</label>".
"</div>";
}//3rd foreach
}//2nd foreach
echo "</fieldset>
}//1st foreach
И вот вопрос: как определить (после проверки), есть ли у элемента ошибка, чтобы добавить класс ошибки начальной загрузки?
Я думал, что возможное решение может быть:
//custom validator
[...]
public function isValid($value, array $context = null)
{
$this->setValue($value);if ($value==1) {
$this->error(self::MESSAGE1); //Your answer (radio with value=1) is wrong!
$this->error(self::CODE1); //1
return false;
}
[...]
return true;
}
затем:
//updated view script
$messages=$form->getMessages();
foreach($form->getFieldsets() as $fieldset) {
echo "<fieldset><legend>".$fieldset->getName()."</legend>";
foreach ($fieldset as $element) {
$opt=$element->getOption('value_options');
foreach ($opt as $key=>$val) {
if($element->getValue()==$key){$check="checked='checked'";}else{$check=null;}
$field=$fieldset->getName();
$radio=$element->getName();
if(isset($messages[$fieldset][$radio]) && $messages[$fieldset][$radio]==$key){$class='has-error';}else{$class=null;}
echo"<div class='form-group ".$class."'>".
"<label class='custom-control'>".
"<input type='radio' class='custom-control-input' name='".$element->getName()."' value='".$key."' ".$check.">".
"<span class='custom-control-indicator'></span>".
"<span class='custom-control-description'>*TEXT*</span>".
"</label>".
"</div>";
}//3rd foreach
}//2nd foreach
echo "</fieldset>
}//1st foreach
Единственная проблема в том, что $element->getName()
возвращает строку с подобным: "fieldsetName[elementName]"
в то время как $form->getMessages()
возвращает многомерный массив, как fieldsetName=array(elementName=array(/*MESSAGES*/),);
Возможным хаком может быть добавление пользовательского атрибута к определению элемента в классе формы.
Я знаю, что это довольно сыро, но это просто концепция. Как вы думаете? Есть ли лучшее решение для выявления ошибок?
РЕДАКТИРОВАТЬ:
Вероятно, мое первое объяснение не было очень ясным (моя вина).
Для большей ясности ЭТО должно быть (визуальный) ожидаемый результат.
Очевидно, что идея довольно разная, поскольку вся логика (фильтрация / проверка / визуализация /) должна обрабатываться php / zend. Jquery должен только отправлять форму асинхронно (метод ajax + serializeArray) и затем возвращать визуализированное представление (используя новый viewmodel-> setTerminal ()).
Но это не проблема.
Проблема в том, что радиоэлемент расширяет Zend \ Form \ View \ Helper \ FormMultiCheckbox, тогда невозможно обернуть отдельные параметры.
//Module/src/Form/ExampleForm
[...]
$this->add([
'name' => 'radio_element',
'type' => 'radio',
'options' => [
'label' => 'radio_element',
'value_options' => [
1 => 'one',
2 => 'two',
3 => 'three',
],
],
]);
[...]
//the view
echo "<div class='MY_CUSTOM_WRAPPER'>";
echo $this->formRadio($form->get('radio_element'));
echo "</div>";
//Output:
<div class='MY_CUSTOM_WRAPPER'>
<label><input type='radio' name='radio_element' value='1'>one</label>
<label><input type='radio' name='radio_element' value='2'>two</label>
<label><input type='radio' name='radio_element' value='3'>three</label>
</div>
Единственный способ обернуть каждую опцию (насколько я знаю) — это использовать пользовательский помощник (для простоты я фактически обошел этот шаг, действуя непосредственно в скрипт вида).
И вот настоящая проблема: так как три опции НЕ являются ЭЛЕМЕНТАМИ (но являются опциями элемента), мне нужно:
Мое решение (обновлено):
//custom validator
public function isValid($value, array $context = null)
{
$this->setValue($value);
if ($value==1) {
$this->setMessage('Your answer is wrong because...',self::ERRDESC);
$this->error(self::ERRNO); //defined above as %value% -> =1
$this->error(self::ERRDESC);
return false;
}
if ($value==2) {
$this->setMessage('That\'s right!!!',self::ERRDESC);
$this->setMessage("correct".$value,self::ERRNO);
$this->error(self::ERRNO);
$this->error(self::ERRDESC);
return false;
}
if ($value==3) {
$this->setMessage('That\'s really wrong! ',self::ERRDESC);
$this->error(self::ERRNO);//defined above as %value% -> =3
$this->error(self::ERRDESC);
return false;
}
[...]
return true;
}
//view script
foreach($form->getFieldsets() as $fieldset) {
echo "<fieldset><legend>".$fieldset->getName()."</legend>";
foreach ($fieldset as $element) {
$opt=$element->getOption('value_options');
$class2=null;
foreach ($opt as $key=>$val) {
if($element->getValue()==$key){$check="checked='checked'";}else{$check=null;}
$class=null;
if(isset($element->getMessages()['ERRNO'])){
switch($element->getMessages()['ERRNO']){
case $key: $class='is-invalid'; $class2='alert-danger';
break;
case "correct".$key: $class='is-valid'; $class2='alert-success';
break;
}
}
echo"<div class='custom-control custom-radio'>
<input type='radio' id='radio_".$key."' name='".$element->getName()."' class='custom-control-input ".$class."' value='".$key."' ".$check.">
<label class='custom-control-label' for='radio_".$key."'>$val</label>
</div>
";
}//3rd foreach (radio element's options)
echo"<div class='alert $class2' role='alert'>";
echo isset($element->getMessages()['ERRDESC']) ? $element->getMessages()['ERRDESC']:null;
echo "</div>";
}//2nd foreach (radio element)
echo "</fieldset>";
}//1st foreach (fieldset)
Знаете ли вы лучшее решение, чтобы получить и отформатировать параметры радио Signle?
Задача ещё не решена.
Других решений пока нет …