Laravel: Eloquent і запити
20 питань · ~20 хв · Версія v3.0
Увійдіть, щоб продовжити
Моделі, зв'язки й N+1, Query Builder, касти, скопи, події, масове присвоєння й конкурентні оновлення - питання всіх рівнів, від junior до lead.
- За спробу
- 20
- У пулі
- 145
- Проходжень
- 0
- Середній бал
- -
- Пройшли на 70%+
- -
Питання для підготовки
52 питанняEloquent - це ORM (object-relational mapping) Laravel, реалізація патерну Active Record. Кожній таблиці відповідає модель (зазвичай у app/Models), рядок таблиці - це екземпляр моделі, а робота з даними виглядає як робота зі звичайними PHP-об'єктами замість написання SQL.
Створити модель (за конвенцією однина: Post → таблиця posts):
php artisan make:model Post -mf # одразу з міграцією та фабрикою
Базові операції (CRUD):
$post = Post::create(['title' => 'Привіт']); // create
$post = Post::find(1); // read
$post->update(['title' => 'Оновлено']); // update
$post->delete(); // delete
Post::where('is_published', true)->latest()->get();
Що дає Eloquent понад Query Builder:
- Зв'язки -
hasOne,hasMany,belongsTo,belongsToMany, поліморфні. - Аксесори/мутатори та касти атрибутів (наприклад, дати, enum, JSON).
- Події моделі та обзервери (
creating,saved,deleted). - Scopes для перевикористання умов запитів.
Eloquent побудований поверх Query Builder, тож ті самі методи (where, orderBy, join) доступні і на моделях.
N+1 виникає, коли ви завантажуєте N моделей одним запитом, а потім у циклі звертаєтесь до їхнього зв'язку - це генерує ще N запитів.
$posts = Post::all(); // 1 запит
foreach ($posts as $post) {
echo $post->author->name; // +1 запит на кожен пост → N запитів
}
Рішення - eager loading через with():
$posts = Post::with('author')->get(); // лише 2 запити загалом
Вкладені та умовні зв'язки:
Post::with(['author', 'comments.user'])->get(); // вкладений eager
Post::with(['comments' => fn ($q) => $q->latest()])->get(); // умовний
Лічильники без завантаження зв'язку - withCount() (без N+1 і без вантаження самих рядків):
$posts = Post::withCount('comments')->get(); // доступ через $post->comments_count
Виявлення: Model::preventLazyLoading(! app()->isProduction()) у boot() кидає виняток на ледачих завантаженнях поза продакшеном; також допомагають Telescope і Debugbar.
Обидва методи виконують запит, але повертають різне:
get()повертає колекцію (Illuminate\Database\Eloquent\Collection) усіх відповідних моделей. Якщо нічого не знайдено - порожню колекцію, а неnull. Підходить, коли треба перебрати кілька записів.first()повертає одну першу модель абоnull, якщо нічого не знайдено. Підходить, коли очікуєш один запис.
$posts = Post::where('active', true)->get(); // Collection (0..N моделей)
$post = Post::where('slug', $slug)->first(); // Post|null
foreach ($posts as $post) { /* ... */ } // get() - ітеруємо
echo $post?->title; // first() - перевіряємо на null
Споріднені методи:
find($id)- пошук за первинним ключем.firstOrFail()/findOrFail()- якfirst()/find(), але кидаютьModelNotFoundException(HTTP 404), якщо запис відсутній.pluck('email')- колекція значень одного стовпця.value('email')- одне скалярне значення з першого рядка.
Підсумок: get() - багато рядків (колекція), first() - один рядок (модель або null).
Eloquent підтримує всі поширені типи зв'язків між таблицями, кожен оголошується методом на моделі:
- One To One -
hasOne/belongsTo. Приклад:User↔Profile. - One To Many -
hasMany/belongsTo. Приклад:Post→ багатоComment. - Many To Many -
belongsToManyчерез проміжну (pivot) таблицю. Приклад:User↔Role. - Has One/Many Through - доступ до віддаленого зв'язку через проміжну модель.
- Polymorphic -
morphTo/morphMany: модель належить кільком типам (наприклад,Commentможе належати іPost, іVideo).
Оголошення зв'язку:
class Post extends Model
{
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
}
class Comment extends Model
{
public function post(): BelongsTo
{
return $this->belongsTo(Post::class);
}
}
Використання:
$post->comments; // колекція коментарів
$comment->post->title; // зворотний бік
Post::with('comments')->get(); // eager loading проти N+1
Завжди завантажуйте потрібні зв'язки через with(), щоб уникнути проблеми N+1.
Mass Assignment - це присвоєння групи атрибутів моделі з масиву (наприклад, Model::create($request->all())). Вразливість виникає, коли користувач підкидає неочікувані поля (скажімо, is_admin).
Захист - білий або чорний список у моделі:
protected $fillable = ['title', 'body']; // дозволено лише ці
// або
protected $guarded = ['id', 'is_admin']; // заборонено ці
Найкраща практика: не передавати $request->all(), а валідувати й передавати $request->validated().
Спершу почитати
Прочитати - ще не значить знати
20 питань, по одному на екран, ~20 хв. Після завершення - розбір кожної помилки з посиланням на питання.