{{TOC}} **Формы** - непременная часть любого вёб-приложения. Мы используем их для авторизации и регистрации нового пользователя, на странице обратной связи, при отправки комментария и для множества других задач. Однако их создание и последующая проверка может быть неприятным процессом - и здесь на сцену выходит Laravel, который даёт нам несколько аккуратных классов для интуитивно понятной работы с формами и их данными. == ((#form)) Создание форм в Laravel == Создавать формы в Laravel неожиданно просто. По большей части ничего не мешает вам использовать стандартные теги HTML, но Laravel может значитель облегчить вам жизнь. Скажем, класс %%Form%% имеет метод %%label()%%, позволяющий вам связать метки с полями формы используя соответствующие (автосгенерированные) ID. Давайте в качестве примера создадим простую форму: %%

Register!

%% Всё очевидно, не так ли? Мы открываем форму //POST//-запроса на ((док3:routing маршруте)) **register**, создаём несколько меток и полей ввода и добавляем CSRF-ключ, после чего закрываем форму. Если нам нужен //безопасный маршрут// (с использованием //HTTPS//), то заменим вызов %%Form::open()%% на вызов %%Form::open_secure()%%, а если нам нужно создать форму для загрузки файлов, то пригодится %%Form::open_for_files()%%. Вы наверняка заметили некие вызовы %%Input::old()%% - о них мы поговорим ((#msg чуть позже)), а пока просто запомните, что они здесь есть. Класс **Form** содержит множество методов для простого создания форм - вы можете ознакомиться с ними в ((док3:views/forms документации)). == Подделка межсайтовых запросов (CSRF) == Я не буду вдаваться в подробности о CSRF - ((http://www.codinghorror.com/blog/2008/09/cross-site-request-forgeries-and-you.html Jeff Atwood)) написал очень наглядную статью о том, что это такое и как этого избежать. Метод **Form::token()** создаёт случайную последовательность символов, сохраняет её в данных ((док3:session/usage сессии)) (это значит, что вам нужно включить их поддержку в %%(t)application/config/session.php%%) и выводит её в виде скрытого поля формы. При обработке запроса от формы, использующей CSRF-ключ мы можем использовать встроенный фильтр **csrf** для проверки того, что никто не "поработал" над запросом и он действительно исходит от пользователя. Вот как выглядит код фильтра (файл **application/routes.php**): %% Route::filter('csrf', function () { if (Request::forged()) return Response::error('500'); }); %% Мы можем настроить его как нам нужно, но для этой статьи нам вполне хватит стандартного обработчика. Начнём писать наш //POST//-((док3:routing маршрут)), который будет обрабатывать регистрацию (файл **application/routes.php**): %% Route::post('register', array('before' => 'csrf', function () { // регистрация нового пользователя. })); %% Вот и всё, что нам нужно для включения CSRF-фильтра - просто убедится, что он указан в списке **before**. == ((#validate)) Проверка введённых данных == Теперь, когда мы удостоверились, что пришедшему запросу можно доверять, нам нужно проверить данные, которые ввёл в форму пользователь. Вернёмся к нашему марштуру **register**, добавим туда проверку, а затем я объясню, что к чему. Несколько человек заметили, что проверка не должна выполняться в контроллере. Обычно лучше всего делать это в модели - моя следующая статья "((9))" описывает именно этот способ решения задачи. %% // файл application/routes.php. Route::post('register', array('before' => 'csrf', function () { $rules = array( 'username' => array('required', 'unique:users,username'), 'email' => array('required', 'email', 'unique:users,email'), 'password' => array('required', 'min:7') ); $validation = Validator::make(Input::all(), $rules); if ($validation->fails()) { // проверка не пройдена. return Redirect::to('register')->with_input()->with_errors($validation); } // данные прошли проверку - можем создавать нового пользователя. })); %% Вот, что мы здесь делаем: 1. **Определяем массив правил** - //ключи// соответствуют именам полей ввода формы, //значения// - правилам, которым они должны соответствовать. В нашем случае поле **username** ("имя пользователя") должно быть **заполнено** (%%(t)required%%) и **уникально** (%%(t)unique%%). 2. **Создаём объект %%Validator%%** - //первым параметром// он принимает данные для проверки (в нашем случае данные формы, полученные через //POST//), //вторым// - набор правил. 3. Затем мы **выполняем проверку** - если она не прошла, переадресуем пользователя обратно к ((док3:routing маршруту)) **register** (используя //GET//) со старым вводом и сообщениями об ошибках. 4. Если же **все поля заполнены верно** - регистрируем новую учётную запись, авторизуем пользователя, либо делаем что-то ещё. Последовательность действий, которые мы здесь выполняем, называют шаблоном "((ВП:Post/Redirect/Get Post/Redirect/Get))" (PRG) - это отличный способ предотвратить двойную отправку формы. Теперь посмотрим на правила, которые мы определили выше. **required** - обозначает, что поле должно быть заполнено - иными словами, оно должно иметь значение. **unique** - здесь немного более сложный момент. Это правило принимает 3 параметра, 2 из которых можно опустить. **Первый параметр** - имя таблицы, в котором нужно проверять значение на уникальность; **второй** - имя поля в таблице, если оно не соответствует имени поля в форме (в этом случае его можно пропустить); **третий** и последний параметр - значение (//id//) для первичного ключа. Предположим, что мы обновляем существующий профиль пользователя - мы точно так же хотим, чтобы его e-mail был уникальным, но если он решил не изменять свой адрес, то нам не стоит говорить, что введённый e-mail уже занят - им самим. Для этого мы и передаём ID пользователя - Laravel исключит соответствующую запись из проверки. ##(indent_1) %% $rules = array( 'email' => array('required', 'email', 'unique:users,email,' . $user->id) ); %% ## **e-mail** - проверяет, что введённое значение //похоже// на правильный e-mail адрес, но не проверяет его на //существование//. **min** - устанавливает минимально допустимую длину значения данного поля. Полный список доступных правил можно найти в ((док3:validation#rules документации)). == ((#msg)) Отображение ошибок == В Laravel все //((док3:views представления))// ("views") уже имеют предустановленную переменную %%$errors%% - если вы не установили её сами. Эта переменная содержит объект **Messages** - тот самый, который возвращает нам **Validator**. Вы могли заметить, что в ((#validate предыдущем примере)) мы переадресовывали клиента //со вводом// (%%with_input()%%) и //с ошибками// (%%with_errors()%%). Свойство %%$validation->errors%% содержит тот же объект **Messages** со всеми ошибками, найденными во входных данных. Laravel определит, что была выполнена переадресация //с ошибками// и автоматически привяжет этот объект к //представлению//. Вы скоро поймёте, что я имею в виду. Помните вызовы %%Input::old()%% в ((#form нашей форме))? Когда мы переадресовываем запрос //со вводом// они вернут значения, которые пользователь ввёл в форму ранее. Поэтому если я сказал, что моё имя - "Jason", но регистрация не произошла, "Jason" останется введённым даже после переадресации меня обратно на форму. Отлично! ##(alert) Поля ввода паролей изначально не имеют значения по умолчанию, но вы можете задать его явно используя //массив атрибутов//: %% echo Form::password('password', array('value' => Input::old('password'))); %% ## Вернёмся к нашей форме. Как вы помните, у нас есть переменная **$errors** - объект %%Messages%%. Давайте сделаем нашу форму более дружелюбной, отображая ошибки, когда она была неверно заполнена: %%

Register!

has('username')) { echo $errors->first('username'); } echo Form::label('email', 'E-mail') . Form::text('email', Input::old('email')); if ($errors->has('email')) { echo $errors->first('email'); } echo Form::label('password', 'Password') . Form::password('password'); if ($errors->has('password')) { echo $errors->first('password'); } echo Form::submit('Register!'); echo Form::token() . Form::close(); ?> %% Сообщения будут показаны только в случае, когда определённое поле содержит ошибки. Вы можете отформатировать сообщение в нужный HTML передав его во втором параметре: %% echo $errors->first('username', ':message'); %% Либо, если вы хотите отобразить первое сообщение об ошибке для любого поля, а не только для **username**: %% echo $errors->first(); %% Наконец, вы можете **отобразить все возникшие ошибки**: %% has()): ?> Мы обнаружили следующие ошибки: %% %%$errors->all()%% возвращает массив отформатированных сообщений, поэтому мы объединяем его в строку. == Собственные правила и ошибки == Часто вам понадобится создавать собственные правила для проверки ввода. До **Laravel 3** это делалось наследованием класса **Validator** и добавлением к нему методов. С поддержкой ((док3:bundles пакетов)) потребовалось более надёжное решение, чтобы не создавать множество подклассов. //Laravel 3// позволяет **регистрировать произвольные правила** с помощью %%Validator::register()%%: %% // этот код может быть помещён, например, в application/start.php: Validator::register('starts_with', function ($attribute, $value, $parameters) { return starts_with($value, $parameters[0]); }); %% В этом простом обработчике мы просто возвращаем **true**, если значение начинается на указанную строку (переданную в первом и единственном параметре), и **false** в противном случае. Здесь используется одна из немногих //глобальных функций// Laravel - %%starts_with()%%. Использование нового правила: %% $rules = array( 'website' => array('required', 'starts_with:http://') ); %% Здесь нам потребовалось определить поле как //обязательное// ("required"). Крмое этого нам также нужно добавить сообщение об ошибке в файл **application/language/en/validation.php**: %% 'custom' => array( 'website_starts_with' => 'Website must start with http://' ) %% Либо можно это сделать при создании экземпляра **Validator**, передав его в //третьем параметре//: %% $rules = array( 'website' => array('required', 'starts_with:http://') ); $messages = array( 'website_starts_with' => 'Website must start with http://' ); $validation = Validator::make(Input::all(), $rules, $messages); %% **Вот и всё!** Итак, мы с вами создали форму, отправляющую //POST//-запрос на наш ((док3:routing маршрут)), где происходит проверка ввода ((док3:controllers контроллером)) - который, в свою очередь, при обнаружении ошибок отправляет клиента обратно с сохранением предыдущего ввода и выводом соответствующих сообщений.