{{Laracast Laravel 5 Fundamentals, 12, Form Requests and Controller Validation, 26.01.2015, 26.06.2016, https://laracasts.com/series/laravel-5-fundamentals/episodes/12}} %%(hvlraw) %% (0:00) Хорошо, мы готовы разобраться с валидацией, это довольно интересная тема. Так что если я перейду на %%ArticlesController%% - вот где мы закончили в прошлый раз. Единственное различие в том, что я добавил несколько документирующих комментариев в различные методы. И хорошо, если это станет и вашей привычкой. И, к счастью, если вы используете что-нибудь, наподобие PhpStorm, то этот процесс может быть автоматизирован. Хорошо, давайте пойдём в низ страницы, в метод %%store()%%. Вот на чём нам тут нужно сосредоточиться. Итак, если вы помните, эту форму в её текущем состоянии можно отправить, и это не вызовет ошибки. (0:30) Попробуем отправить её, и... новая запись была внесена в таблицу БД. И мы выводим её на экран, но там ничего нет. Это очень плохо. Итак, вместо этого мы используем валидацию, чтобы убедиться, что такого рода вещей больше не случится. Итак, давайте начнём разбираться прямо сейчас. Но для начала напишем: %%(sql) select * from articles; %% и я собираюсь также сделать %%(sql) delete from articles where id = 7; %% Просто чтобы начать с нуля. Хорошо, давайте займёмся этим. (1:00) Сейчас я покажу вам два различных способа как с этим работать. Один способ - использовать нечто, что мы называем запросом для форм (класс и объект). Это действительно полезно для более сложных проверок. Но бывают и случаи где нужна очень простая валидация. Вы просто хотите убедиться, что пользователь заполнил одно-два поля, и что теперь вы можете идти дальше. И для таких ситуаций мы можем воспользоваться типажом (trait), который Laravel включает в себя по умолчанию. Итак, давайте начнём работу с запросами для форм. Как вы можете представить, у нас для этого есть генератор. (1:30) Итак, если я запущу: %%(sh) php artisan %% И мы прокрутим вверх, вы увидите тут раздел %%(t)make:request%%. Считайте что эти классы - это способ для пользователя сделать какой-либо запрос к вашему приложению, например, регистрация, продление подписки, или в данном случае - создание новой статьи. Так почему бы нам это не попробовать? Сделаем новый запрос для формы, и мы могли бы назвать его %%CreateArticle%% или %%PublishArticle%% или %%Schedule%% – любую терминологию, уместную для вашего приложения: (2:00) %%(sh) php artisan make:request CreateArticleRequest %% В данном случае простого метода CRUD достаточно. Многие люди также добавляют тут в конце %%Request%%, как суффикс. Вы можете тоже так сделать или просто оставить как есть, тут всё зависит от ваших предпочтений. Мы его добавим. И вот теперь всё. Теперь мы переключимся обратно, и, если я открою %%(t)app/HttpRequests%%, вот куда все те классы будут добавлены (%%(t)CreateArticleRequest.php%%). Это выглядит немного путано, но, фактически, тут всё очень, очень просто. (2:30) Итак, у нас здесь два различных метода, %%authorize()%% и %%rules()%%. Первый определяет, есть ли у пользователя разрешение выполнять такой тип запроса или действия? Вот пример. Что если кто-нибудь попытается отредактировать комментарий, который он сам не создавал? Конечно же, мы не хотим допускать этого. Джон не может редактировать комментарий, который создала Джейн. Тут (в методе %%authorize()%%) мы и можем добавлять такого типа логику. В нашем случае, у нас в приложении даже не настроена система регистрации и подписки. (3:00) Так что я просто установлю здесь %%true%%: %% return true; %% Сейчас кто угодно может сделать этот запрос. Затем у нас есть наш метод %%rules()%%. И здесь мы можем добавлять наши условия, по сути, для запроса. Например, если мы вернёмся к %%articles/create%%, у нас есть заголовок (%%title%%), тело (%%body%%) и дата публикации (%%published_at%%). Хорошо, мы можем ссылаться на эти имена. Заголовок будет обязателен: %% 'title' => 'required', %% Затем, тело статьи тоже будет обязательно для заполнения: %% 'body' => 'required', %% и наша дата %%published_at%% также будет обязательна: %% 'published_at' => 'required' %% (3:30) Но, возможно, есть и другие вещи, которые нам тут нужно сделать. Например, представьте, что у вас есть форма регистрации, и ваше поле электронной почты обязательно для заполнения, но оно также должно быть определённого типа. Оно должно походить на адрес электронной почты. И затем, возможно, ваше имя тоже обязательно, и оно должно быть длиной не менее 2 символов. И если вы хотите увидеть весь огромный список того, что вы можете делать, посетите ((/docs/v5/validation эту страницу документации)). Почему бы нам не добавить ещё парочку? (4:00) Мы могли бы либо использовать тут символ pipe (%%(t)|%%), так что я мог бы написать %%(t) required|date %% (тут также ожидается тип - дата) Заметьте, что мы используем %%(t)|%%, чтобы разделить их. Или, если вы предпочтёте иное, то можете использовать простой массив %%['required', 'date']%%. Любой из вариантов сработает. Давайте оставим вариант с %%(t)|%%. Далее, для заголовка, просто для интереса, скажем ему нужно быть длиной минимум 3 символа. Как тут нам быть? Я могу написать: %% 'title' => 'required|min:3', %% то есть %%(t)min:%% и затем минимальное значение. Рассматривайте это как наш аргумент. (4:30) В нашем случае, он должен быть длиной по крайней мере 3 символа, так что мы можем представить его таким образом. Хорошо, сейчас мы настроили авторизацию, а также наши правила для валидации данных, теперь давайте переключимся на наш контроллер, и сейчас всё, что нам нужно сделать – это ввести подсказку для ожидаемого типа данных в коде вот здесь: %% public function store(Requests\CreateArticleRequest $request) %% Здесь дадим имя %%$request%%. Хорошо, таким образом, мы можем оставить тут всё как есть, или можем импортировать класс наверху и слегка подчистить код. Затем обновим наш комментарий. (5:00) Класс. Теперь вот очень важная для понимания концепция. Когда мы ввели подсказку ожидаемого типа нашего запроса (typehint) для форм, за кадром Laravel сообразит, что мы здесь занимаемся валидацией. И он автоматически запустит валидацию до того, как этот метод на самом деле начнёт выполняться. Это очень важно понять. Другими словами, это означает, что тело этого метода никогда не будет запускаться. Мы никогда не создадим статью, если только наша проверка не прошла. (5:30) Теперь что же произойдёт, если валидация завершилась неудачей? Например, если пользователь не ввёл ничего в поле заголовка, то проверка потерпит неудачу, этот метод никогда не запустится, и вместо этого мы на самом деле перенаправим его на предыдущую страницу, чтобы пользователь мог попробовать опять. ОК, давайте прежде чем проверить как всё работает в браузере, вместе пройдём весь этот цикл ещё один раз. Пользователь приходит на страницу %%(t)create%%, заполняет различные поля формы. (6:00) Когда форма отправлена, мы готовимся запустить этот метод. Однако, Laravel понял тот факт, что мы задали подсказку формата типа данных для этого объекта, и он постарается сделать всё возможное, чтобы дать нам именно такой объект. Однако, в процессе он обнаружит, что этот объект может быть проверен, так что он автоматически запустит для вас всю валидацию. Если валидация прошла успешно, то нам даётся объект запроса, и статья создаётся. Однако, ещё раз, если проверка не прошла, то этот метод не будет срабатывать, а вместо этого мы перенаправляемся на предыдущий маршрут, чтобы пользователь мог исправить ошибку в форме. (6:30) Сейчас я хочу, чтобы вы отметили что мы реализовали проверку, не добавив ни единой строки в тело этого метода. Так что я удалю этот комментарий. Далее, мы получаем запрос, значит нам больше не нужно использовать фасад. Я могу просто сказать: %% Article::create($request->all()); %% И это было бы эквивалентно. Что означает, что тут сверху мы также можем убрать вот этот импорт. (7:00) ОК, всё очень красиво. Итак, почему бы нам не попробовать теперь это в браузере и не посмотреть, сработает ли это? Мы идём в %%(t)articles/create%%, и в этот раз мы введём данные. Так, Enter, Enter и текущая дата – всё ОК. Добавить статью, и всё сработало. Так что никаких вопросов там. А в этот раз мы попробуем отправить форму, не заполнив что-либо. Давайте оставим тело пустым. Хорошо, сейчас, я добавляю статью, и давайте посмотрим. (7:30) Нас не перенаправили обратно на нашу главную страницу, а просто отправили обратно в эту же форму, потому что валидация потерпела неудачу. Так что, вот что я имею в виду, когда говорю, что это всё в некотором роде происходит автоматически, и это довольно круто. Но у нас есть одна проблема. Если пользователь совершил ошибку, мы его не известили, что же именно пошло не так, и у них на самом деле нет никакой подсказки, и, как вы можете себе представить, в большой форме, это будет проблемой. Нам нужно выдать какую-то обратную связь. Так почему бы нам это не исправить? (8:00) Мы можем сделать это, вернувшись к нашему представлению, и почему бы нам не показывать ошибки валидации прямо здесь под формой? И вот ещё одна вещь, которую важно понять. Ваши представления всегда будут иметь доступ к переменной ошибок %%$errors%%. Чтобы продемонстрировать это, давайте сделаем так: %% var_dump($errors) %% Давайте посмотрим, что из этого выйдет. Итак, мы идём на страницу, и теперь вы можете видеть, что это набор ошибок. Нам не нужно было создавать эту переменную или передавать её в представление - всё это было автоматически сделано Laravel. (8:30) Но прямо сейчас здесь пусто, потому что у нас нет ошибок валидации. Но если мы запустим вот так, то смотрите что тут сейчас... Мы запустили проверку, она провалилась, нас перенаправили обратно, набор ошибок заполнился, и сейчас вы можете увидеть тут две ошибки. Итак, вот что мы можем сделать. Мы могли бы сказать: %% @if ($errors->any()) %% Это всего лишь метод-помощник, который даёт нам знать, если у нас возникли какие-либо ошибки. Итак, если они есть (и мы закроем %%@endif%% внизу), то мы хотим вывести их на экран. (9:00) Почему бы нам тут не использовать какие-нибудь классы Bootstrap: %%(h)