Безвкусный validation php. Валидировали, валидировали… и вывалидировали! Сравниваем валидаторы данных в PHP. Создание экземпляра Validator и валидация данных

Reg.ru: домены и хостинг

Крупнейший регистратор и хостинг-провайдер в России.

Более 2 миллионов доменных имен на обслуживании.

Продвижение, почта для домена, решения для бизнеса.

Более 700 тыс. клиентов по всему миру уже сделали свой выбор.

*Наведите курсор мыши для приостановки прокрутки.

Назад Вперед

Валидация и очистка данных средствами PHP

Безопасность данных является очень важным моментом, который часто недооценивается как разработчиками, так и клиентами. Начиная с PHP 5.2.0 производить очистку и валидацию данных (проверку на соответствие определенным критериям) стало проще с введением фильтрации данных. Сегодня мы рассмотрим способы фильтрации, как использовать фильтры и создадим несколько пользовательских функций.

Введение

Я всегда чувствовал, что писать код на PHP легко, а еще легче писать на PHP плохой код. Широкому распространению PHP в сфере веб-разработки способствовали многие проекты с открытым исходным кодом (open-source) вроде WordPress, Drupal, Magento. Кроме того это и веб-приложения вроде Facebook и т.д. При столь широком применении PHP (динамические веб-сайты, платформы для блоггинга, системы управления контентом, использование в приложениях для электронной коммерции и др.) вероятность столкнуться с "грязной" информацией и небезопасными системами очень велика. Данное руководство покажет некоторые методы очистки и валидации данных с помощью PHP. Мы сфокусируем внимание на нескольких типах входных данных и на том, как использовать PHP-фильтры и пользовательские функции.

Зачем очищать и проверять?

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

Среди основных:

- XSS (Cross-Site Scripting - Межсайтовый скриптинг)

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

- SQL-инъекция

Следующей популярной уязвимостью является другая форма инъекции кода, которая позволяет реализовывать различные виды вредоносного поведения, включая несанкционированный доступ к информации, изменение информации в базе данных либо иное нарушения нормального функционирования веб-приложения. Осуществляется данная атака путем внедрения в запрос произвольного SQL-кода, предназначенного для взаимодействия с базой данных.

- CSRF/XSRF (Cross-Site Request Forgery - Подделка межсайтовых запросов)

Данная уязвимость менее распространенная по сравнению с предыдущими. Обычно такого рода уязвимости возникают при работе с сессиями и cookies и реже - при плохо проверенных и очищенных данных. CSRF может использоваться для выполнения сайтом каких либо запросов без ведома пользователя. Один из известных способов реализации данной атаки - использование неверно сформированного атрибута src у картинки, что приводит к выполнению какого-либо скрипта, а не к отображению картинки.

- Некорректная информация

Некорректная информация сама по себе не является "уязвимостью". Однако такая информация во многих случаях приводит к возникновению ряда проблем как для владельца сайта, так и для администратора баз данных. Зачастую некорректная по структуре информация приводит к нарушениям в работе, особенно если сайт реализован на любительском уровне, не по стандартам а также к сбоям в работе автоматизированных систем, ожидающих для обработки четко структурированные данные в определенном формате.


Перевод диалога к картинке:

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

О Боже, он что, что-то сломал?

Вашего сына действительно зовут Robert"); DROP TABLE students; ?

О, да, мы зовем его Little Bobby Tables

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

А я надеюсь, вы научитесь проверять заносимую в базу данных информацию.

Для наших целей мы будем фокусироваться только на использовании серверных методов повышения безопасности информации, поэтому давайте посмотрим, как определяются термины "sanitization" и "validation" применительно к PHP. Обратимся к руководству по PHP:

"Валидация используется для проверки того, отвечает ли проверяемая информация определенным требованиям. Например, используя FILTER_VALIDATE_EMAIL мы определяем, является ли информация корректным (т.е. верным по структуре) e-mail-адресом, но не изменяем эти данные.

Очистка же подразумевает возможное изменение проверяемой информации, например - удаление нежелательных символов. Скажем, при использовании FILTER_SANITIZE_EMAIL будут удалены символы, которые не должны содержаться в e-mail-адресе. Т.е. в данном случае не происходит проверки корректности адреса (т.е. валидации), а удаляются заведомо неподходящие символы - не более того."

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

Какие фильтры есть у меня?

Все инсталляции PHP не могут быть идентичными. Несмотря на то, что фильтры были введены в PHP 5.2.0, не все инсталляции имеют одинаковый набор фильтров. В большинстве случаев все фильтры, о которых мы будем говорить, будут уже включены в установленный PHP на вашем сервере, но чтобы вы чуть больше знали о фильтрах, мы узнаем о том, что доступно именно на вашем сервере. В исходных кодах приложен файл getfilters.php , который, будучи однажды установленным и запущенным на сервере, отобразит список всех ваших фильтров (как фильтры информации, доступные через функцию filter_var , так и потоковые, доступные через stream_filter_append )

Echo "Data Filters\n

\n\n"; echo "\n"; echo "\n"; foreach(filter_list() as $id =>$filter) { echo "\n"; } echo "
Filter IDFilter Name
$filter".filter_id($filter)."
\n";

Сначала мы получаем массив, содержащий список всех доступных фильтров с помощью функции filter_list , после чего проходим циклом по массиву, выводя на экран имя фильтра и его ID.

Как мне использовать фильтр?

Фильтры PHP для валидации и очистки активируются посредством передачи функции filter_var как минимум двух параметров. В качестве примера давайте применим фильтр очистки для целого числа:

$value = "123abc456def"; echo filter_var($value, FILTER_SANITIZE_NUMBER_INT);

В этом примере у нас есть переменная value , которую мы передаем функции filter_var из расширения PHP Filters Extension, используя фильтр FILTER_SANITIZE_NUMBER_INT. В качестве результата мы получим:

Фильтр очистки для целых числе удаляет все символы, не являющиеся целыми числами, выдавая нам "чистое" целое число. В исходниках вы можете попробовать различные входные данные, и к ним будет применяться ряд общих фильтров. В архив включены различные строки, которые вы можете использовать в качестве тестового материала самостоятельно.

Что делают различные фильтры?

Список ниже неполный, но он содержит большинство фильтров, которые идут в стандартной установке PHP 5.2.0+.

FILTER_VALIDATE_BOOLEAN: Проверяет, является ли переданная информация булевым значением TRUE или FALSE. Если переданное значение - значение не типа Boolean, то возвращается FALSE. Скрипт ниже выведет TRUE для примера с переменной value1 value02 :

$value01 = TRUE; if(filter_var($value01,FILTER_VALIDATE_BOOLEAN)) { echo "TRUE"; } else { echo "FALSE"; } echo "

" $value02 = FALSE; if(filter_var($value02,FILTER_VALIDATE_BOOLEAN)) { echo "TRUE"; } else { echo "FALSE"; }

FILTER_VALIDATE_EMAIL: Проверяет, является ли переданная информация корректным с точки зрения структуры e-mail адресом. Она не проверяет, существует ли этот адрес на самом деле, а только валидность адреса, т.е. правильность его структуры. Скрипт ниже выведет TRUE для примера с переменной value01 и FALSE для примера с переменной value02 (так как не хватает обязательной части со знаком @):

$value01 = "[email protected]"; if(filter_var($value01,FILTER_VALIDATE_EMAIL)) { echo "TRUE"; } else { echo "FALSE"; } echo "

" $value02 = "nettuts"; if(filter_var($value02,FILTER_VALIDATE_EMAIL)) { echo "TRUE"; } else { echo "FALSE"; }

FILTER_VALIDATE_FLOAT: Проверяет, является ли переданное значение числом с плавающей точкой. Скрипт ниже выведет TRUE для примера с переменной value01 и FALSE для примера с переменной value02 (так как разделить "," не разрешен в числах с плавающей точкой):

$value01 = "1.234"; if(filter_var($value01,FILTER_VALIDATE_FLOAT)) { echo "TRUE"; } else { echo "FALSE"; } echo "

