Создание CRUD-приложения на PHP с помощью Laravel
CRUD-приложение объединяет четыре базовые операции с данными: создание записи, просмотр списка, изменение и удаление. Такой сценарий встречается в каталогах товаров, системах учета клиентов, блогах, внутренних панелях управления и мобильных приложениях с серверной частью. Laravel упрощает реализацию этих функций за счет маршрутизации, ORM, миграций, шаблонов и встроенной валидации.
В качестве примера создадим небольшой справочник задач. Пользователь сможет добавить задачу, увидеть все записи, открыть отдельную страницу, изменить сведения и удалить ненужный элемент. Подход подходит для Laravel-приложений, которые используют MySQL, PostgreSQL или SQLite.
Laravel хорошо подходит для PHP-разработки, где важны ясная структура проекта и быстрое создание прототипов. Модель отвечает за работу с таблицей, контроллер связывает запросы с бизнес-логикой, а Blade-шаблоны формируют HTML-интерфейс. Благодаря этому код можно постепенно расширять, не превращая проект в набор несвязанных файлов.
Ниже используется классическая серверная архитектура Laravel. Она будет полезна для административной панели, сайта с личным кабинетом или REST-сервиса, который позже можно подключить к приложению на Android, iOS, Windows, macOS или Linux.
Подготовка проекта Laravel
Для работы понадобятся PHP, Composer и выбранная база данных. Новое приложение создается командой:
composer create-project laravel/laravel task-manager
cd task-manager
php artisan serve
После запуска встроенного сервера проект будет доступен по адресу http://127.0.0.1:8000. Параметры подключения к базе данных хранятся в файле .env. Для MySQL пример конфигурации выглядит так:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=task_manager
DB_USERNAME=root
DB_PASSWORD=
Название базы данных нужно предварительно создать в MySQL или другой СУБД. Laravel не хранит параметры окружения в исходном коде, поэтому файл .env не следует добавлять в публичный репозиторий. Для локальной разработки можно использовать SQLite, указав DB_CONNECTION=sqlite и создав файл database/database.sqlite.
После изменения конфигурации полезно очистить кэш настроек:
php artisan config:clear
Основные команды Laravel выполняются через Artisan. С его помощью создаются модели, контроллеры, миграции, сидеры и тесты. Такой единый инструмент заметно ускоряет разработку CRUD-системы.
Миграция и модель задачи
Создадим модель Task вместе с миграцией:
php artisan make:model Task -m
Laravel добавит класс модели в каталог app/Models, а файл миграции — в database/migrations. В миграции опишем структуру таблицы:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->boolean('is_completed')->default(false);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('tasks');
}
};
Поле title будет обязательным названием задачи, description позволит сохранить подробности, а is_completed определит ее состояние. Методы up и down нужны соответственно для применения и отмены изменения схемы базы данных.
Запустим миграцию:
php artisan migrate
Чтобы разрешить массовое заполнение модели, откроем app/Models/Task.php:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
protected $fillable = [
'title',
'description',
'is_completed',
];
protected $casts = [
'is_completed' => 'boolean',
];
}
Свойство $fillable защищает приложение от записи неожиданных полей через массив данных запроса. Приведение is_completed к типу boolean позволяет работать со значением как с логическим флагом, а не как со строкой из базы данных.
Контроллер и маршруты ресурсов
Для CRUD-операций создадим ресурсный контроллер:
php artisan make:controller TaskController --resource
В файле routes/web.php достаточно добавить один маршрут:
use App\Http\Controllers\TaskController;
use Illuminate\Support\Facades\Route;
Route::resource('tasks', TaskController::class);
Эта запись формирует маршруты для списка, создания, сохранения, просмотра, редактирования, обновления и удаления задач. Например, GET /tasks вызывает index, POST /tasks — store, а DELETE /tasks/{task} — destroy.
Контроллер можно заполнить следующим кодом:
namespace App\Http\Controllers;
use App\Models\Task;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class TaskController extends Controller
{
public function index(): View
{
$tasks = Task::latest()->paginate(10);
return view('tasks.index', compact('tasks'));
}
public function create(): View
{
return view('tasks.create');
}
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'title' => ['required', 'string', 'max:255'],
'description' => ['nullable', 'string'],
]);
Task::create($validated);
return redirect()
->route('tasks.index')
->with('success', 'Задача создана');
}
}
Laravel автоматически подставит объект Task в методы, где используется параметр модели. Это называется неявной привязкой модели к маршруту. Пагинация ограничивает число элементов на странице и создает ссылки для перехода между страницами.
Blade-интерфейс для операций CRUD
Создадим общий шаблон resources/views/layouts/app.blade.php. В нем можно разместить HTML-каркас, навигацию и вывод сообщений:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', 'Задачи')</title>
</head>
<body>
@if (session('success'))
<p>{{ session('success') }}</p>
@endif
@yield('content')
</body>
</html>
Страница списка resources/views/tasks/index.blade.php выводит данные модели и ссылки:
@extends('layouts.app')
@section('title', 'Список задач')
@section('content')
<h1>Список задач</h1>
<a href="{{ route('tasks.create') }}">Добавить задачу</a>
@forelse ($tasks as $task)
<article>
<h2>
<a href="{{ route('tasks.show', $task) }}">
{{ $task->title }}
</a>
</h2>
<p>{{ $task->description }}</p>
<p>{{ $task->is_completed ? 'Выполнена' : 'В работе' }}</p>
</article>
@empty
<p>Задач пока нет.</p>
@endforelse
{{ $tasks->links() }}
@endsection
Функция {{ }} автоматически экранирует вывод и снижает риск XSS-атак. Для просмотра отдельной записи понадобится шаблон show.blade.php, а для добавления и редактирования удобно использовать одну форму. Повторяющиеся поля можно вынести в частичный шаблон, чтобы не копировать разметку.
Валидация и сохранение данных
Форма создания задачи должна отправлять данные методом POST и содержать CSRF-токен:
<form method="POST" action="{{ route('tasks.store') }}">
@csrf
<label for="title">Название</label>
<input
id="title"
name="title"
value="{{ old('title') }}"
required
>
@error('title')
<p>{{ $message }}</p>
@enderror
<label for="description">Описание</label>
<textarea name="description">{{ old('description') }}</textarea>
@error('description')
<p>{{ $message }}</p>
@enderror
<button type="submit">Сохранить</button>
</form>
Метод validate проверяет входные параметры до обращения к базе данных. Если название отсутствует или превышает 255 символов, Laravel возвращает пользователя к форме и помещает ошибки в объект сессии. Функция old сохраняет введенные значения, поэтому человеку не приходится заполнять форму заново.
Для редактирования используется скрытое поле метода:
<form method="POST" action="{{ route('tasks.update', $task) }}">
@csrf
@method('PUT')
<input name="title" value="{{ old('title', $task->title) }}">
<textarea name="description">{{ old('description', $task->description) }}</textarea>
<label>
<input
type="checkbox"
name="is_completed"
value="1"
@checked($task->is_completed)
>
Выполнена
</label>
<button type="submit">Обновить</button>
</form>
В контроллере методы изменения и удаления будут выглядеть так:
public function update(Request $request, Task $task): RedirectResponse
{
$validated = $request->validate([
'title' => ['required', 'string', 'max:255'],
'description' => ['nullable', 'string'],
'is_completed' => ['nullable', 'boolean'],
]);
$task->update([
...$validated,
'is_completed' => $request->boolean('is_completed'),
]);
return redirect()
->route('tasks.index')
->with('success', 'Задача обновлена');
}
public function destroy(Task $task): RedirectResponse
{
$task->delete();
return redirect()
->route('tasks.index')
->with('success', 'Задача удалена');
}
Проверка приложения и дальнейшее развитие
Удаление выполняется отдельной формой с методом DELETE:
<form method="POST" action="{{ route('tasks.destroy', $task) }}">
@csrf
@method('DELETE')
<button type="submit">Удалить</button>
</form>
Для реального проекта стоит добавить подтверждение удаления на стороне браузера, авторизацию и проверку прав доступа. Например, пользователь должен видеть и изменять только собственные задачи. Laravel Policies позволяют вынести такие правила из контроллера в отдельный класс.
Работу CRUD-функций удобно проверить через PHPUnit или Pest. Тест должен создать задачу, отправить запрос, проверить редирект и убедиться, что запись появилась в базе данных. Отдельно проверяются невалидные данные, редактирование, удаление и запрет доступа к чужим объектам.
Если приложение будет использоваться как сервер для мобильного или настольного клиента, вместо Blade можно добавить API-маршруты. В этом случае контроллер возвращает JSON, а данные оформляются через API Resources. Авторизацию можно реализовать с помощью Laravel Sanctum, а фоновые уведомления и тяжелые операции — перенести в очереди.
Готовый проект можно разместить на VPS или облачной платформе. Перед публикацией нужно настроить рабочую базу данных, выполнить php artisan migrate --force, отключить режим отладки и сгенерировать ключ приложения. Такая основа подходит для расширения: можно добавить категории, поиск, сортировку, роли пользователей и синхронизацию с внешними клиентами.