Отображение ошибок валидации
Изменение шаблона отображения форм и ошибок валидации в Symfony.
Тема пошла о валидации, и я продолжаю эту тему, поскольку столкнулся с одной некрасивой проблемой при отображении форм. Задача: выводить ошибки валидации по одной, а не все сразу.
Стандартно, Symfony проверяет все ошибки в передаваемой форме и выводит их списком над полем где эти ошибки случились. Что если я хочу показывать только одну? На помощь может придти креативный подход к делу, а именно:
- Накатать partial для вывода, где поле за полем будет выводится форма и только одна ошибка для него, первая.
- Выводить все ошибки как есть, и гасить их каким-нибудь Javascript'ом
- Колдовать со списками ошибок на CSS и в итоге получить не совсем кроссбраузерное решение
Всё это можно, но не так интересно программисту, как покопаться во фреймворке и найти способ поизящнее. Я начал искать в классах sfForm, sfWidget и так далее. Нашёл откуда ростут ноги у ошибок и куда они складываются. Обнаружились они в массивах $errors повсеместно в классе sfWidgetFormSchemaFormatter. Массив этот проходит обработку методом unnestErrors. Я решил убить двух зайцев сразу, сделав во-первых своё custom-форматирование полей, во вторых избавиться от лишних ошибок переписав unnestErrors.
Насмотревшись вдоволь на поставляемые с симфонией два шаблона отображения форм я выбрал что-то среднее между ними обоими. Поля будут в табличке, а ошибки в теге span сразу за полями. На span я поставлю стиль position: absolute чтобы их положение никак не влияло на остальную вёрстку и не увечила форму. Я так хочу, ибо не люблю пляшущих форм.
Создаю свой formatter в файле lib/widget/sfWidgetFormSchemaFormatterMy.class.php
class sfWidgetFormSchemaFormatterMy extends sfWidgetFormSchemaFormatter
{
protected
$rowFormat = "<tr>\n <th>%label%</th>\n <td>%field%%error%%help%%hidden_fields%</td>\n</tr>\n",
$errorRowFormat = "",
$helpFormat = '<br />%help%',
$decoratorFormat = "",
$errorListFormatInARow = "%errors%",
$errorRowFormatInARow = "<span class=\"error\">← %error%</span>\n",
$namedErrorRowFormatInARow = "<span class=\"error\">← %name%: %error%</span>\n";
Добавляю обещанный удар серпом по ошибкам валидации.
function unnestErrors($errors, $prefix = '')
{
$newErrors = parent::unnestErrors($errors, $prefix);
$newErrors = array_slice($newErrors, 0,1);
return $newErrors;
}
Здесь остаётся только первая ошибка для каждого поля. Если вам стало не понятно, посмотрите метод sfWidgetFormSchemaFormatter::unnestErrors где происходит основное действо.
Добавляю CSS для красивости:
.error {
position: absolute;
color: red;
padding: 8px;
background: white;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
Проверяю как всё работает.
Было:

Стало:

Кажется, получилось очень даже аппетитно. Всё, что мне нужно теперь чтобы форма выглядела так, это добавить в класс формы одну строчку:
$this->getWidgetSchema()->setFormFormatterName('my');
Спасибо за внимание.