" $value02 = "1,234"; if(filter_var($value02,FILTER_VALIDATE_FLOAT)) { echo "TRUE"; } else { echo "FALSE"; }

FILTER_VALIDATE_INT: Проверяет, является ли переданное значение целым числом. Скрипт ниже выведет TRUE для примера с переменной value01 и FALSE для примера с переменной value02 (десятичные числа не являются целыми):

$value01 = "123456"; if(filter_var($value01,FILTER_VALIDATE_INT)) { echo "TRUE"; } else { echo "FALSE"; } echo "

" $value02 = "123.456"; if(filter_var($value02,FILTER_VALIDATE_INT)) { echo "TRUE"; } else { echo "FALSE"; }

FILTER_VALIDATE_IP: Проверяет, является ли переданное значение корректным IP-адресом. Она не проверяет, есть ли ответ от этого адреса, а лишь то, что переданное значение по своей структуре является IP-адресом. Скрипт ниже выведет TRUE для примера с переменной value01 и FALSE для примера с переменной value02 :

$value01 = "192.168.0.1"; if(filter_var($value01,FILTER_VALIDATE_IP)) { echo "TRUE"; } else { echo "FALSE"; } echo "

" $value02 = "1.2.3.4.5.6.7.8.9"; if(filter_var($value02,FILTER_VALIDATE_IP)) { echo "TRUE"; } else { echo "FALSE"; }

FILTER_VALIDATE_URL: Проверяет, является ли переданное значение корректным URL-адресом. Она не проверяет, она не проверяет доступность ресурса, а лишь то, что соблюдена структура URL-адреса. Скрипт ниже выведет TRUE для примера с переменной value01 и FALSE для примера с переменной value02 :

$value01 = "http://net.tutsplus.com"; if(filter_var($value01,FILTER_VALIDATE_URL)) { echo "TRUE"; } else { echo "FALSE"; } echo "

" $value02 = "nettuts"; if(filter_var($value02,FILTER_VALIDATE_URL)) { echo "TRUE"; } else { echo "FALSE"; }

FILTER_SANITIZE_STRING: По умолчанию данный фильтр удаляет любую некорректную или не разрешенную информацию в строке. Например, она удалит любые тэги HTML вроде или из входящей строки:

$value = "alert("TROUBLE HERE");"; echo filter_var($value, FILTER_SANITIZE_STRING);

Данный скрипт удалит тэги и вернет следующее:

Alert("TROUBLE HERE");

FILTER_SANITIZE_ENCODED: Многие программисты используют функцию urlencode() . Данный фильтр по сути выполняет те же функции. Например, следующий пример выполнит кодирование любых спецсимволов и пробелов во входящей строке:

$value = "alert("TROUBLE HERE");"; echo filter_var($value, FILTER_SANITIZE_ENCODED);

Скрипт закодирует пунктуацию, пробелы, скобки и вернет следующее:

%3Cscript%3Ealert%28%27TROUBLE%20HERE%27%29%3B%3C%2Fscript%3E

FILTER_SANITIZE_SPECIAL_CHARS: Данный фильтр по умолчанию производит HTML-кодирование спецсимволов вроде кавычек, амперсандов и скобок. Так как демо-страница не может явно показать это (так как HTML-кодированные спецсимволы будут проинтерпретированы браузером и отображены), вы можете увидеть это, если заглянете в исходный код:

$value = "alert("TROUBLE HERE");"; echo filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);

Произойдет конвертация спецсимволов в их HTML-сущности:

FILTER_SANITIZE_EMAIL: Этот фильтр делает именно то, о чем подумал каждый из нес. Он удаляет из адреса символы, которых не должно быть в адресе (круглые и квадратные скобки, двоеточия и т.п.) Положим, вы случайно добавили в ваш адрес скобки вокруг какой-либо буквы (не спрашивайте, как, включите ваше воображение:))

$value = "t(e)[email protected]"; echo filter_var($value, FILTER_SANITIZE_EMAIL);

Произойдет удаление скобок и вы получите на выходе свой чистый и красивый е-mail:

[email protected]

Это отличный фильтр, который можно использовать в формах для ввода e-mail, особенно в паре с FILTER_VALIDATE_EMAIL, что позволит снизить число ошибок пользователей и предотвратить атаки типа XSS.

FILTER_SANITIZE_URL: Данный фильтр похож на предыдущий. Он удаляет любые символы, недопустимые в URL. К примеру, скажем, в адресе случайно оказался знак "®". Опять же, как он туда попал - сплошная загадка.

$value = "http://net.tuts®plus.com"; echo filter_var($value, FILTER_SANITIZE_URL);

Таким образом мы удалим ненужный знак "®" и получим нормальный адрес:

Http://net.tutsplus.com

FILTER_SANITIZE_NUMBER_INT: Данный фильтр похож на FILTER_VALIDATE_INT, но вместо простой проверки на то, является ли число целым, он еще и удаляет все, что не является целым числом. Отличная вещь, особенно против надоедливых спам-ботов и обманщиков, норовящих ввести в поле какую-нибудь ерунду:

$value01 = "123abc456def"; echo filter_var($value01, FILTER_SANITIZE_NUMBER_INT); echo "
"; $value02 = "1.2.3.4.5.6.7.8.9"; echo filter_var($value02, FILTER_SANITIZE_NUMBER_INT);

123456 123456789

FILTER_SANITIZE_NUMBER_FLOAT: Похож на FILTER_VALIDATE_INT. Точно также позволяет добиться аналогичного эффекта:

$value01 = "123abc456def"; echo filter_var($value01, FILTER_SANITIZE_NUMBER_FLOAT); echo "
"; $value02 = "1.2.3.4.5.6.7.8.9"; echo filter_var($value02, FILTER_SANITIZE_NUMBER_FLOAT);

Оба набора символов преобразуются и на выходе получаем следующую картину:

123456 123456789

$value = "1.23"; echo filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT);

Точка будет удалена и возвращено значение:

Одна из главных причин того, что фильтры FILTER_SANITIZE_NUMBER_FLOAT и FILTER_SANITIZE_INT разделены - это возможность использовать специальный флаг FILTER_FLAG_ALLOW_FRACTION, который идет как третий параметр, передаваемый функции filter_var :

$value = "1.23"; echo filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);

Опции, флаги и контролирующие массивы - Майн Готт!

Флаг, использовавшийся в предыдущем примере - лишь один из способов получить более детальный контроль над типами данных, которые будут подвергаться очистке, определениями ограничителей, тем, как обрабатываются фильтрами массивы и др. Узнать больше про флаги и функции, используемые всвязи с применением фильтров вы можете в руководстве PHP, в части, посвященной Фильтрам - php.net/manual/en/book.filter.php .

Другие методы очистки информации средствами PHP

Сейчас мы рассмотрим несколько ключевых методов очистки данных для предотвращения атаки на ваше приложение. Они особенно актуальны для тех приложений, которые по-прежнему работают на PHP4, так как появились с его релизом.

htmlspecialchars: эта функция PHP преобразует 5 специальных символов в соответствующие HTML-сущности.

Преобразованию подвергаются:

& (амперсанд)
" (двойные кавычки) когда не установлен флаг ENT_NOQUOTES
’ (одинарные кавычки) только когда установлен флаг ENT_QUOTES
< (меньше, чем)
> (больше, чем)

Используется данная функция точно так же, как и любая другая в PHP:

Echo htmlspecialchars("$string");

htmlentities: Подобно функции htmlspecialchars эта функция конвертирует специальные символы в их HTML-сущности. Все отличие в том, что в данном случае конвертируются все спецсимволы, которые могут быть конвертированы. Это достаточно распространенный метод для запутывания (обфускации) e-mail-адресов от спам-ботов, так как далеко не все из них настроены на чтение html-сущностей:

Echo htmlentities("$string");

mysql_real_escape_string: Это функция MySQL, помогающая защититься от атак типа SQL-инъекция. Считается хорошей практикой (а по сути необходимостью) пропускать всю информацию передаваемую SQL-запросу через эту функцию. Она экранирует все опасные спецсимволы, которые могут вызвать проблемы и стать причиной того, что little Bobby Tables уничтожит еще одну таблицу в школьной базе данных.

$query = "SELECT * FROM table WHERE value=".mysql_real_escape_string("$string")." LIMIT 1,1"; $runQuery = mysql_query($query);

Пользовательские функции

Для многих людей встроенных функций и фильтров может оказаться недостаточно. Часто может потребоваться более жесткая и узкая валидация или очистка. Чтобы добиться нужных результатов многие сами пишут функции для валидации данных. В качестве примера можно привести вариант поиска в базе данных значений определенного типа, вроде:

Function checkZipCode($value) { $zipcheck = "SELECT COUNT(*) FROM `database`.`zipcodes` WHERE value="".filter_var(mysql_real_escape_string($value),FILTER_SANITIZE_NUMBER_INT)."""; $count = mysql_query($zipcheck); if($count==1) { return TRUE; } else { return FALSE; } }

Другие пользовательские функции могут быть не связаны с базой напрямую, а заниматься подготовкой информации перед вставкой в базу:

Function cleanString($string) { $detagged = strip_tags($string); if(get_magic_quotes_gpc()) { $stripped = stripslashes($detagged); $escaped = mysql_real_escape_string($stripped); } else { $escaped = mysql_real_escape_string($detagged); } return $escaped; }

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

Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!


Почти всем интерактивные веб-приложения необходимо проверить вводимые данные. Например, в регистрационной форме, вероятно, потребует пароль для подтверждения. Может быть, адрес электронной почты должен быть уникальным. Проверка данных может быть громоздким процессом. К счастью,только не в Laravel.Класс Validator обеспечивает удивительный набор помощников для проверки, максимально облегчая проверку данных. Давайте рассмотрим пример:

Получение массива данных для валидации: $input = Input::all(); Определение правил валидации данных: $rules = array("name" => "required|max:50", "email" => "required|email|unique:users",); Создание экземпляра Validator и валидация данных: $validation = Validator::make($input, $rules); if ($validation->fails()) { return $validation->errors; }

При наличии свойства errors вы получаете доступ к сборщику сообщений, позволяющему легко создавать свои сообщения об ошибках. Конечно же, все правила валидации имеют соощения об ошибках по умолчанию. Стандартные сообщения об ошибках находятся в language/en/validation.php .

Теперь вы знакомы с основными правилами использования класса Validator. Вы готовы к исследованию и изучению правил используемых для проверки ваших данных!

Правила валидации Обязательные данные Проверка на обязательное не пустое значение параметра: "name" => "required" Alpha, Alpha Numeric, & Alpha Dash Проверка на наличие только букв: "name" => "alpha" Проверка на наличие только букв и цифр: "username" => "alpha_num" Проверка на наличие только букв, цифр, тире и символа подчеркивания: "username" => "alpha_dash" Размер Проверка на размер строки строчного атрибута, или диапазон значений числового атрибута: "name" => "size:10" Проверка на диапазон значений: "payment" => "between:10,50"

Примечание: Минимум и максимум включены в диапазон.

Проверка минимального размера атрибута: "payment" => "min:10" Проверка максимального размера атрибута: "payment" => "max:50" Числовые типы Проверка принадлежности атрибута к числовому типу: "payment" => "numeric" Проверка принадлежности атрибута к целочисленному типу: "payment" => "integer" Вхождения и исключения Проверка атрибута на вхождение в массив значений: "size" => "in:small,medium,large" Проверка атрибута на исключение из массива значений: "language" => "not_in:cobol,assembler" Подтверждение

Правило confirmed проверяет, что для данного атрибута есть соответствующее подтверждение * attribute_confirmation *.

Проверка атрибута на подтверждение: "password" => "confirmed"

В этом примере Validator проверяет, что параметр password удовлетворяет условиям password_confirmation из массива валидации.

Акцептирование

Правило accepted проверяет параметр на значение yes или 1 . Это правило проверяет установку обязательных чекбоксов, таких, как, например, флажок согласия с "Условиями предоставления услуг".

Проверка акцептирования: "terms" => "accepted" Соответствия и различия Проверка, что атрибут такой же как, и сравниваемый другой артибут: "token1" => "same:token2" Проверка на то, что атрибут имеет разное значение: "password" => "different:old_password", Регулярные выражения

Правило match проверяет атрибут на удовлетворение регулярному выражению.

Поверка на удовлетворение регулярному выражению: "username" => "match:/+/"; Уникальность и существование Проверка параметра на уникальность в базе данных: "email" => "unique:users"

В этом примере параметр email проверяется на уникальность в таблице users . Необходимо проверить уникальность атрибута другого столбца, кроме этого? Нет проблем:

Указание другого столбца для проверки: "email" => "unique:users,email_address"

Часто, при обновлении записи, вам нужно использовать правило уникальности, но при этом исключить обновляемую запись. Напрмер, вы хотите дать возможность пользователям изменять свои почтовые адреса. Но, когда запускается правило unique , вам нужно пропустить этого пользователя, чтобы не вызвать мнимую ошибку проверки. Это просто:

Игнорирование указанного ID: "email" => "unique:users,email_address,10" Проверка на наличие атрибута в указанной базе данных: "state" => "exists:states" Указание имени столбца для правила exists: "state" => "exists:states,abbreviation" Даты Проверка того, что параметр даты имеет значение до...: "birthdate" => "before:1986-28-05"; Проверка того, что параметр даты имеет значение после...: "birthdate" => "after:1986-28-05";

Примечание: Проверка before и after использует функцию PHP strtotime .

E-Mail адреса Проверка того, что параметр является E-Mail адресом: "address" => "email"

Примечание: Это правило использует встроенный в PHP метод filter_var .

URLs Проверка того, что параметр есть URL: "link" => "url" Проверка того, что параметр есть активный URL: "link" => "active_url"

Примечание: Правило active_url используетs checkdnsr для проверки активности URL.

Загрузки

Правила mimes проверяют, что загружаемый файл соответствует MIME типу. Это правило использует расширение PHP Fileinfo проверяющее содержимое файла и определяющее его тип. Любые расширения файлов, применимые к этому правилу, определяются в config/mimes.php .

Проверка принадлежности файла определенному типу: "picture" => "mimes:jpg,gif"

Примечание: При проверке не забудьте использовать Input::file() или Input::all().

Проверка того, что файл изображение: "picture" => "image" Проверка на размер файла: "picture" => "image|max:100" Запрос сообщения об ошибке

Laravel позволяет работать с сообщениями об ошибках с помощью простого класса - сборщика ошибок. После вызова методов passes или fails экземпляра Validator, вы можете получить доступ к ошибке при помощи свойства errors . Сборщик ошибок имеет простые функции для запроса сообщений об ошибках:

Определение, что атрибут имеет сообщение об ошибке: if ($validation->errors->has("email")) { // The e-mail attribute has errors... } Запрос первого сообщения об ошибке для атрибута: echo $validation->errors->first("email");

Вам может понадобиться обернуть ваше сообщение об ошибке в HTML тэги. Нет проблем. Вызывая:message place-holder, определите формат вторым параметром метода.

Форматирование сообщения об ошибке: echo $validation->errors->first("email", ""); Получение всех сообщений об ошибках для атрибута: $messages = $validation->errors->get("email"); Форматирование всех сообщений об ошибках для аттрибута: $messages = $validation->errors->get("email", ""); Получение всех сообщений об ошибках для всех атрибутов: $messages = $validation->errors->all(); Форматирование всех сообщений об ошибках для всех атрибутов: $messages = $validation->errors->all(""); Прохождение валидации

После того как вы выполнили вашу проверку, нужен простой способ отображения ошибок в представлении. Laravel делает его очень легко. Давайте рассмотрим типичный сценарий. Это может быть определено двумя путями:

Route::get("register", function() { return View::make("user.register"); }); Route::post("register", function() { $rules = array(...); $validation = Validator::make(Input::all(), $rules); if ($validation->fails()) { return Redirect::to("register")->with_errors($validation); } });

Отлично! Итак, мы имеем два простых маршрута для формы регистрации. Один для обработки отображения формы, и один для обработки ввода в форму. В POST маршруте мы проводим некоторые проверки на входе. Если проверка не пройдена, будем преадресовывать обратно в регистрационную форму с указанием ошибок и отображения последних в форме.

Но, обратите внимание, мы явно не связываем ошибки с целью в нашем GET маршруте . Тем не менее, переменная ошибки будет доступна в представлении. Laravel разумно определяет, есть ли ошибки в работе сессии, и если они есть, присоединяет сообщения к представлению. Если ошибок нет, пустой контейнер сообщения об ошибке все равно будет присоединен к представлению. В представлении всегда будет доступен контейнер сообщений об ошибках. Нам нравится облегчать вам жизнь.

Пользовательские сообщения об ошибках

Хотите использовать собственное сообщение об ошибке? Может быть, вы даже хотите использовать пользовательские сообщения об ошибке для данного атрибута и правила. В любом случае, класс Validator позволяет легко это сделать.

Создание массива собственных сообщений об ошибках для Validator: $messages = array("required" => "The:attribute field is required.",); $validation = Validator::make(Input::get(), $rules, $messages);

Отлично! Теперь наши пользовательских сообщения будет использоваться всегда при проверке. Но, что за выражение :attribute в нашем сообщении. Для облегчения вашей жизни, класс Validator заменяет :attribute на атрибут, ввод которго вызвал ошибку. Он даже удалит символ подчеркивания из имени атрибута. Вы также можете использовать :other , :size , :min , :max , и :values заполнители для конструирования ваших сообщений об ошибках.

Примеры пользовательских сообщений об ошибках: $messages = array("same" => "The:attribute and:other must match.", "size" => "The:attribute must be exactly:size.", "between" => "The:attribute must be between:min - :max.", "in" => "The:attribute must be one of the following types: :values",);

Что, если вам нужно определить необходимое сообщение, но только для атрибута электронной почты? Без проблем. Просто укажите сообщение, используя attribute_rule именование:

Определение сообщения для конктретного атрибута: $messages = array("email_required" => "We need to know your e-mail address!",);

В данном примере, пользовательское сообщение об ошибке будет использовано только для атрибута email, в остальных случаях будут использоваться стандартные сообщения.

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

Добавление собственного массива в языковый файл: "custom" => array("email_required" => "We need to know your e-mail address!",) Собственные правила валидации

Laravel предоставляет ряд мощных правил проверки. Тем не менее, вы можете создать свои собственные. Есть два простых способа создания правил проверки. И тот и другой надежны в использовании. Вам остается только выбрать более подходящий для вашего проекта.

Регистрация собственного валидационного правила: Validator::register("awesome", function($attribute, $value, $parameters) { return $value == "awesome"; });

В этом примере мы зарегистрировали новые правила проверки для класса Validator. Это правило получает три параметра. Во-первых, это имя проверяемого атрибута, второй - значение проверяемого атрибута, а третий представляет собой массив из параметров, которые были заданы для правила.

Так выглядит вызов вашего правила:

$rules = array("username" => "required|awesome",);

Конечно, вам нужно определить сообщение об ошибке для нового правила. Вы можете сделать это либо в специальных сообщениях массива:

$messages = array("awesome" => "The attribute value must be awesome!",); $validator = Validator::make(Input::get(), $rules, $messages);

или, добавив запись для правила в language/en/validation.php :

"awesome" => "The attribute value must be awesome!",

Как уже упоминалось выше, вы даже можете указать и получить список параметров в пользовательском правиле:

// При построении правила... $rules = array("username" => "required|awesome:yes",); // В правиле... Validator::register("awesome", function($attribute, $value, $parameters) { return $value == $parameters; }

В данном примере параметр аргументов вашего правила проверки будет получать массив, содержащий один элемент: "да".

Еще один способ для создания и хранения пользовательских правил проверки заключается в расширении класса Validator. Причем благодаря пространствам имен в Laravel этот класс может расширить сам себя. Тем самым вы создаете новую версию валидатора, который имеет все ранее существующие функции в сочетании с новыми дополнениями. Вы даже можете выбрать, чем заменить некоторые из методов по умолчанию, если вы хотите. Давайте посмотрим на примере:

Сначала, создаете класс Validator который наследует класс Laravel\Validator и размещаете его в application/libraries :

Определение собственного класса:

Run example »

Notice that at the start of the script, we check whether the form has been submitted using $_SERVER["REQUEST_METHOD"]. If the REQUEST_METHOD is POST, then the form has been submitted - and it should be validated. If it has not been submitted, skip the validation and display a blank form.

However, in the example above, all input fields are optional. The script works fine even if the user does not enter any data.

The next step is to make input fields required and create error messages if needed.


В предыдущей статье я обещал написать сравнение своей собственной библиотеки с другими имеющимися решениями, так что сегодня мы рассмотрим валидацию с помощью Aura.Filter , Respect Validation , Sirius Validation и Valitron .


Давайте представим, что у нас в разработке есть некий публичный сервис, который предполагает регистрацию пользователей для полного доступа ко всем функциям. Таким образом, форма регистрации будет содержать следующие поля:

  • name. Должно содержать ровно два слова, где первое - имя пользователя, а второе - фамилия.
  • login. Если значение передано, то оно должно только латинские буквы, дефисы и нижнее подчеркивание.
  • email. Должно содержать валидный адрес электронной почты.
  • password. Должен быть установлен и иметь длину не более 64 символов.
  • agreed. Типичный флажок, который пользователь должен установить, чтобы подтвердить своё согласие с условиями сервиса.
  • Итак, у нас пять полей, которые пользователь должен заполнить, чтобы зарегистрироваться в нашем воображаемом сервисе. Давайте представим, что нам на вход поступили полностью невалидные данные:


    $data = [ "name" => "Альберт", // Должно быть два слова "login" => "@lbert", // "Запрещенный" символ @ "email" => "что-то не то", // Здесь должен быть e-mail "password" => Aura.Filter

    Валидация с использованием Aura.Filter начинается с фабрики фильтров. Нам необходимо создать так называемый «фильтр субъекта», так как мы будет валидировать массив, а не индивидуальное значение.

    Определяем правила use Aura\Filter\FilterFactory; $filter = (new FilterFactory)->newSubjectFilter(); $filter->validate("name") ->isNotBlank() ->is("two_words") ->setMessage("Имя должно состоять из двух слов."); $filter->validate("login") ->isBlankOr("alnum") ->setMessage("Если вы указываете логин, он должен содержать только латинские символы."); $filter->validate("email") ->isNotBlank() ->is("email") ->setMessage("Пожалуйста, напишите корректный адрес эл. почты."); $filter->validate("password") ->isNotBlank() ->is("strlenMax", 64) ->setMessage("Пожалуйста, напишите пароль."); $filter->validate("agreed") ->is("callback", function($subject, $field) { return $subject->{$field} === true; })->setMessage("Вам необходимо согласиться с условиями сервиса.");

    Как видите, описание правил достаточно простое. Aura.Filter предоставляет целый набор полезных правил «из коробки» и некоторые из них были использованы в примере выше:

  • метод isNotBlank. Указывает, что поле не может иметь пустое значение.
  • alnum. Это правило допускает только латинские буквы.
  • email. И так понятно:)
  • strlenMax. Указывает, что поле не может превышать длину, указанную вторым аргументом метода is .
  • callback. Этот тип правила похож на замыкания из Kontrolio. Он позволяет определить правило в виде замыкания. В это замыкание Aura.Filter передает «субъект», наш массив данных из формы, и поле, в данном случае agreed .
  • Наверняка вы заметили, что я не указал правило two_words . Естественно, в Aura.Filter такого правила нет, поэтому нам необходимо его создать. Как гласит документация, это делается с помощью отдельного класса для правила:


    /** * Правило, которое валидирует имя пользователя. * Имя пользователя состоит из двух слов: имени и фамилии, разделенных одним пробелом. */ class UserNameRule { /** * Валидирует имя пользователя. * * @param object|array $subject * @param string $field * @param int $max * * @return bool */ public function __invoke($subject, $field, $max = null) { $value = $subject->{$field}; if (! is_scalar($value)) { return false; } return (bool) preg_match("/^+\s+$/u", $value); } }

    The second step is to let the filter factory know about our new rule. It’s done by passing the first argument as an array of rules to the filter factory:


    Следущий шаг - уведомить Aura.Filter о том, что мы создали новое правило и хотим его использовать. Это делается с помощью передачи массива правил в первый аргумент фабрики:


    use Aura\Filter\FilterFactory; $rules = [ "two_words" => function() { return new UserNameRule; } ]; $filter = (new FilterFactory($rules))->newSubjectFilter();

    Теперь наше правило two_words может быть использовано так же, как и любое другое правило из стандартной поставки.

    Обратная связь

    Как вы помните, входящие данные, которые мы валидируем, - полностью невалидны, потому что каждое поле содержит некорректное значение или не содержит его вовсе. Поэтому предполагается, что в результате валидации мы получим ошибки и соответствующие сообщения о них.


    Валидируем с Aura.Filter мы следующим образом:


    $valid = $filter->apply($data); if (! $valid) { $failures = $filter->getFailures(); $messages = $failures->getMessages(); }

    В $messages записывается массив, поэтому для вывода сообщений нам потребуется два вложенных foreach:


    Respect Validation

    Вторая библиотека, использованная мной в сравнении, - относительно популярное решение под названием Respect Validation . Раз люди ей доверяют, думаю, там есть что посмотреть.


    Для чистоты эксперимента при сравнении библиотек мы будем использовать один и тот же набор данных, определенный в начале:


    use Respect\Validation\Validator as v; $data = [ "name" => "Альберт", // Должно быть два слова "login" => "@lbert", // "Запрещенный" символ @ "email" => "что-то не то", // Здесь должен быть e-mail "password" => "" // Пароль вообще не указан // "agreed" нет в массиве, потому что пользователь не установил флажок ]; Определяем правила

    Как и в случае с Aura.Filter, нам необходимо собственное правило валидации для имени пользователя, поэтому давайте с него и начнем:


    namespace MyNamespace; use Respect\Validation\Rules\AbstractRule; class UserNameRule extends AbstractRule { public function validate($input) { return (bool) preg_match("/^+\s+$/u", $input); } }

    Внешнее API правил практически идентично Aura.Filter, только используется метод validate() вместо магии __invoke(). Мне оно, это API, показалось более простым и понятным. Ну, и к Kontrolio оно ближе:)


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


    use Respect\Validation\Exceptions\NestedValidationException; class UserNameRuleException extends NestedValidationException { // }

    Ну и наконец-то мы можем провалидировать наши данные. Для начала мы передаем валидатору наше новое правило, чтобы он узнал о нем, и мы смогли его использовать в дальнейшем. В Respect Validation это делается вызовом метода with() с передачей пространства имен, в котором находятся нестандартные правила.


    v::with("MyNamespace\\");

    Теперь все нестандартные правила, находящиеся в пространстве имен MyNamespace , будут «опознаны» валидатором. Следующий шаг - описать необходимые правила и выполнить валидацию.


    v::attribute("name", v::userNameRule()) ->attribute("login", v::alnum("-_")) ->attribute("email", v::email()) ->attribute("password", v::notEmpty()->stringType()->length(null, 64)) ->attribute("agreed", v::trueVal()) ->assert((object) $data);

    Обратите внимание на то, как мы применяем наше правило к атрибуту name . Здесь название класса правило трансформировалось в название метода валидатора. Остальные правила, в общем-то, интуитивно понятны.


    Отдельно стоит сказать о том, зачем мы приводим массив $data к объекту. Дело в том, что Respect Validation принимает на вход объекты, а не массивы. Это следует учесть при разработке с использованием данной библиотеки.

    Обратная связь

    В отличие от Aura.Filter валидатор Respect выбрасывает исключение, когда валидация провалена. И в этом исключении содержатся сообщения об ошибках валидации. Поэтому пример, который только что был показан, должен быть записан следующим образом:


    try { v::with("RespectValidationExample\\"); v::attribute("name", v::userNameRule()) ->attribute("login", v::alnum("-_")) ->attribute("email", v::email()) ->attribute("password", v::notEmpty()->stringType()->length(null, 64)) ->attribute("agreed", v::trueVal()) ->assert((object) $data); } catch (NestedValidationException $ex) { $messages = $ex->getMessages(); }

    Используя getMessages() , мы получим плоский массив всех сообщений, который валидатор собрал в процессе валидации. Задампив массив, мы получим примерно такой результат:


    array(5) { => string(29) “Data validation failed for %s” => string(60) “login must contain only letters (a-z), digits (0–9) and “-_”” => string(25) “email must be valid email” => string(26) “password must not be empty” => string(32) “Attribute agreed must be present” }

    Можно поменять сообщения на свои собственные. Возможно, я как-то не так понял эту библиотеку, но мне этот процесс не показался таким уж очевидным: необходимо использовать метод findMessages() на обработанном исключении, в котором вы определяете сообщения не для атрибутов, а для правил.


    $ex->findMessages([ "userNameRule" => "Имя пользователя должно состоять из двух слов.", "alnum" => "Ваш логин нам не нравится.", "email" => "Вы явно не хотите давать нам свой e-mail.", "notEmpty" => "Ну и где же ваш пароль?", "agreed" => "Жаль, что вы не согласны." ]);

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


    array(5) { => string(40) “Имя пользователя должно состоять из двух слов.” => string(31) “Ваш логин нам не нравится.” => string(25) “email must be valid email” => string(5) “Ну и где же ваш пароль?” => string(9) “Жаль, что вы не согласны.” }

    Как видите, сообщение для поля электронной почты не применилось, осталось стандартное. А вот сообщение за индексом 4 наоборот! И это при том, что я использовал не название правила, а название поля. В то время как если бы я использовал название правила (trueVal), моё сообщение бы куда-то затерялось. Комментарии опытных пользователей данной библиотеки очень приветствуются.

    Sirius Validation

    Ок, давайте перейдем к следующей библиотеке и посмотрим, как она справится со схожими задачами.

    Определяем правила

    И снова нам необходимо определить правило для имени пользователя. Мы его напишем как-то так:


    class UserNameRule extends AbstractRule { // Сообщения об ошибках const MESSAGE = "Имя пользователя должно состоять из двух слов."; const LABELED_MESSAGE = "{label} должно состоять из двух слов."; public function validate($value, $valueIdentifier = null) { return (bool) preg_match("/^+\s+$/u", $value); } }

    Обратите внимание на разницу в подходах в сравнении с уже рассмотренными библиотеками. Мы определяем два вида сообщений в константах, нежели используя свойства, методы или аргументы правила.


    Теперь давайте опишем логику валидации:


    $validator = new Validator; $validator ->add("name", "required | MyApp\Validation\Rule\UserNameRule") ->add("login", "required | alphanumhyphen", null, "Логин может содержать только латинские буквы, черточки и подчеркивания.") ->add("email", "required | email", null, "Пожалуйста, укажите корректный e-mail.") ->add("password", "required | maxlength(64)", null, "Ваш пароль, сударь.") ->add("agree", "required | equal(true)", null, "Почему же вы не согласились?");

    Как видите, набор правил весьма прост и читабелен. Для описания мы используем названия, разделенные горизонтальными черточками. Этот подход похож на тот, что используется в Laravel и Kontrolio.


    Четвертый аргумент метода add() описывает сообщение об ошибке валидации, которое Sirius использует, если валидация будет провалена. А почему же мы не добавили сообщение для нашего нового правила UserNameRule ?


    $validator->add("name", "required | MyApp\Validation\Rule\UserNameRule")

    Так это потому, что сообщения уже описаны в константах класса:


    class UserNameRule extends AbstractRule { // Сообщения об ошибках const MESSAGE = "Имя пользователя должно состоять из двух слов."; ...

    Другой вариант - использовать метод addMessage() самого валидатора:


    $validator->addMessage("email", "Пожалуйста, укажите корректный e-mail.");

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

    Обратная связь

    Чтобы выполнить валидацию, мы вызываем метод валидатора validate() , передавая в него данные:


    $data = [ "name" => "Альберт", // Должно быть два слова "login" => "@lbert", // "Запрещенный" символ @ "email" => "что-то не то", // Здесь должен быть e-mail "password" => "" // Пароль вообще не указан // "agreed" нет в массиве, потому что пользователь не установил флажок ]; $validator->validate($data);

    В отличие от Respect, Sirius не выкинет исключение, а просто вернет false . Сообщения об ошибках валидации можно получить через метод валидатора getMessages() . Он возвращает ошибки, сгруппированные по атрибутам, так что для прохода по ошибкам нам понадобится два цикла foreach:


    foreach ($validator->getMessages() as $attribute => $messages) { foreach ($messages as $message) { echo $message->getTemplate() . "\n"; } }

    Здесь $message - объект класса Sirius\Validation\ErrorMessage , у которого есть метод getTemplate() , возвращающий то самое необходимое нам сообщение.

    ValitronОпределяем правила

    Первое отличие: чтобы добавить новое правило, не нужно создавать отдельный класс. Можно просто использовать замыкание, возвращающее булевский результат.


    Для добавления кастомных правил в Valitron есть статический метод addRule() , в котором первые два аргумента обязательны, а третий - по желанию. Мне понравился такой способ, так как тут сразу в одном месте указывается идентификатор правила, логика и сообщение об ошибке.


    use Valitron\Validator; Validator::addRule("two_words", function($field, $value) { return (bool) preg_match("/^+\s+$/u", $value); }, "Имя пользователя должно состоять ровно из двух слов.");

    Второе отличие - то, как правила применяются к атрибутам. Во всех предыдущих случаях мы видели, что атрибут - вещь как бы первичная.


    В Valitron пошли другим путем и на первое место поставили именно правила валидации. Описывая правила, вы как бы применяете атрибуты к этим правилам, а не наоборот.


    $validator = new Validator($data); $validator ->rule("two_words", "name")->label("") ->rule("required", [ "name", "login", "email", "password", "agreed" ]) ->rule("slug", "login") ->rule("email", "email") ->rule("accepted", "agreed");

    Как видно из примера, в методе rule() мы сначала пишем название правила, а уже затем указываем атрибуты, которые должны соответствовать этому правилу. Более явный пример - правило required , где показано, как атрибуты «принадлежат» этому правилу.


    Valitron (как и другие решения, которые мы успели рассмотреть) предоставляет стандартные сообщения об ошибках. Если вы просто воспользуетесь ими, то увидите, что каждое сообщение начинается с названия соответствующего атрибута.


    Valitron подставляет имена атрибутов в текст сообщения даже в том случае, когда используются нестандартные сообщения об ошибках. Поэтому мы и воспользовались методом label() с пустой строкой, чтобы убрать имя атрибута.


    $validator->rule("two_words", "name")->label("") Обратная связь

    Конкретно что касается валидации, API библиотеки Valitron практически ничем не отличается от того, что мы уже видели в статье. Чтобы выполнить валидацию мы вызываем метод валидатора validate() :


    $validator->validate();

    Сообщения об ошибках валидации можно получить с помощью метода getErrors() :


    $validator->errors();

    Сообщения здесь группирутся по атрибутам точно так же, как и в Sirius Validation, за исключеним того, что для сообщения нет отдельного класса, и мы получаем обычный многомерный массив.


    foreach ($validator->errors() as $attribute => $messages) { foreach ($messages as $message) { echo $message . "\n"; } } Kontrolio

    Ну и наконец, последняя библиотека на сегодня - моя собственная разработка под названием Kontrolio .

    Определяем правила

    Снова, в пятый раз мы создадим правило валидации для имени пользователя. Всё относительно просто и стандартно:


    namespace MyProject\Validation\Rules; use Kontrolio\Rules\AbstractRule; class TwoWords extends Kontrolio\Rules\AbstractRule { public function isValid($input = null) { return (bool) preg_match("/^+\s+$/u", $input); } }

    Теперь мы создаем фабрику и регистриуем правило в ней, используя метод extend() :


    namespace MyProject; use Kontrolio\Factory; use MyProject\Validation\Rules\TwoWords; $factory = Kontrolio\Factory::getInstance()->extend();

    После регистрации правила мы можем воспользоваться им в том числе по имени - two_words . Давайте создадим валидатор:


    $data = [ "name" => "Альберт", // Должно быть два слова "login" => "@lbert", // "Запрещенный" символ @ "email" => "что-то не то", // Здесь должен быть e-mail "password" => "" // Пароль вообще не указан // "agreed" нет в массиве, потому что пользователь не установил флажок ]; $rules = [ "name" => "two_words", "login" => "sometimes|alphadash", "email" => "email", "password" => "length:1,64", "agreed" => "accepted" ]; $messages = [ "name" => "Имя пользователя должно состоять из двух слов.", "login" => "Ваш логин нам не нравится.", "email" => "Вы явно не хотите давать нам свой e-mail.", "password" => "Ну и где же ваш пароль?", "agreed" => "Жаль, что вы не согласны." ]; $validator = $factory->make($data, $rules, $messages);

    Мы описали правила, используя синтаксис, схожий с тем, что используется в Laravel, хотя могли использовать и более многословный вариант:


    $rules = [ "name" => new TwoWords, "login" => , "email" => new Email, "password" => new Length(1, 64), "agreed" => new Accepted ]; Обратная связь

    Валидация запускается всё тем же методом validate() :


    $validator->validate();

    Теперь мы можем получить сообщения об ошибках, используя один из методов getErrors() или getErrorsList() . Первый метод позволяет сделать более сложный вывод ошибок, тогда как второй возвращает плоский массив. Используя getErrors() мы можем вывести сообщения как-то так:



    А с getErrorsList() можно сделать более простой список сообщений:


    Итог

    В данной статье я показал примеры использования следующих библиотек:

  • Aura.Filter
  • Respect Validation
  • Sirius Validation
  • Valitron
  • Kontrolio
  • «Пример из реального мира» может показаться слишком простым. Я вынужден согласиться, так как, действительно, некоторые возможности библиотек остались за бортом статьи. В принципе, если вам это будет интересно, вы можете изучить их особенности самостоятельно.


    Каждая из библиотек предлагает свои фишки, имеет свои тёмные стороны, так что я думаю, что это дело вкуса и задачи - выбрать ту самую.


    Благодарю за прочтение. Сделайте правильный выбор.

    Теги:

    • валидация данных
    • php
    • велосипедостроение
    Добавить метки

    Laravel поставляется с простой, удобной системой валидации (проверки входных данных на соответствие правилам) и получения сообщений об ошибках - классом Validation .

    Простейший пример валидации $validator = Validator::make(array("name" => "Дейл"), array("name" => "required|min:5"));

    Первый параметр, передаваемый методу make - данные для проверки. Второй параметр - правила, которые к ним должны быть применены.

    Использование массивов для указания правил

    Несколько правил могут быть разделены либо прямой чертой (|), либо быть отдельными элементами массива.

    $validator = Validator::make(array("name" => "Дейл"), array("name" => array("required", "min:5")));

    Проверка нескольких полей $validator = Validator::make(array("name" => "Дейл", "password" => "плохойпароль", "email" => "[email protected]"), array("name" => "required", "password" => "required|min:8", "email" => "required|email|unique"));

    Как только был создан экземпляр Validator , метод fails (или passes) может быть использован для проведения проверки.

    If ($validator->fails()) { // Переданные данные не прошли проверку }

    Если Validator нашёл ошибки, вы можете получить его сообщения таким образом:

    $messages = $validator->messages();

    Вы также можете получить массив правил, данные которые не прошли проверку, без самих сообщений:

    $failed = $validator->failed();

    Проверка файлов

    Класс Validator содержит несколько изначальных правил для проверки файлов, такие как size , mimes и другие. Для выполнения проверки над файлами просто передайте эти файлы вместе с другими данными.

    Хук после валидации

    Laravel после завершения валидации может запустить вашу функцию-замыкание, в которой вы можете, например, проверить что-то особенное или добавить какое-то своё сообщение об ошибке. Для этого служит метод after() :

    $validator = Validator::make(...); $validator->after(function($validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add("field", "Something is wrong with this field!"); } }); if ($validator->fails()) { // }

    Вы можете добавить несколько after , если это нужно.

    Валидация в контроллерах

    Писать полный код валидации каждый раз, когда нужно провалидировать данные - это неудобно. Поэтому Laravel предоставляет несколько решений для упрощения этой процедуры.

    Базовый контроллер App\Http\Controllers\Controller включает в себя трейт ValidatesRequests , который уже содержит методы для валидации:

    /** * Сохранить пост в блоге. * * @param Request $request * @return Response */ public function store(Request $request) { $this->validate($request, [ "title" => "required|unique|max:255", "body" => "required", ]); // }

    Если валидация проходит, код продолжает выполняться. Если нет - бросается исключение Illuminate\Contracts\Validation\ValidationException . Если вы не поймаете это исключение, его поймает фреймворк, заполнит flash-переменные сообщениями об ошибках валидации и средиректит пользователя на предыдущую страницу с формой - сам!

    В случае AJAX-запроса редиректа не происходит, фреймворк отдает ответ с HTTP-кодом 422 и JSON с ошибками валидации.

    Код, приведенный выше, аналогичен вот этому::

    /** * Сохранить пост в блоге. * * @param Request $request * @return Response */ public function store(Request $request) { $v = Validator::make($request->all(), [ "title" => "required|unique|max:255", "body" => "required", ]); if ($v->fails()) { return redirect()->back()->withErrors($v->errors()); } // }

    Изменения формата ошибок

    Если вы хотите кастомизировать сообщения об ошибках валидации, которые сохраняются во флэш-переменных сессии при редиректе, перекройте метод formatValidationErrors в вашем контроллере:

    /** * {@inheritdoc} */ protected function formatValidationErrors(\Illuminate\Validation\Validator $validator) { return $validator->errors()->all(); } Валидация запросов

    Для реализации более сложных сценариев валидации вам могут быть удобны так называемые Form Requests. Это специальные классы HTTP-запроса, содержащие в себе логику валидации. Они обрабатывают запрос до того, как он поступит в контроллер.

    Чтобы создать класс запроса, используйте artisan-команду make:request:

    Php artisan make:request StoreBlogPostRequest

    Класс будет создан в папке app/Http/Requests . Добавьте необходимые правила валидации в его метод rules:

    /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ "title" => "required|unique|max:255", "body" => "required", ]; }

    Для того, чтобы фреймворк перехватил запрос перед контроллером, добавьте этот класс в аргументы необходимого метода контроллера:

    При грамотном использовании валидации запросов вы можете быть уверены, что в ваших контроллерах всегда находятся только отвалидированные входные данные!

    В случае неудачной валидации фреймворк заполняет флэш-переменные ошибками валидации и возврашает редирект на предыдущую страницу. В случае AJAX-запроса отдается ответ с кодом 422 и JSON с ошибками валидации.

    Контроль доступа

    Классы Form Request также содержат метод authorize . В этом методе вы можете проверять, разрешено ли пользователю совершать это действие, обновлять данный ресурс. Например, если пользователь пытается отредактировать комментарий к посту, является ли он его автором?

    /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { $commentId = $this->route("comment"); return Comment::where("id", $commentId) ->where("user_id", Auth::id())->exists(); }

    Обратите внимание на вызов метода route() выше. Этод метод дает вам доступ к параметрам в урле (в данном случае это {comment}), определенным в роуте:

    Route::post("comment/{comment}");

    Если метод authorize возвращает false, фреймворк формирует ответ с HTTP-кодом 403 и сразу же отсылает его. Метод контроллера не выполняется.

    /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; }

    Изменения формата ошибок во флэш-переменных

    Если вы хотите кастомизировать сообщения об ошибках валидации, которые сохраняются во флэш-переменных сессии при редиректе, переопределите метод formatValidationErrors в базовом классе запросов (App\Http\Requests\Request):

    /** * {@inheritdoc} */ protected function formatValidationErrors(\Illuminate\Validation\Validator $validator) { return $validator->errors()->all(); } Работа с сообщениями об ошибках

    После вызова метода messages объекта Validator вы получите объект MessageBag , который имеет набор полезных методов для доступа к сообщениям об ошибках.

    Получение первого сообщения для поля echo $messages->first("email"); Получение всех сообщений для одного поля foreach ($messages->get("email") as $message) { // } Получение всех сообщений для всех полей foreach ($messages->all() as $message) { // } Проверка на наличие сообщения для поля if ($messages->has("email")) { // } Получение ошибки в заданном формате echo $messages->first("email", "");

    Примечание: по умолчанию сообщения форматируются в вид, который подходит для Twitter Bootstrap.

    Получение всех сообщений в заданном формате foreach ($messages->all("
  • :message
  • ") as $message) { // } Ошибки и шаблоны

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

    Route::get("register", function() { return View::make("user.register"); }); Route::post("register", function() { $rules = array(...); $validator = Validator::make(Input::all(), $rules); if ($validator->fails()) { return redirect("register")->withErrors($validator); } });

    Заметьте, что когда проверки не пройдены, мы передаём объект Validator объекту переадресации Redirect с помощью метода withErrors . Этот метод сохранит сообщения об ошибках в одноразовых flash-переменных сессии, таким образом делая их доступными для следующего запроса.

    Однако, заметьте, мы не передаем в View::make("user.register"); переменные $errors в шаблон. Laravel сам проверяет данные сессии на наличие переменных и автоматически передает их шаблону, если они доступны. Таким образом, важно помнить, что переменная $errors будет доступна для всех ваших шаблонов всегда, при любом запросе. . Это позволяет вам считать, что переменная $errors всегда определена и может безопасно использоваться. Переменная $errors - экземпляр класса MessageBag .

    Таким образом, после переадресации вы можете прибегнуть к автоматически установленной в шаблоне переменной $errors:

    Именованные MessageBag

    Если у вас есть несколько форм на странице, то вы можете выбрать имя объекта MessageBag, в котором будут возвращаться тексты ошибок, чтобы вы могли их корректно отобразить для нужной формы.

    Return redirect("register")->withErrors($validator, "login");

    Получить текст ошибки из MessageBag с именем login:

    Правила проверки

    Ниже список всех доступных правил и их функции:

    accepted

    Поле должно быть в значении yes , on или 1 . Это полезно для проверки принятия правил и лицензий.

    active_url

    Поле должно быть корректным URL, доступным через функцию checkdnsrr .

    after:date

    Поле должно быть датой, более поздней, чем date

    alpha

    Поле должно содержать только алфавитные символы.

    alpha_dash

    Поле должно содержать только алфавитные символы, цифры, знаки подчёркивания (_) и дефисы (-).

    alpha_num

    Поле должно содержать только алфавитные символы и цифры.

    array

    Поле должно быть массивом.

    before:date

    Поле должно быть датой, более ранней, чем date . Строки приводятся к датам функцией strtotime .

    between:min ,max

    Поле должно иметь размер в диапазоне от min до max . Строки, числа и файлы трактуются аналогично правилу size .

    boolean

    Поле должно быть логическим (булевым). Разрешенные значения: true , false , 1 , 0 , "1" и "0" .

    confirmed

    Значение поля должно соответствовать значению поля с этим именем, плюс foo_confirmation . Например, если проверяется поле password , то на вход должно быть передано совпадающее по значению поле password_confirmation .

    date

    Поле должно быть правильной датой в соответствии с функцией strtotime .

    date_format:format

    Поле должно подходить под формат даты format в соответствии с функцией date_parse_from_format .

    different:field

    Значение проверяемого поля должно отличаться от значения поля field .

    email

    Поле должно быть корректным адресом e-mail.

    exists:table ,column

    Поле должно существовать в заданной таблице базе данных.

    Простое использование:

    "state" => "exists:states"

    Указание имени поля в таблице:

    "state" => "exists:states,abbreviation"

    Вы также можете указать больше условий, которые будут добавлены к запросу "WHERE":

    "email" => "exists:staff,email,account_id,1"

    image

    Загруженный файл должен быть изображением в формате jpeg, png, bmp, gif или svg.

    in:foo ,bar ,...

    Значение поля должно быть одним из перечисленных (foo , bar и т.д.).

    integer

    Поле должно иметь корректное целочисленное значение.

    ip

    Поле должно быть корректным IP-адресом.

    max:value

    Значение поля должно быть меньше или равно value

    mimes:foo ,bar ,...

    MIME-тип загруженного файла должен быть одним из перечисленных.

    Простое использование:

    "photo" => "mimes:jpeg,bmp,png"

    min:value

    Значение поля должно быть более value . Строки, числа и файлы трактуются аналогично правилу .

    not_in:foo ,bar ,...

    Значение поля не должно быть одним из перечисленных (foo , bar и т.д.).

    numeric

    Поле должно иметь корректное числовое или дробное значение.

    regex:pattern

    Поле должно соответствовать заданному регулярному выражению.

    Внимание: при использовании этого правила может быть нужно перечислять другие правила в виде элементов массива, особенно если выражение содержит символ вертикальной черты (|).

    required

    Проверяемое поле должно присутствовать и иметь непустое значение.

    required_if:field ,value ,...

    Проверяемое поле должно присутствовать и иметь непустое значение, если другое поле field присутствует и имеет любое из значений value .

    required_with:foo ,bar ,...

    Присутствует и имеет непустое значение хотя бы одно из перечисленных полей (foo , bar и т.д.).

    required_with_all:foo ,bar ,...

    Проверяемое поле должно присутствовать и иметь непустое значение, но только если присутствуют и имеют непустое значение все перечисленные поля (foo , bar и т.д.).

    required_without:foo ,bar ,...

    Проверяемое поле должно присутствовать и иметь непустое значение, но только если не присутствует или имеет пустое значение хотя бы одно из перечисленных полей (foo , bar и т.д.).

    required_without_all:foo ,bar ,...

    Проверяемое поле должно присутствовать и иметь непустое значение, но только если не присутствуют или имеют пустые значения все перечисленные поля (foo , bar и т.д.).

    same:field

    Поле должно иметь то же значение, что и поле field .

    size:value

    Поле должно иметь совпадающий с value размер. Для строк это обозначает длину, для чисел - число, для файлов - размер в килобайтах.

    timezone

    Поле должно содержать идентификатор часового пояса (таймзоны), один из перечисленных в php-функции timezone_identifiers_list

    unique:table ,column ,except ,idColumn

    Значение поля должно быть уникальным в заданной таблице базы данных. Если column не указано, то будет использовано имя поля.

    Простое использование "email" => "unique:users" Указание имени поля в таблице "email" => "unique:users,email_address" Игнорирование определённого ID "email" => "unique:users,email_address,10" Добавление дополнительных условий

    Вы также можете указать больше условий, которые будут добавлены к запросу "WHERE"":

    "email" => "unique:users,email_address,NULL,id,account_id,1"

    В правиле выше только строки с account_id равном 1 будут включены в проверку.

    url

    Поле должно быть корректным URL.

    Примечание: используется PHP-функция filter_var

    Условные правила

    Иногда вам нужно валидировать некое поле только тогда, когда оно присутствует во входных данных. Для этого добавьте правило sometimes:

    $v = Validator::make($data, array("email" => "sometimes|required|email",));

    В примере выше для поля email будет запущена валидация только когда $data["email"] существует.

    Сложные условные правила

    Иногда вам нужно, чтобы поле имело какое-либо значение только если другое поле имеет значеие, скажем, больше 100. Или вы можете требовать наличия двух полей, только когда также указано третье. Это легко достигается условными правилами. Сперва создайте объект Validator с набором статичных правил, которые никогда не изменяются:

    $v = Validator::make($data, array("email" => "required|email", "games" => "required|numeric",));

    Теперь предположим, что ваше приложение написано для коллекционеров игр. Если регистрируется коллекционер с более, чем 100 играми, то мы хотим их спросить, зачем им такое количество. Например, у них может быть магазин или может им просто нравится их собирать. Итак, для добавления такого условного правила мы используем метод Validator .

    $v->sometimes("reason", "required|max:500", function($input) { return $input->games >= 100; });

    Первый параметр этого метода - имя поля, которое мы проверяем. Второй параметр - правило, которое мы хотим добавить, если переданная функция-замыкание (третий параметр) вернёт true . Этот метод позволяет легко создавать сложные правила проверки ввода. Вы можете даже добавлять одни и те же условные правила для нескольких полей одновременно:

    $v->sometimes(array("reason", "cost"), "required", function($input) { return $input->games >= 100; });

    Примечание: Параметр $input , передаваемый замыканию - объект Illuminate\Support\Fluent и может использоваться для чтения проверяемого ввода и файлов.

    Собственные сообщения об ошибках

    Вы можете передать собственные сообщения об ошибках вместо используемых по умолчанию. Есть несколько способов это сделать.

    Передача своих сообщений в Validator $messages = array("required" => "Поле:attribute должно быть заполнено.",); $validator = Validator::make($input, $rules, $messages);

    Примечание: строка:attribute будет заменена на имя проверяемого поля. Вы также можете использовать и другие строки-переменные.

    Использование других переменных-строк $messages = array("same" => "Значения:attribute и:other должны совпадать.", "size" => "Поле:attribute должно быть равно exactly:size.", "between" => "Значение:attribute должно быть от:min и до:max.", "in" => "Поле:attribute должно иметь одно из следующих значений: :values",); Указание собственного сообщения для отдельного поля

    Иногда вам может потребоваться указать своё сообщение для отдельного поля.

    $messages = array("email.required" => "Нам нужно знать ваш e-mail адрес!",);

    Указание собственных сообщений в файле локализации

    Также можно определять сообщения валидации в файле локализации вместо того, чтобы передавать их в Validator напрямую. Для этого добавьте сообщения в массив custom файла локализации app/lang/xx/validation.php .

    "custom" => array("email" => array("required" => "Нам нужно знать ваш e-mail адрес!",),),

    Собственные правила проверки Регистрация собственного правила валидации

    Laravel изначально содержит множество полезных правил, однако вам может понадобиться создать собственные. Одним из способов зарегистрировать произвольное правило - через метод Validator::extend .

    Validator::extend("foo", function($attribute, $value, $parameters) { return $value == "foo"; });

    Примечание: имя правила должно быть в формате_с_подчёркиваниями.

    Переданная функция-замыкание получает три параметра: имя проверяемого поля $attribute , значение поля $value и массив параметров $parameters , переданных правилу.

    Вместо функции в метод extend можно передать ссылку на метод класса:

    Validator::extend("foo", "FooValidator@validate");

    Обратите внимание, что вам также понадобится определить сообщение об ошибке для нового правила. Вы можете сделать это либо передавая его в виде массива строк в Validator , либо вписав в файл локализации.

    Расширение класса Validator

    Вместо использования функций-замыканий для расширения набора доступных правил вы можете расширить сам класс Validator . Для этого создайте класс, который наследует Illuminate\Validation\Validator . Вы можете добавить новые методы проверок, начав их имя с validate .