Lab / Laravel

Laravel 8 принцип создания CRUD с минимальным колличеством кода

В данном уроке мы с вами научимся создавать простой CRUD на Laravel 8 на примере заметок, а заодно и оптимизировать код. Тут будут использованы некоторые ухищрения которые заметно стали экономить мне время при создании приложений на Laravel. Роуты

Давайте начнём с создания роутов. Поскольку это CRUD соответственно у нас должно быть 7 роутов типа:

Route::get('notes', 'NotesController@index');
Route::get('notes/create', 'NotesController@create');
Route::get('notes/store', 'NotesController@store');
Route::get('notes/{id}/edit', 'NotesController@edit');
Route::get('notes/{id}/update', 'NotesController@update');
Route::get('notes/{id}/show', 'NotesController@show');
Route::get('notes/{id}/destroy', 'NotesController@destroy');

Для тех кто не в курсе роуты прописываются в файле routes/web.php

Но право же, для каждой сущности плодить по 7 роутов, ещё и таких типовых, это бесполезная трата времени, лучше уж пойти погулять в лесу или выгулять пса 😉

Роутинг в одну строку

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

# Подключаем контроллер
use App\Http\Controllers\UserController;

# Добавляем роут
Route::resource('notes', 'NotesController');

Контроллер и модель

Теперь сгенирируем сам роутер с этими методами:

php artisan make:controller NotesController --resource --model=Note

Флаг --resource сразу сделает нам разметку контроллера, а флаг --model ещё и предложит создать модель на лету. Для этого необходимо ответить Y в ответ на вопрос - A App\Models\Note model does not exist. Do you want to generate it?

И всё! Больше нам ничего писать в роутах не нужно, потому, что они сами создадутся. Не верите? Давайте сами в этом убедимся. Вам всего лишь нужно запустить в терминале команду: php artisan route:list что бы увидеть все доступные пути в текущем приложении.

Должно получится вот такое великолепие со всеми путями алиасами и методами:

| Method    | URI                | Name          | Action                                      | Middleware
| POST      | notes              | notes.store   | App\Http\Controllers\NotesController@store  | web |
| GET|HEAD  | notes              | notes.index   | App\Http\Controllers\NotesController@index  | web |
| GET|HEAD  | notes/create       | notes.create  | App\Http\Controllers\NotesController@create | web |
| PUT|PATCH | notes/{note}       | notes.update  | App\Http\Controllers\NotesController@update | web |
| GET|HEAD  | notes/{note}       | notes.show    | App\Http\Controllers\NotesController@show   | web |
| DELETE    | notes/{note}       | notes.destroy | App\Http\Controllers\NotesController@destroy| web |
| GET|HEAD  | notes/{note}/edit  | notes.edit    | App\Http\Controllers\NotesController@edit   | web |

Миграция

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

php artisan make:migration create_notes_table --create=notes

Теперь вы можете перейти в папку database/migrations и там найдёте ваш новенький файл миграции create_notes_table.php Там уже будут стандартные обязательные для всех моделей поля. Давайте немного расширим нашу будущую таблицу добавив в неё поля title и body:

Schema::create('notes', function (Blueprint $table) {
    $table->increments('id');
    $table->string('title');
    $table->text('body');
    $table->timestamps();
});

Всё можно выполнять миграцию:

php artisan migrate --path=/database/migrations/create_notes_table

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

В контроллере нам необходимо будет описать все доступные нам методы которые нам создал роутер.

Итак за показ всех доступных заметок у нас будет отвечать метод index()

public function index()
{
    $notes = Note::orderBy('id', 'desc')
        ->paginate(10); // Трюк для получения пагинатора
    return view('note.index')->with('nodes', $nodes);
}

Как вы видите здесь мы просто получаем все объекты нашего класса Note

Ну и давайте по быстрому сюда прикрутим вьюшку. Да кстати что бы не засорять папку Views рекомендую для всех типов создавать свои подпапки в нашем случае это будет папка note и в ней вложенный файл index.blade.php у меня получился приметрно такой вид:

@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Node admin  <a href="/node/create" class="btn btn-success"> + Create</a></div>
                <div class="panel-body">
                    <table class="table">
                        <thead>
                        <tr>
                            <th>id</th>
                            <th>Title</th>
                            <th>Type</th>
                            <th>Action</th>
                        </tr>
                        </thead>
                        <tbody>
                        @foreach($nodes as $node)
                            <tr>
                                <td>{{ $node->id }}</td>
                                <td>{{ $node->title }}</td>
                                <td>{{ $node->type }}</td>
                                <td style="text-align:right;">
                                    <a href="/node/{{ $node->id }}" class="btn btn-info">View</a>
                                    <a href="/node/{{ $node->id }}/edit" class="btn btn-success">Edit</a>
                                    <a href="/node/{{ $node->id }}/destroy" class="btn btn-danger">Dellete</a>
                                </td>
                            </tr>
                        @endforeach
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Валидация и ошибки

Для того чтобы организовать валидацию подключите в контроллере класс валидации

use Illuminate\Foundation\Validation\ValidatesRequests;

Затем внутри класса контроллера укажите глобальное использование класса валидации

class NoteController extends Controller
{
    use ValidatesRequests;

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

public function store(Request $request)
{
    $this->validate($request, [
        'title' => 'required',
        'slug' => 'uniqe'
    ]);

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

@if($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach($errors->all() as $error)
                <li>{{$error}}</li>
            @endforeach
        </ul>
    </div>
@endif