Практика: CRUD-приложение от и до

CRUD — это четыре базовые операции над данными (создание, чтение, обновление, удаление), и собрав их вместе, вы получаете каркас почти любого приложения.

Суть: аббревиатура CRUD — Create, Read, Update, Delete. В Laravel это семь методов ресурсного контроллера, связанных с маршрутами, моделью Eloquent и шаблонами Blade.

Пришло время собрать всё изученное в одном проекте. Подавляющее большинство веб-приложений — это, по сути, управление списками сущностей: товары, статьи, задачи, пользователи. Над каждой сущностью выполняются четыре операции: создать, прочитать, обновить, удалить — это и есть CRUD. Освоив один полный CRUD, вы сможете построить почти любую функцию.

В Laravel CRUD ложится на знакомые кирпичики. Маршруты создаёт Route::resource, логику держит ресурсный контроллер, данные — миграция и модель Eloquent, интерфейс — шаблоны Blade, а корректность ввода обеспечивает валидация. Соберём это на примере сущности «Задача» (Task).

Карта CRUD

  CRUD = 4 операции -> 7 маршрутов
  ----------------------------------
  CREATE  | GET  /tasks/create  -> форма
          | POST /tasks         -> store()  сохранить
  READ    | GET  /tasks          -> index()  список
          | GET  /tasks/{id}      -> show()   одна
  UPDATE  | GET  /tasks/{id}/edit -> форма
          | PUT  /tasks/{id}      -> update() обновить
  DELETE  | DELETE /tasks/{id}    -> destroy() удалить

Контроллер целиком

<?php
namespace App\Http\Controllers;

use App\Models\Task;
use Illuminate\Http\Request;

class TaskController extends Controller
{
    public function index()
    {
        $tasks = Task::latest()->paginate(10);
        return view('tasks.index', ['tasks' => $tasks]);
    }

    public function create()
    {
        return view('tasks.create');
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'title' => 'required|string|max:255',
            'done'  => 'boolean',
        ]);
        Task::create($data);
        return redirect()->route('tasks.index')->with('ok', 'Создано');
    }

    public function edit(Task $task)
    {
        return view('tasks.edit', ['task' => $task]);
    }

    public function update(Request $request, Task $task)
    {
        $data = $request->validate(['title' => 'required|max:255']);
        $task->update($data);
        return redirect()->route('tasks.index');
    }

    public function destroy(Task $task)
    {
        $task->delete();
        return redirect()->route('tasks.index');
    }
}

Шаблон списка

@extends('layouts.app')
@section('content')
    @if (session('ok'))
        <p>{{ session('ok') }}</p>
    @endif

    <a href="{{ route('tasks.create') }}">Новая задача</a>
    <ul>
        @foreach ($tasks as $task)
            <li>
                {{ $task->title }}
                <a href="{{ route('tasks.edit', $task) }}">Изменить</a>
                <form method="POST" action="{{ route('tasks.destroy', $task) }}">
                    @csrf @method('DELETE')
                    <button>Удалить</button>
                </form>
            </li>
        @endforeach
    </ul>
    {{ $tasks->links() }}   {{-- пагинация --}}
@endsection

Как работает под капотом: связь всех слоёв

Когда вы нажимаете «Создать», форма шлёт POST на /tasks. Роутер направляет его в store(), валидация проверяет ввод, Eloquent через Task::create() пишет в базу, затем редирект ведёт обратно на список, где index() снова достаёт задачи и Blade рисует их. Это полный круг MVC, который вы прошли за весь курс.

  ПОЛНЫЙ ЦИКЛ CREATE
  ------------------
  форма (Blade) --POST /tasks--> Router
                                   |
                              store() контроллер
                                   |
                              validate() правила
                                   |
                              Task::create() Eloquent -> БД
                                   |
                              redirect -> index()
                                   |
                              Blade рисует список

Смоделируем CRUD над списком в памяти на Python — те же четыре операции, что и в Laravel.

Попробуй сам ▶

# CRUD над списком задач (аналог TaskController)
tasks, next_id = {}, 1

def store(title):                 # CREATE
    global next_id
    tasks[next_id] = {'title': title, 'done': False}
    next_id += 1

def index():                      # READ (список)
    return list(tasks.items())

def update(tid, title):           # UPDATE
    if tid in tasks:
        tasks[tid]['title'] = title

def destroy(tid):                 # DELETE
    tasks.pop(tid, None)

store('Изучить маршруты')
store('Изучить Eloquent')
update(1, 'Изучить маршруты (готово)')
destroy(2)
for tid, t in index():
    print(tid, t['title'])

Частые ошибки

  • Забыть @method('DELETE'). Без него форма удаления уйдёт как POST и маршрут не совпадёт.
  • Не защитить маршруты. CRUD без middleware auth позволит чужим менять данные.
  • Игнорировать пагинацию. Task::all() на большой таблице повесит страницу — используйте paginate().

Best practices

  • Применяйте Route::resource и неявное связывание моделей — меньше кода.
  • Выносите правила в Form Request, а доступ закрывайте политиками.
  • Следуйте паттерну POST-Redirect-GET и показывайте флеш-сообщения через session().

Итог: CRUD соединяет все слои Laravel — маршруты, контроллер, валидацию, Eloquent и Blade — в работающее приложение. Освоив этот каркас, вы готовы строить реальные проекты на Laravel.

Проверьте себя
1. Что означает аббревиатура CRUD?
ACreate, Read, Update, Delete
BController, Route, User, Database
CCache, Run, Update, Deploy
DClass, Request, URL, Data
2. Почему для списка лучше использовать paginate() вместо all()?
Apaginate() красивее
Ball() на большой таблице загружает все записи сразу и тормозит страницу
Call() не работает
Dpaginate() удаляет данные