Создание сущности в CRUD требует наличия двух маршрутов: один для отображения формы, другой для обработки формы. Кроме того, важно понимать как взаимодействуют между собой эти маршруты, как обрабатываются ошибки и так далее. Начнем с того что у нас есть три состояния:
- Отображение новой формы
- Отображение формы с подсвеченными ошибками валидации после ее отправки
- Редирект на какую-то страницу (обычно это редактирование сущности или список сущностей) после успешной обработки формы
Самое интересное здесь – вторая часть. Когда данные формы приходят в обработчик формы (пользователь нажал кнопку отправки), этот обработчик выполняет "валидацию", то есть проверку введенных данных. Например, проверяет, что данные в принципе есть, то есть они не пустые. Если данные корректные, то обработка завершается и пользователя отправляют в другое место, но если нет, то Laravel должен отработать эту ситуацию по особенному.
С точки зрения пользователя, это выглядит просто. Сайт снова отображает форму с подставленными значениями, которые он ввел раньше. Кроме этого, на странице выводятся возникшие ошибки. Дальше пользователь их исправляет и отправляет форму заново. Этот процесс может повторяться много раз перед тем, как пользователь сделает все правильно.
Технически Laravel ведет себя так. Если пользователь ввел что-то некорректно, то происходит редирект на страницу с формой. Laravel автоматически записывает данные формы в сессию, а затем использует эти данные для подстановки в форму (тут участвует formModel()
).
Форма
Как обычно, нам придется добавить три вещи: маршрут, обработчик маршрута, шаблон.
Маршрут
<?php
Route::get('articles/create', [ArticleController::class, 'create'])
->name('articles.create');
Важно добавить этот маршрут до маршрута articles/{id}. Иначе последний перехватит обращение к articles/create, так как он соответствует шаблону.
Обработчик
<?php
namespace App\Http\Controllers;
use App\Models\Article;
class ArticleController extends Controller
{
// Вывод формы
public function create()
{
// Передаем в шаблон вновь созданный объект. Он нужен для вывода формы
$article = new Article();
return view('article.create', compact('article'));
}
}
Шаблон
{{ html()->modelForm($article, 'POST', route('articles.store'))->open() }}
{{ html()->label('Имя', 'name') }}
{{ html()->input('text', 'name') }}
{{ html()->label('Содержание', 'body') }}
{{ html()->textarea('body') }}
{{ html()->submit('Создать') }}
{{ html()->closeModelForm() }}
Обработчик данных формы
Маршрут
<?php
// POST-запрос
Route::post('articles', [ArticleController::class, 'store'])
->name('articles.store');
Обработчик
<?php
namespace App\Http\Controllers;
use App\Models\Article;
// Нам понадобится объект запроса
use Illuminate\Http\Request;
class ArticleController extends Controller
{
// Здесь нам понадобится объект запроса для извлечения данных
public function store(Request $request)
{
// Проверка введенных данных
// Если будут ошибки, то возникнет исключение
// Иначе возвращаются данные формы
$data = $request->validate([
'name' => 'required|unique:articles',
'body' => 'required|min:1000',
]);
$article = new Article();
// Заполнение статьи данными из формы
$article->fill($data);
// При ошибках сохранения возникнет исключение
$article->save();
// Редирект на указанный маршрут
return redirect()
->route('articles.index');
}
}
В данном обработчике нам понадобился доступ к объекту запроса. Любая информация о HTTP-запросе, любые данные, отправленные по HTTP, можно получить только через $request
.
Как и раньше тут может возникнуть вопрос, каким образом Laravel понимает что в этот метод надо передать объект запроса, а в другие ничего передавать не надо. Ответ кроется в метапрограммировании, которое в PHP делается через Reflection API.
Первым делом объект $request
используется в валидации. Валидация в Laravel привязана к запросу. Она выполняется с помощью метода validate($request, $params)
, доступного в каждом контроллере. Второй аргумент в этом методе – массив, в котором ключ это название поля из формы, а значение, это набор "валидаторов", правил, которые применяются к значению для проверки его корректности. Валидаторы отделяются друг от друга вертикальной чертой. Вот что они означают:
- required – не может быть пустым
- min:1000 – минимум 1000 символов
- unique:articles – поле (name) должно быть уникально в таблице articles
Метод validate
ничего не делает, если с данными все в порядке, и выбрасывает исключение в случае ошибок. Затем Laravel перехватывает это исключение и выполняет всю остальную работу за нас. Он сохраняет данные формы, делает редирект на страницу отображения формы и формирует переменную $errors
, доступную в шаблоне. Самый простой способ вывести ошибки, добавить над формой такой код:
@if ($errors->any())
<div>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
Вернемся к нашему обработчику. Сразу после валидации выполняется создание сущности, наполнение ее данными формы и сохранение.
<?php
$article = new Article();
$article->fill($data);
$article->save();
Метод fill($params)
выполняет, так называемый mass-assignment, то есть установку сразу всех значений через передачу ассоциативного массива. Такой способ удобнее чем копировать каждое значение индивидуально:
<?php
// Может получиться много кода!
$article->name = $request->input('name');
$article->body = $request->input('body');
Но mass-assignment обладает одним недостатком, который приходится обрабатывать отдельно. Такой способ установки значений опасен, так как пользователь может послать любые данные, включая те, которых нет в форме (это HTTP, пользователь может отправить все что угодно). Это значит, что пользователь может переписать любые данные, к которым он может не иметь доступа. Это могут быть пароли, идентификаторы и приватные данные.
Разные фреймворки обрабатывают эту ситуацию по-разному. Laravel предлагает создавать внутри модели массив, в котором перечисляются поля, доступные для mass-assignment. Все, что там не перечислено, будет игнорироваться:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
protected $fillable = ['name', 'body'];
}
Это легко проверить в Tinker:
>>> $article = new Article();
=> App\Models\Article {#3033}
>>> $article->fill(['name' => 'supername', 'wrongfield' => 'boom!']);
=> App\Models\Article {#3033
name: "supername",
}
После того как объект сохранился в базе данных, осталось перенаправить пользователя в то место, куда мы хотим его отправить после успешного создания. Обычно отправка идет на список сущностей или страницу редактирования.
<?php
// Не забудьте сделать return
return redirect()
->route('articles.index');
Обратите внимание, что у обработчика данных формы нет своего шаблона. Он в любом случае выполняет редирект: либо на исходную форму, либо на результирующую.
Самостоятельная работа
- Выполните все шаги из теории.
- Создайте через интерфейс несколько статей. Проверьте работу валидации.
- Попробуйте самостоятельно добавить вывод флеш-сообщений.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.