Создание (CRUD)

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

  • Отображение новой формы
  • Отображение формы с подсвеченными ошибками валидации после её отправки
  • Редирект на какую-то страницу (обычно это редактирование сущности или список сущностей) после успешной обработки формы

Самое интересное здесь – вторая часть. Когда данные формы приходят в обработчик формы (пользователь нажал кнопку отправки), этот обработчик выполняет "валидацию", то есть проверку введённых данных. Например, проверяет, что данные в принципе есть, то есть они не пустые. Если данные корректные, то обработка завершается и пользователя отправляют в другое место, но если нет, то Laravel должен отработать эту ситуацию по особенному.

С точки зрения пользователя, это выглядит просто. Сайт снова отображает форму с подставленными значениями, которые он ввёл раньше. Кроме этого, на странице выводятся возникшие ошибки. Дальше пользователь их исправляет и отправляет форму заново. Этот процесс может повторяться много раз перед тем, как пользователь сделает все правильно.

Технически Laravel ведёт себя так. Если пользователь ввёл что-то некорректно, то происходит редирект на страницу с формой. Laravel автоматически записывает данные формы в сессию, а затем использует эти данные для подстановки в форму (тут участвует Form::model).

Форма

Как обычно нам придётся добавить три вещи: маршрут, обработчик маршрута, шаблон.

Маршрут

<?php

Route::get('/articles/create', 'ArticleController@create')
  ->name('articles.create');

Важно добавить этот маршрут до маршрута /articles/{id}. Иначе последний перехватит обращение к /articles/create, так как он соответствует шаблону.

Обработчик

<?php

namespace App\Http\Controllers;

use App\Article;

class ArticleController extends Controller
{
    // Вывод формы
    public function create()
    {
        // Передаём в шаблон вновь созданный объект. Он нужен для вывода формы через Form::model
        $article = new Article();
        return view('article.create', compact('article'));
    }
}

Шаблон


{{ Form::model($article, ['url' => route('articles.store')]) }}
    {{ Form::label('name', 'Название') }}
    {{ Form::text('name') }}<br>
    {{ Form::label('body', 'Содержание') }}
    {{ Form::textarea('body') }}<br>
    {{ Form::submit('Создать') }}
{{ Form::close() }}

Обработчик данных формы

Маршрут

<?php

// POST-запрос
Route::post('/articles', 'ArticleController@store')
  ->name('articles.store');

Обработчик

<?php

namespace App\Http\Controllers;

use App\Article;
// Нам понадобится объект запроса
use Illuminate\Http\Request;

class ArticleController extends Controller
{
    // Здесь нам понадобится объект запроса для извлечения данных
    public function store(Request $request)
    {
        // Проверка введённых данных
        // Если будут ошибки, то возникнет исключение
        // Иначе возвращаются данные формы
        $data = $this->validate($request, [
            '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;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    protected $fillable = ['name', 'body'];
}

Это легко проверить в Tinker:

>>> $article = new Article();
=> App\Article {#3033}
>>> $article->fill(['name' => 'supername', 'wrongfield' => 'boom!']);
=> App\Article {#3033
     name: "supername",
   }

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

<?php

// Не забудьте сделать return
return redirect()
    ->route('articles.index');

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

Самостоятельная работа

  1. Выполните все шаги из теории.
  2. Создайте через интерфейс несколько статей. Проверьте работу валидации.
  3. Попробуйте самостоятельно добавить вывод флеш-сообщений.

Дополнительные материалы

  1. Валидаторы
  2. Flash-сообщения

Для полного доступа к курсу, нужна профессиональная подписка

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

Получить доступ
115
курсов
892
упражнения
2241
час теории
3196
тестов

Зарегистрироваться

или войти в аккаунт

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

Нажимая кнопку «Зарегистрироваться», вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг».

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Нажимая кнопку «Зарегистрироваться», вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг».