Eloquent ORM: модели и CRUD

Рейтинг: 75.8% · 12 голосов
Курс по Laravel: маршруты, Eloquent, Blade, миграции, очереди и API. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
oleg_php
Сообщения: 25
Зарегистрирован: 14 май 2026, 08:06

Eloquent ORM: модели и CRUD

Сообщение oleg_php »

АкадемияLaravel с нуляГлава 5 из 18
Оглавление курса (18)
  1. Знакомство с Laravel и установка окружения
  2. Маршруты и контроллеры
  3. Blade: шаблоны и вёрстка страниц
  4. Миграции и структура базы данных
  5. Eloquent ORM: модели и CRUD (вы здесь)
  6. Связи в Eloquent: hasMany, belongsTo и другие
  7. Формы и валидация данных
  8. Аутентификация пользователей
  9. Middleware и защита маршрутов
  10. Очереди и фоновые задачи
  11. Отправка почты и уведомления
  12. Строим REST API на Laravel
  13. Авторизация: Gates и Policies
  14. Работа с файлами: загрузка, Storage, диски local и S3
  15. Тестирование: Pest и PHPUnit, фабрики, сидеры, RefreshDatabase
  16. Сервис-контейнер, провайдеры, свои artisan-команды и планировщик
  17. Деплой в продакшен и обзор современного фронтенда (Vite, Livewire, Inertia)
  18. События и слушатели, кеширование, логирование
В четвёртой главе мы описали структуру базы миграциями, но сами данные пока трогали разве что через консоль mysql. Пора это исправить. В этой главе подключим Eloquent, штатную ORM Laravel, и научимся создавать, читать, обновлять и удалять записи без единой строчки SQL.

Что такое Eloquent:

Eloquent реализует паттерн Active Record: каждой таблице соответствует класс-модель, а каждая строка таблицы превращается в объект этого класса. Создали объект, заполнили поля, вызвали save(), и Eloquent сам соберёт INSERT. Главное удобство в том, что вы работаете с привычными PHP-объектами, а не клеите строки запросов руками.

Создаём модель:

Код: Выделить всё

php artisan make:model Post
Команда создаст файл app/Models/Post.php. Если миграции для таблицы ещё нет, добавьте флаг -m, и artisan сгенерирует её заодно. Eloquent живёт конвенциями: класс Post автоматически ищет таблицу posts (имя класса в snake_case и во множественном числе), первичным ключом считает колонку id, а created_at и updated_at заполняет сам. Пока вы следуете конвенциям, конфигурировать нечего.

Руками нужно сделать две вещи: перечислить поля, доступные для массового заполнения, и описать касты типов. Без каста published_at приедет из базы обычной строкой, а с кастом datetime станет объектом Carbon, который можно сравнивать с now() и форматировать:

Код: Выделить всё

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = ['title', 'body', 'published_at'];

    protected function casts(): array
    {
        return [
            'published_at' => 'datetime',
        ];
    }
}
Метод casts() появился в Laravel 11, в старых проектах встретите свойство protected $casts, оно работает так же.

CRUD на практике:

Удобнее всего пробовать в tinker, интерактивной консоли Laravel. Запустите её командой php artisan tinker в корне проекта и выполняйте код построчно.

Код: Выделить всё

// создание
$post = Post::create([
    'title' => 'Первый пост',
    'body'  => 'Привет, Eloquent',
]);

// чтение
$all   = Post::all();
$post  = Post::find(1);
$fresh = Post::where('published_at', '<=', now())->latest()->get();

// обновление
$post->title = 'Новый заголовок';
$post->save();

// то же самое одним вызовом, тоже подчиняется $fillable
$post->update(['title' => 'Новый заголовок']);

// удаление
$post->delete();
Post::destroy([2, 3]);
create() принимает массив полей и сразу пишет запись в базу. Альтернатива в два шага: new Post, заполнить свойства, save(). Второй вариант не зависит от $fillable, но в контроллерах чаще встречается именно create(), потому что туда удобно передать провалидированные данные формы (этим займёмся в седьмой главе). Для обновления симметричный приём: $post->update([...]) принимает массив, прогоняет его через $fillable и сразу сохраняет. В контроллерах это самый ходовой вариант.

В контроллере типовое чтение выглядит так:

Код: Выделить всё

public function show(int $id)
{
    $post = Post::findOrFail($id);

    return view('posts.show', ['post' => $post]);
}
findOrFail() при отсутствии записи бросит исключение, которое Laravel превратит в страницу 404. Обычный find() вернёт null, и ошибку вы поймаете дальше по коду, в куда менее понятном месте.

Писать findOrFail() в каждом методе при этом не обязательно. Если типизировать параметр моделью, Laravel сам найдёт запись по {post} из маршрута и сам отдаст 404, когда её нет. Это называется route model binding, и в свежем коде вы чаще увидите именно его:

Код: Выделить всё

public function show(Post $post)
{
    return view('posts.show', ['post' => $post]);
}
Типичные грабли:

Поле молча пропадает при create(). Когда $fillable заполнен, Eloquent по умолчанию не бросает исключений: лишние атрибуты просто отбрасываются, и вы получаете запись без нужного поля и без единой ошибки. MassAssignmentException вылетает только если у модели вообще нет $fillable или включён строгий режим. Лечится добавлением поля в $fillable, а не заменой на $guarded = [], которая отключает защиту целиком. И включите в локальном окружении Model::shouldBeStrict() (или точечно Model::preventSilentlyDiscardingAttributes()) в AppServiceProvider: тихое отбрасывание превратится в явное исключение, и такие баги перестанут прятаться.

Post::all() на боевой таблице. Метод тащит в память все строки разом. На учебной базе это незаметно, а на таблице в полмиллиона записей положит процесс. Для списков используйте paginate(15), а в Blade-шаблоне из третьей главы выводите ссылки пагинации через $posts->links(). Для фоновой обработки всей таблицы (экспорт, пересчёт полей) есть chunk(), lazy() и cursor(), они читают записи порциями. Подробно разберём их позже, пока просто запомните, что all() для таких задач не подходит.

Таблица названа не по конвенции. Обычная история с legacy-базами. Укажите имя явно, protected $table = 'tbl_articles'; в модели, и Eloquent перестанет искать несуществующую таблицу.

Массовый update идёт мимо событий модели. Запрос вида Post::where(...)->update([...]) проставит updated_at сам, об этом заботится Eloquent-билдер. А вот события модели (saving, updated и прочие) и наблюдатели при таком обновлении не сработают: модели даже не загружаются из базы. Метки времени теряются, только если спуститься ещё ниже, в DB::table()->update() или сырой SQL, там Laravel не трогает вообще ничего. Для одиночных записей надёжнее find() плюс save() или $post->update(): они и события вызовут, и метки обновят.

Что усвоили:

Модель отражает таблицу, конвенции избавляют от конфигурации, $fillable защищает от массового заполнения лишних полей, а весь CRUD укладывается в пять-шесть методов. В следующей главе свяжем модели между собой: посты получат автора и комментарии через hasMany и belongsTo.
👍4 ❤️2 🔥1 😄 🤔2
✔ Лучший ответ сформирован автоматически — quantumdruid
oleg_php писал(а):findOrFail() при отсутствии записи бросит исключение, которое Laravel превратит в страницу 404 а route model binding из второй главы это оно же под капотом? то есть если написать public function show(Post $post), то findOrFail сам вызовется и руками его дергать не надо?
Перейти к ответу →
Аватара пользователя
elastic_lord
Сообщения: 1
Зарегистрирован: 14 май 2026, 06:42

Re: Eloquent ORM: модели и CRUD

Сообщение elastic_lord »

а можно вместо перечисления полей просто написать protected $guarded = []; ? видел такое в паре опенсорс проектов и вроде живут. если в контроллере всё равно валидация и в create() уходит только $request->validated(), то левые поля туда и так не попадут. или я что-то упускаю?
👍1 ❤️2 🔥 😄 🤔1
Аватара пользователя
quantumdruid
Сообщения: 2
Зарегистрирован: 18 май 2026, 21:44

Re: Eloquent ORM: модели и CRUD

Сообщение quantumdruid »

✔ Лучший ответ — сформирован автоматически
oleg_php писал(а):findOrFail() при отсутствии записи бросит исключение, которое Laravel превратит в страницу 404
а route model binding из второй главы это оно же под капотом? то есть если написать public function show(Post $post), то findOrFail сам вызовется и руками его дергать не надо?
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
polka13
Сообщения: 2
Зарегистрирован: 14 май 2026, 02:05

Re: Eloquent ORM: модели и CRUD

Сообщение polka13 »

за tinker отдельное спасибо. полчаса гонял в нём запросы, пока не понял, где у меня условие в where кривое. раньше бы дебажил через dd() в контроллере и обновлял страницу после каждой правки
👍1 ❤️ 🔥 😄 🤔2
Аватара пользователя
scala91
Сообщения: 1
Зарегистрирован: 15 май 2026, 20:47

Re: Eloquent ORM: модели и CRUD

Сообщение scala91 »

по поводу legacy таблиц добавлю из своего опыта: если ещё и первичный ключ не id, а какой-нибудь post_id, то нужен protected $primaryKey = 'post_id', иначе find() молча вернёт null и будешь час гадать почему. наступал на это на старом проекте с самописной базой
👍 ❤️1 🔥 😄 🤔
Ответить
← Предыдущая глава
Миграции и структура базы данных
Следующая глава →
Связи в Eloquent: hasMany, belongsTo и другие

Все главы курса «Laravel с нуля»

Поделиться темой: ✈ Telegram VK

Вернуться в «Laravel с нуля»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость