rzaripov.kz

Создание 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, отключить режим отладки и сгенерировать ключ приложения. Такая основа подходит для расширения: можно добавить категории, поиск, сортировку, роли пользователей и синхронизацию с внешними клиентами.