Eloquent: Початок роботи
- Вступ
- Генерація Класів Моделей
- Умовності моделі Eloquent
- Отримання моделей
- Отримання Одиночних Моделей / Агрегатів
- Вставка та оновлення моделей
- Видалення моделей
- Обрізка Моделей
- Реплікація моделей
- Області запитів
- Порівняння Моделей
- Події
Вступ
Laravel включає Eloquent, об'єктно-реляційний відображувач (ORM), який робить взаємодію з вашою базою даних приємною. При використанні Eloquent кожна таблиця бази даних має відповідну "Модель", яка використовується для взаємодії з цією таблицею. Окрім отримання записів з таблиці бази даних, моделі Eloquent дозволяють вставляти, оновлювати та видаляти записи з таблиці.
Перш ніж почати, переконайтеся, що ви налаштували підключення до бази даних у файлі конфігурації вашого застосунку config/database.php. Для отримання додаткової інформації про налаштування вашої бази даних перегляньте документацію з налаштування бази даних.
Генерація Класів Моделей
Щоб почати, давайте створимо модель Eloquent. Моделі зазвичай знаходяться в директорії app\Models і розширюють клас Illuminate\Database\Eloquent\Model. Ви можете використовувати make:model Artisan команду для генерації нової моделі:
php artisan make:model Flight
Якщо ви хочете згенерувати міграцію бази даних під час створення моделі, ви можете використовувати опцію --migration або -m:
php artisan make:model Flight --migration
Ви можете генерувати різні інші типи класів при створенні моделі, такі як фабрики, наповнювачі, політики, контролери та запити форм. Крім того, ці опції можуть бути об'єднані для створення декількох класів одночасно:
# Створити модель та клас FlightFactory... php artisan make:model Flight --factory php artisan make:model Flight -f # Створити модель та клас FlightSeeder... php artisan make:model Flight --seed php artisan make:model Flight -s # Створити модель та клас FlightController... php artisan make:model Flight --controller php artisan make:model Flight -c # Створити модель, ресурсний клас FlightController та класи запитів форми... php artisan make:model Flight --controller --resource --requests php artisan make:model Flight -crR # Створити модель та клас FlightPolicy... php artisan make:model Flight --policy # Створити модель, міграцію, фабрику, сідер і контролер... php artisan make:model Flight -mfsc # Скорочення для створення моделі, міграції, фабрики, сідера, політики, контролера та запитів форми... php artisan make:model Flight --all php artisan make:model Flight -a # Створити проміжну (pivot) модель... php artisan make:model Member --pivot php artisan make:model Member -p
Інспектування Моделей
Іноді може бути складно визначити всі доступні атрибути та відношення моделі, просто переглядаючи її код. Натомість спробуйте команду Artisan model:show, яка надає зручний огляд усіх атрибутів та відношень моделі:
php artisan model:show Flight
Конвенції моделі Eloquent
Моделі, згенеровані командою make:model, будуть розміщені в директорії app/Models. Давайте розглянемо базовий клас моделі та обговоримо деякі ключові конвенції Eloquent:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
// ...
}
Назви таблиць
Після перегляду наведеного вище прикладу, ви могли помітити, що ми не вказали Eloquent, яка таблиця бази даних відповідає нашій моделі Flight. За замовчуванням, "snake case", множинна назва класу буде використовуватися як назва таблиці, якщо інша назва не вказана явно. Отже, в цьому випадку, Eloquent припускатиме, що модель Flight зберігає записи в таблиці flights, тоді як модель AirTrafficController зберігатиме записи в таблиці air_traffic_controllers.
Якщо таблиця бази даних, що відповідає вашій моделі, не відповідає цій конвенції, ви можете вручну вказати ім'я таблиці моделі, визначивши властивість table у моделі:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Таблиця, пов'язана з моделлю.
*
* @var string
*/
protected $table = 'my_flights';
}
Первинні ключі
Eloquent також припускатиме, що відповідна таблиця бази даних кожної моделі має стовпець первинного ключа з назвою id. Якщо необхідно, ви можете визначити захищену властивість $primaryKey у вашій моделі, щоб вказати інший стовпець, який слугує первинним ключем вашої моделі:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Первинний ключ, пов'язаний з таблицею.
*
* @var string
*/
protected $primaryKey = 'flight_id';
}
Крім того, Eloquent припускає, що первинний ключ є зростаючим цілим числом, що означає, що Eloquent автоматично перетворить первинний ключ на ціле число. Якщо ви бажаєте використовувати незростаючий або нечисловий первинний ключ, ви повинні визначити публічну властивість $incrementing у вашій моделі, встановлену в false:
<?php
class Flight extends Model
{
/**
* Вказує, чи є ID моделі автоінкрементним.
*
* @var bool
*/
public $incrementing = false;
}
Якщо первинний ключ вашої моделі не є цілим числом, ви повинні визначити захищену властивість $keyType у вашій моделі. Ця властивість повинна мати значення string:
<?php
class Flight extends Model
{
/**
* Тип даних первинного ключа ID.
*
* @var string
*/
protected $keyType = 'string';
}
"Складені" первинні ключі
Eloquent вимагає, щоб кожна модель мала принаймні один унікальний ідентифікатор "ID", який може служити її первинним ключем. "Складені" первинні ключі не підтримуються моделями Eloquent. Однак ви можете додати додаткові багатоколонкові унікальні індекси до таблиць вашої бази даних на додаток до унікального первинного ключа таблиці.
UUID та ULID ключі
Замість використання автоінкрементних цілих чисел як первинних ключів вашої моделі Eloquent, ви можете вибрати використання UUID. UUID - це універсально унікальні алфавітно-цифрові ідентифікатори, які мають довжину 36 символів.
Якщо ви хочете, щоб модель використовувала UUID ключ замість автоінкрементного цілочисельного ключа, ви можете використати трейд Illuminate\Database\Eloquent\Concerns\HasUuids у моделі. Звичайно, ви повинні переконатися, що модель має еквівалентний UUID стовпець первинного ключа:
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use HasUuids;
// ...
}
$article = Article::create(['title' => 'Traveling to Europe']);
$article->id; // "8f8e8478-9035-4d23-b9a7-62f4d2612ce5"
За замовчуванням, трейд HasUuids буде генерувати "впорядковані" UUID для ваших моделей. Ці UUID є більш ефективними для індексованого зберігання в базі даних, оскільки їх можна сортувати лексикографічно.
Ви можете перевизначити процес генерації UUID для заданої моделі, визначивши метод newUniqueId у моделі. Крім того, ви можете вказати, які стовпці повинні отримувати UUID, визначивши метод uniqueIds у моделі:
use Ramsey\Uuid\Uuid;
/**
* Генеруйте новий UUID для моделі.
*/
public function newUniqueId(): string
{
return (string) Uuid::uuid4();
}
/**
* Отримати стовпці, які повинні отримати унікальний ідентифікатор.
*
* @return array<int, string>
*/
public function uniqueIds(): array
{
return ['id', 'discount_code'];
}
Якщо ви бажаєте, ви можете використовувати "ULIDs" замість UUIDs. ULIDs схожі на UUIDs; однак, вони мають лише 26 символів у довжину. Як і впорядковані UUIDs, ULIDs є лексикографічно сортувальними для ефективної індексації бази даних. Щоб використовувати ULIDs, ви повинні застосувати трейд Illuminate\Database\Eloquent\Concerns\HasUlids у вашій моделі. Ви також повинні переконатися, що модель має еквівалентний первинний ключ ULID:
use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use HasUlids;
// ...
}
$article = Article::create(['title' => 'Traveling to Asia']);
$article->id; // "01gd4d3tgrrfqeda94gdbtdk5c"
Мітки часу
За замовчуванням Eloquent очікує, що стовпці created_at та updated_at існують у відповідній таблиці бази даних вашої моделі. Eloquent автоматично встановить значення цих стовпців, коли моделі створюються або оновлюються. Якщо ви не хочете, щоб ці стовпці автоматично керувалися Eloquent, ви повинні визначити властивість $timestamps у вашій моделі зі значенням false:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Вказує, чи модель повинна мати часові мітки.
*
* @var bool
*/
public $timestamps = false;
}
Якщо вам потрібно налаштувати формат часових міток вашої моделі, встановіть властивість $dateFormat у вашій моделі. Ця властивість визначає, як атрибути дати зберігаються в базі даних, а також їх формат, коли модель серіалізується в масив або JSON:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Формат зберігання стовпців дати моделі.
*
* @var string
*/
protected $dateFormat = 'U';
}
Якщо вам потрібно налаштувати назви стовпців, які використовуються для зберігання міток часу, ви можете визначити константи CREATED_AT та UPDATED_AT у вашій моделі:
<?php
class Flight extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
}
Якщо ви хочете виконати операції з моделлю без зміни її позначки часу updated_at, ви можете виконати операції з моделлю в межах замикання, переданого методу withoutTimestamps:
Model::withoutTimestamps(fn () => $post->increment('reads'));
Підключення до бази даних
За замовчуванням всі моделі Eloquent використовуватимуть підключення до бази даних, яке налаштоване для вашого застосунку. Якщо ви хочете вказати інше підключення, яке слід використовувати при взаємодії з конкретною моделлю, ви повинні визначити властивість $connection у моделі:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* З'єднання з базою даних, яке має використовуватися моделлю.
*
* @var string
*/
protected $connection = 'mysql';
}
Значення Атрибутів За Замовчуванням
За замовчуванням, новий екземпляр моделі не міститиме жодних значень атрибутів. Якщо ви хочете визначити значення за замовчуванням для деяких атрибутів вашої моделі, ви можете визначити властивість $attributes у вашій моделі. Значення атрибутів, розміщені в масиві $attributes, повинні бути у своєму сирому, "зберігаємому" форматі, так ніби вони щойно були прочитані з бази даних:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * Значення атрибутів моделі за замовчуванням. * * @var array */ protected $attributes = [ 'options' => '[]', 'delayed' => false, ]; }
Налаштування Строгості Eloquent
Laravel пропонує кілька методів, які дозволяють налаштувати поведінку та "строгість" Eloquent у різних ситуаціях.
Спочатку метод preventLazyLoading приймає необов'язковий булевий аргумент, який вказує, чи слід запобігати відкладеному завантаженню. Наприклад, ви можете бажати вимкнути відкладене завантаження лише в непроизводничих середовищах, щоб ваше виробниче середовище продовжувало функціонувати нормально, навіть якщо відкладене завантаження відношення випадково присутнє у виробничому коді. Зазвичай цей метод слід викликати в методі boot вашого застосунку AppServiceProvider:
use Illuminate\Database\Eloquent\Model; /** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { Model::preventLazyLoading(! $this->app->isProduction()); }
Також, ви можете вказати Laravel викидати виняток при спробі заповнити атрибут, який не можна заповнити, викликавши метод preventSilentlyDiscardingAttributes. Це може допомогти запобігти несподіваним помилкам під час локальної розробки, коли ви намагаєтеся встановити атрибут, який не було додано до масиву fillable моделі:
Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction());
Отримання Моделей
Після того як ви створили модель і її відповідну таблицю бази даних, ви готові почати отримувати дані з вашої бази даних. Ви можете уявити кожну модель Eloquent як потужний конструктор запитів, що дозволяє вам плавно виконувати запити до таблиці бази даних, пов'язаної з моделлю. Метод моделі all отримає всі записи з відповідної таблиці бази даних моделі:
use App\Models\Flight;
foreach (Flight::all() as $flight) {
echo $flight->name;
}
Побудова запитів
Метод Eloquent all поверне всі результати з таблиці моделі. Однак, оскільки кожна модель Eloquent слугує як конструктор запитів, ви можете додати додаткові обмеження до запитів, а потім викликати метод get для отримання результатів:
$flights = Flight::where('active', 1)
->orderBy('name')
->limit(10)
->get();
Оскільки моделі Eloquent є конструкторами запитів, вам слід переглянути всі методи, надані конструктором запитів Laravel. Ви можете використовувати будь-який з цих методів при написанні ваших запитів Eloquent.
Оновлення Моделей
Якщо у вас вже є екземпляр моделі Eloquent, отриманий з бази даних, ви можете "оновити" модель за допомогою методів fresh та refresh. Метод fresh повторно отримає модель з бази даних. Існуючий екземпляр моделі не буде змінено:
$flight = Flight::where('number', 'FR 900')->first();
$freshFlight = $flight->fresh();
Метод refresh повторно оновить існуючу модель, використовуючи свіжі дані з бази даних. Крім того, всі завантажені зв'язки також будуть оновлені:
$flight = Flight::where('number', 'FR 900')->first();
$flight->number = 'FR 456';
$flight->refresh();
$flight->number; // "FR 900"
Колекції
Як ми бачили, методи Eloquent, такі як all і get, отримують кілька записів з бази даних. Однак ці методи не повертають звичайний PHP масив. Натомість повертається екземпляр Illuminate\Database\Eloquent\Collection.
Клас Collection Eloquent розширює базовий клас Laravel Illuminate\Support\Collection, який надає різноманітні корисні методи для взаємодії з колекціями даних. Наприклад, метод reject може бути використаний для видалення моделей з колекції на основі результатів викликаного замикання:
$flights = Flight::where('destination', 'Paris')->get();
$flights = $flights->reject(function (Flight $flight) {
return $flight->cancelled;
});
На додаток до методів, наданих базовим класом колекцій Laravel, клас колекцій Eloquent надає декілька додаткових методів, які спеціально призначені для взаємодії з колекціями моделей Eloquent.
Оскільки всі колекції Laravel реалізують ітерабельні інтерфейси PHP, ви можете перебирати колекції так, ніби це масив:
foreach ($flights as $flight) {
echo $flight->name;
}
Розбиття Результатів на Частини
Ваш застосунок може вичерпати пам'ять, якщо ви спробуєте завантажити десятки тисяч записів Eloquent за допомогою методів all або get. Замість використання цих методів, метод chunk може бути використаний для обробки великої кількості моделей більш ефективно.
Метод chunk отримує підмножину моделей Eloquent, передаючи їх у замикання для обробки. Оскільки за один раз отримується лише поточний фрагмент моделей Eloquent, метод chunk значно зменшує використання пам'яті при роботі з великою кількістю моделей:
use App\Models\Flight;
use Illuminate\Database\Eloquent\Collection;
Flight::chunk(200, function (Collection $flights) {
foreach ($flights as $flight) {
// ...
}
});
Перший аргумент, переданий методу chunk, це кількість записів, які ви бажаєте отримати за "шматок". Замикання, передане як другий аргумент, буде викликано для кожного шматка, отриманого з бази даних. Запит до бази даних буде виконано для отримання кожного шматка записів, переданих до замикання.
Якщо ви фільтруєте результати методу chunk на основі стовпця, який ви також будете оновлювати під час ітерації над результатами, вам слід використовувати метод chunkById. Використання методу chunk у таких сценаріях може призвести до несподіваних і непослідовних результатів. Внутрішньо метод chunkById завжди буде отримувати моделі зі стовпцем id, більшим за останню модель у попередньому чанку:
Flight::where('departed', true)
->chunkById(200, function (Collection $flights) {
$flights->each->update(['departed' => false]);
}, column: 'id');
Оскільки методи chunkById та lazyById додають власні умови "where" до запиту, що виконується, зазвичай слід логічно групувати власні умови в межах замикання:
Flight::where(function ($query) {
$query->where('delayed', true)->orWhere('cancelled', true);
})->chunkById(200, function (Collection $flights) {
$flights->each->update([
'departed' => false,
'cancelled' => true
]);
}, column: 'id');
Пакетна обробка за допомогою ледачих колекцій
Метод lazy працює подібно до методу chunk в тому сенсі, що за лаштунками він виконує запит частинами. Однак, замість того щоб передавати кожну частину безпосередньо в зворотний виклик, метод lazy повертає сплощену LazyCollection моделей Eloquent, що дозволяє взаємодіяти з результатами як з єдиним потоком:
use App\Models\Flight;
foreach (Flight::lazy() as $flight) {
// ...
}
Якщо ви фільтруєте результати методу lazy на основі стовпця, який ви також будете оновлювати під час ітерації над результатами, вам слід використовувати метод lazyById. Внутрішньо метод lazyById завжди буде отримувати моделі зі стовпцем id, більшим за останню модель у попередньому блоці:
Flight::where('departed', true)
->lazyById(200, column: 'id')
->each->update(['departed' => false]);
Ви можете відфільтрувати результати на основі спадного порядку id, використовуючи метод lazyByIdDesc.
Cursors
Подібно до методу lazy, метод cursor може бути використаний для значного зменшення споживання пам'яті вашим застосунком при ітерації через десятки тисяч записів моделі Eloquent.
Метод cursor виконає лише один запит до бази даних; однак окремі моделі Eloquent не будуть гідратовані, поки вони фактично не будуть ітеровані. Тому лише одна модель Eloquent зберігається в пам'яті в будь-який момент часу під час ітерації курсора.
Оскільки метод cursor утримує в пам'яті лише одну модель Eloquent за раз, він не може завантажувати відносини заздалегідь. Якщо вам потрібно завантажити відносини заздалегідь, розгляньте можливість використання методу lazy натомість.
Внутрішньо метод cursor використовує PHP генератори для реалізації цієї функціональності:
use App\Models\Flight;
foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) {
// ...
}
cursor повертає екземпляр Illuminate\Support\LazyCollection. Ліниві колекції дозволяють використовувати багато методів колекцій, доступних у типових колекціях Laravel, завантажуючи в пам'ять лише одну модель за раз:
use App\Models\User;
$users = User::cursor()->filter(function (User $user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
Хоча метод cursor використовує набагато менше пам'яті, ніж звичайний запит (утримуючи в пам'яті лише одну модель Eloquent за раз), він все одно зрештою вичерпає пам'ять. Це відбувається через те, що драйвер PDO у PHP внутрішньо кешує всі необроблені результати запиту у своєму буфері. Якщо ви маєте справу з дуже великою кількістю записів Eloquent, розгляньте можливість використання методу lazy замість цього.
Розширені підзапити
Вибірки з підзапитами
Eloquent також пропонує розширену підтримку підзапитів, що дозволяє отримувати інформацію з пов'язаних таблиць в одному запиті. Наприклад, уявімо, що у нас є таблиця destinations з пунктами призначення та таблиця flights з рейсами до цих пунктів призначення. Таблиця flights містить стовпець arrived_at, який вказує, коли рейс прибув до пункту призначення.
Використовуючи функціональність підзапитів, доступну в методах select та addSelect конструктора запитів, ми можемо вибрати всі destinations та назву рейсу, який нещодавно прибув до цього пункту призначення, використовуючи один запит:
use App\Models\Destination;
use App\Models\Flight;
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
])->get();
Сортування підзапитів
Крім того, функція orderBy конструктора запитів підтримує підзапити. Продовжуючи використовувати наш приклад з рейсами, ми можемо скористатися цією функціональністю, щоб відсортувати всі пункти призначення на основі того, коли останній рейс прибув до цього пункту призначення. Знову ж таки, це може бути зроблено під час виконання одного запиту до бази даних:
return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
)->get();
Отримання Одиничних Моделей / Агрегатів
На додаток до отримання всіх записів, що відповідають заданому запиту, ви також можете отримати окремі записи, використовуючи методи find, first або firstWhere. Замість повернення колекції моделей, ці методи повертають один екземпляр моделі:
use App\Models\Flight;
// Отримати модель за її первинним ключем...
$flight = Flight::find(1);
// Отримати першу модель, що відповідає умовам запиту...
$flight = Flight::where('active', 1)->first();
// Альтернатива для отримання першої моделі, що відповідає умовам запиту...
$flight = Flight::firstWhere('active', 1);
Іноді ви можете захотіти виконати іншу дію, якщо результатів не знайдено. Методи findOr та firstOr повернуть один екземпляр моделі або, якщо результатів не знайдено, виконають передану замикання. Значення, повернене замиканням, буде вважатися результатом методу:
$flight = Flight::findOr(1, function () {
// ...
});
$flight = Flight::where('legs', '>', 3)->firstOr(function () {
// ...
});
Винятки Не Знайдено
Іноді ви можете захотіти викинути виняток, якщо модель не знайдена. Це особливо корисно в маршрутах або контролерах. Методи findOrFail та firstOrFail отримають перший результат запиту; однак, якщо результат не знайдено, буде викинуто Illuminate\Database\Eloquent\ModelNotFoundException:
$flight = Flight::findOrFail(1);
$flight = Flight::where('legs', '>', 3)->firstOrFail();
Якщо ModelNotFoundException не перехоплено, клієнту автоматично надсилається відповідь HTTP 404:
use App\Models\Flight;
Route::get('/api/flights/{id}', function (string $id) {
return Flight::findOrFail($id);
});
Отримання або Створення Моделей
Метод firstOrCreate спробує знайти запис у базі даних, використовуючи задані пари стовпець/значення. Якщо модель не може бути знайдена в базі даних, буде вставлено запис з атрибутами, що є результатом об'єднання першого масиву аргументів з необов'язковим другим масивом аргументів:
Метод firstOrNew, як і firstOrCreate, спробує знайти запис у базі даних, що відповідає заданим атрибутам. Однак, якщо модель не буде знайдена, буде повернуто новий екземпляр моделі. Зверніть увагу, що модель, повернена методом firstOrNew, ще не збережена в базі даних. Вам потрібно вручну викликати метод save, щоб зберегти її:
use App\Models\Flight;
// Отримати рейс за назвою або створити його, якщо він не існує...
$flight = Flight::firstOrCreate([
'name' => 'London to Paris'
]);
// Отримати рейс за назвою або створити його з атрибутами name, delayed і arrival_time...
$flight = Flight::firstOrCreate(
['name' => 'London to Paris'],
['delayed' => 1, 'arrival_time' => '11:30']
);
// Отримати рейс за назвою або створити новий екземпляр Flight...
$flight = Flight::firstOrNew([
'name' => 'London to Paris'
]);
// Отримати рейс за назвою або створити з атрибутами name, delayed і arrival_time...
$flight = Flight::firstOrNew(
['name' => 'Tokyo to Sydney'],
['delayed' => 1, 'arrival_time' => '11:30']
);
Отримання Агрегатів
Коли ви взаємодієте з моделями Eloquent, ви також можете використовувати методи count, sum, max та інші агрегатні методи, надані конструктором запитів Laravel. Як ви могли очікувати, ці методи повертають скалярне значення замість екземпляра моделі Eloquent:
$count = Flight::where('active', 1)->count();
$max = Flight::where('active', 1)->max('price');
Вставка та Оновлення Моделей
Inserts
Звичайно, при використанні Eloquent, нам не тільки потрібно отримувати моделі з бази даних. Нам також потрібно вставляти нові записи. На щастя, Eloquent робить це просто. Щоб вставити новий запис у базу даних, слід створити новий екземпляр моделі та встановити атрибути на моделі. Потім викличте метод save на екземплярі моделі:
<?php namespace App\Http\Controllers; use App\Models\Flight; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class FlightController extends Controller { /** * Зберегти новий рейс у базі даних. */ public function store(Request $request): RedirectResponse { // Валідувати запит... $flight = new Flight; $flight->name = $request->name; $flight->save(); return redirect('/flights'); } }
У цьому прикладі ми призначаємо поле name з вхідного HTTP-запиту атрибуту name екземпляра моделі App\Models\Flight. Коли ми викликаємо метод save, запис буде вставлено в базу даних. Позначки часу моделі created_at та updated_at будуть автоматично встановлені при виклику методу save, тому немає потреби встановлювати їх вручну.
Альтернативно, ви можете використовувати метод create, щоб "зберегти" нову модель, використовуючи одну PHP інструкцію. Вставлений екземпляр моделі буде повернуто вам методом create:
use App\Models\Flight;
$flight = Flight::create([
'name' => 'London to Paris',
]);
Однак, перед використанням методу create, вам потрібно вказати або властивість fillable, або guarded у вашому класі моделі. Ці властивості є необхідними, оскільки всі моделі Eloquent за замовчуванням захищені від вразливостей масового призначення. Щоб дізнатися більше про масове призначення, будь ласка, зверніться до документації з масового призначення.
Updates
Метод save також може бути використаний для оновлення моделей, які вже існують у базі даних. Щоб оновити модель, ви повинні отримати її та встановити будь-які атрибути, які ви бажаєте оновити. Потім ви повинні викликати метод save моделі. Знову ж таки, мітка часу updated_at буде автоматично оновлена, тому немає потреби вручну встановлювати її значення:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->name = 'Paris to London';
$flight->save();
Іноді вам може знадобитися оновити існуючу модель або створити нову модель, якщо відповідна модель не існує. Як і метод firstOrCreate, метод updateOrCreate зберігає модель, тому немає потреби вручну викликати метод save.
У наведеному нижче прикладі, якщо існує рейс з місцем departure Oakland та місцем destination San Diego, його стовпці price та discounted будуть оновлені. Якщо такого рейсу не існує, буде створено новий рейс, який матиме атрибути, що є результатом об'єднання масиву першого аргументу з масивом другого аргументу:
$flight = Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
Масові Updates
Оновлення також можуть бути виконані для моделей, що відповідають заданому запиту. У цьому прикладі всі рейси, які є active і мають destination San Diego, будуть позначені як затримані:
Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);
Метод update очікує масив пар стовпців і значень, які представляють стовпці, що повинні бути оновлені. Метод update повертає кількість змінених рядків.
Коли виконується масове оновлення через Eloquent, події моделі saving, saved, updating та updated не будуть викликані для оновлених моделей. Це тому, що моделі ніколи фактично не отримуються при виконанні масового оновлення.
Перевірка змін атрибутів
Eloquent надає методи isDirty, isClean та wasChanged для перевірки внутрішнього стану вашої моделі та визначення, як її атрибути змінилися з моменту, коли модель була спочатку отримана.
Метод isDirty визначає, чи були змінені будь-які атрибути моделі з моменту її отримання. Ви можете передати конкретне ім'я атрибута або масив атрибутів до методу isDirty, щоб визначити, чи є якісь атрибути "забрудненими". Метод isClean визначить, чи залишився атрибут незмінним з моменту отримання моделі. Цей метод також приймає необов'язковий аргумент атрибута:
use App\Models\User;
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false
$user->isDirty(['first_name', 'title']); // true
$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true
$user->isClean(['first_name', 'title']); // false
$user->save();
$user->isDirty(); // false
$user->isClean(); // true
Метод wasChanged визначає, чи були змінені якісь атрибути, коли модель була востаннє збережена в поточному циклі запиту. За потреби, ви можете передати ім'я атрибута, щоб перевірити, чи був змінений конкретний атрибут:
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->save();
$user->wasChanged(); // true
$user->wasChanged('title'); // true
$user->wasChanged(['title', 'slug']); // true
$user->wasChanged('first_name'); // false
$user->wasChanged(['first_name', 'title']); // true
Метод getOriginal повертає масив, що містить оригінальні атрибути моделі, незалежно від будь-яких змін у моделі з моменту її отримання. Якщо потрібно, ви можете передати конкретне ім'я атрибута, щоб отримати оригінальне значення певного атрибута:
$user = User::find(1);
$user->name; // John
$user->email; // example@example.com
$user->name = 'Jack';
$user->name; // Jack
$user->getOriginal('name'); // John
$user->getOriginal(); // Array of original attributes...
Метод getChanges повертає масив, що містить атрибути, які змінилися під час останнього збереження моделі, тоді як метод getPrevious повертає масив, що містить оригінальні значення атрибутів до останнього збереження моделі:
$user = User::find(1);
$user->name; // John
$user->email; // example@example.com
$user->update([
'name' => 'Jack',
'email' => 'example@example.com',
]);
$user->getChanges();
/*
[
'name' => 'Jack',
'email' => 'example@example.com',
]
*/
$user->getPrevious();
/*
[
'name' => 'John',
'email' => 'example@example.com',
]
*/
Масове призначення
Ви можете використовувати метод create, щоб "зберегти" нову модель, використовуючи одну PHP-інструкцію. Вставлений екземпляр моделі буде повернуто вам методом:
use App\Models\Flight;
$flight = Flight::create([
'name' => 'London to Paris',
]);
Однак, перед використанням методу create, вам потрібно вказати або властивість fillable, або guarded у вашому класі моделі. Ці властивості є необхідними, оскільки всі моделі Eloquent за замовчуванням захищені від вразливостей масового призначення.
Вразливість масового призначення виникає, коли користувач передає неочікуване поле HTTP-запиту, і це поле змінює стовпець у вашій базі даних, чого ви не очікували. Наприклад, зловмисник може надіслати параметр is_admin через HTTP-запит, який потім передається до методу create вашої моделі, дозволяючи користувачу підвищити себе до адміністратора.
Отже, щоб почати, вам слід визначити, які атрибути моделі ви хочете зробити масово призначуваними. Ви можете зробити це, використовуючи властивість $fillable у моделі. Наприклад, давайте зробимо атрибут name нашої моделі Flight масово призначуваним:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * Атрибути, які дозволено масово призначати. * * @var array<int, string> */ protected $fillable = ['name']; }
Після того як ви вказали, які атрибути можуть бути масово призначені, ви можете використовувати метод create для вставки нового запису в базу даних. Метод create повертає новостворений екземпляр моделі:
$flight = Flight::create(['name' => 'London to Paris']);
Якщо у вас вже є екземпляр моделі, ви можете використовувати метод fill, щоб заповнити його масивом атрибутів:
$flight->fill(['name' => 'Amsterdam to Frankfurt']);
Масове призначення та JSON стовпці
Коли призначаються JSON-стовпці, масово призначуваний ключ кожного стовпця повинен бути вказаний у масиві $fillable вашої моделі. З міркувань безпеки, Laravel не підтримує оновлення вкладених атрибутів JSON при використанні властивості guarded:
/** * Атрибути, які можна масово присвоювати. * * @var array<int, string> */ protected $fillable = [ 'options->enabled', ];
Дозвіл на масове призначення
Якщо ви хочете зробити всі ваші атрибути масово призначуваними, ви можете визначити властивість $guarded вашої моделі як порожній масив. Якщо ви вирішите зняти захист з вашої моделі, ви повинні особливо уважно створювати масиви, які передаються методам fill, create та update Eloquent:
/** * Атрибути, які не можна масово присвоювати. * * @var array<string>|bool */ protected $guarded = [];
Винятки масового призначення
За замовчуванням атрибути, які не включені в масив $fillable, тихо відкидаються під час виконання операцій масового присвоєння. У виробничому середовищі це очікувана поведінка; однак під час локальної розробки це може призвести до плутанини, чому зміни моделі не набувають чинності.
Якщо ви бажаєте, ви можете вказати Laravel викидати виняток при спробі заповнити незаповнюваний атрибут, викликавши метод preventSilentlyDiscardingAttributes. Зазвичай цей метод слід викликати в методі boot класу AppServiceProvider вашого застосунку:
use Illuminate\Database\Eloquent\Model; /** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { Model::preventSilentlyDiscardingAttributes($this->app->isLocal()); }
Upserts
Метод Eloquent upsert може бути використаний для оновлення або створення записів в одній атомарній операції. Перший аргумент методу складається з значень для вставки або оновлення, тоді як другий аргумент містить список стовпців, які унікально ідентифікують записи в асоційованій таблиці. Третій і останній аргумент методу - це масив стовпців, які слід оновити, якщо відповідний запис вже існує в базі даних. Метод upsert автоматично встановить часові мітки created_at та updated_at, якщо часові мітки увімкнені в моделі:
Flight::upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], uniqueBy: ['departure', 'destination'], update: ['price']);
Усі бази даних, окрім SQL Server, вимагають, щоб стовпці у другому аргументі методу upsert мали індекс "primary" або "unique". Крім того, драйвери баз даних MariaDB та MySQL ігнорують другий аргумент методу upsert і завжди використовують індекси "primary" та "unique" таблиці для виявлення існуючих записів.
Видалення моделей
Щоб видалити модель, ви можете викликати метод delete на екземплярі моделі:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->delete();
Видалення існуючої моделі за її первинним ключем
У наведеному вище прикладі ми отримуємо модель з бази даних перед викликом методу delete. Однак, якщо ви знаєте первинний ключ моделі, ви можете видалити модель без явного її отримання, викликавши метод destroy. Окрім прийняття одного первинного ключа, метод destroy прийматиме кілька первинних ключів, масив первинних ключів або колекцію первинних ключів:
Flight::destroy(1);
Flight::destroy(1, 2, 3);
Flight::destroy([1, 2, 3]);
Flight::destroy(collect([1, 2, 3]));
Якщо ви використовуєте моделі з м'яким видаленням, ви можете назавжди видалити моделі за допомогою методу forceDestroy:
Flight::forceDestroy(1);
Метод destroy завантажує кожну модель окремо і викликає метод delete, щоб події deleting та deleted були належним чином відправлені для кожної моделі.
Видалення моделей за допомогою запитів
Звичайно, ви можете створити запит Eloquent для видалення всіх моделей, що відповідають критеріям вашого запиту. У цьому прикладі ми видалимо всі рейси, позначені як неактивні. Як і масові оновлення, масові видалення не будуть викликати події моделі для моделей, які видаляються:
$deleted = Flight::where('active', 0)->delete();
Щоб видалити всі моделі в таблиці, слід виконати запит без додавання будь-яких умов:
$deleted = Flight::query()->delete();
При виконанні масового видалення інструкції через Eloquent, події моделі deleting та deleted не будуть відправлені для видалених моделей. Це тому, що моделі ніколи фактично не отримуються при виконанні інструкції видалення.
М'яке видалення
Крім фактичного видалення записів з вашої бази даних, Eloquent також може "м'яко видаляти" моделі. Коли моделі м'яко видаляються, вони фактично не видаляються з вашої бази даних. Натомість, атрибут deleted_at встановлюється на моделі, вказуючи дату та час, коли модель була "видалена". Щоб увімкнути м'яке видалення для моделі, додайте трейд Illuminate\Database\Eloquent\SoftDeletes до моделі:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Flight extends Model
{
use SoftDeletes;
}
Трейт SoftDeletes автоматично перетворить атрибут deleted_at на екземпляр DateTime / Carbon для вас.
Ви також повинні додати стовпець deleted_at до вашої таблиці бази даних. Laravel конструктор схем містить допоміжний метод для створення цього стовпця:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('flights', function (Blueprint $table) {
$table->softDeletes();
});
Schema::table('flights', function (Blueprint $table) {
$table->dropSoftDeletes();
});
Тепер, коли ви викликаєте метод delete на моделі, стовпець deleted_at буде встановлено на поточну дату та час. Однак запис моделі в базі даних залишиться в таблиці. При виконанні запиту до моделі, яка використовує м'яке видалення, м'яко видалені моделі автоматично будуть виключені з усіх результатів запиту.
Щоб визначити, чи була певна модель м'яко видалена, ви можете використовувати метод trashed:
if ($flight->trashed()) {
// ...
}
Відновлення м'яко видалених моделей
Іноді ви можете захотіти "відновити" м'яко видалену модель. Щоб відновити м'яко видалену модель, ви можете викликати метод restore на екземплярі моделі. Метод restore встановить стовпець deleted_at моделі в null:
$flight->restore();
Ви також можете використовувати метод restore у запиті для відновлення декількох моделей. Знову ж таки, як і інші "масові" операції, це не викликатиме жодних подій моделі для моделей, які відновлюються:
Flight::withTrashed()
->where('airline_id', 1)
->restore();
Метод restore також може бути використаний при побудові запитів відношень:
$flight->history()->restore();
Постійне Видалення Моделей
Іноді вам може знадобитися дійсно видалити модель з вашої бази даних. Ви можете використовувати метод forceDelete, щоб назавжди видалити м'яко видалену модель з таблиці бази даних:
$flight->forceDelete();
Ви також можете використовувати метод forceDelete при створенні запитів відношень Eloquent:
$flight->history()->forceDelete();
Запити до м'яко видалених моделей
Включення Моделей з М'яким Видаленням
Як зазначено вище, моделі з м'яким видаленням автоматично виключаються з результатів запиту. Однак, ви можете примусово включити моделі з м'яким видаленням у результати запиту, викликавши метод withTrashed у запиті:
use App\Models\Flight;
$flights = Flight::withTrashed()
->where('account_id', 1)
->get();
Метод withTrashed також може бути викликаний при побудові запиту відношення:
$flight->history()->withTrashed()->get();
Отримання лише м'яко видалених моделей
Метод onlyTrashed буде отримувати лише м'яко видалені моделі:
$flights = Flight::onlyTrashed()
->where('airline_id', 1)
->get();
Обрізка Моделей
Іноді ви можете захотіти періодично видаляти моделі, які більше не потрібні. Щоб досягти цього, ви можете додати трейти Illuminate\Database\Eloquent\Prunable або Illuminate\Database\Eloquent\MassPrunable до моделей, які ви хочете періодично очищати. Після додавання одного з трейтов до моделі, реалізуйте метод prunable, який повертає конструктор запитів Eloquent, що визначає моделі, які більше не потрібні:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Prunable; class Flight extends Model { use Prunable; /** * Отримати запит моделі, що підлягає очищенню. */ public function prunable(): Builder { return static::where('created_at', '<=', now()->subMonth()); } }
Коли позначаєте моделі як Prunable, ви також можете визначити метод pruning у моделі. Цей метод буде викликано перед видаленням моделі. Цей метод може бути корисним для видалення будь-яких додаткових ресурсів, пов'язаних з моделлю, таких як збережені файли, перед тим як модель буде остаточно видалена з бази даних:
/** * Підготувати модель до очищення. */ protected function pruning(): void { // ... }
Після налаштування вашої моделі, що підлягає очищенню, ви повинні запланувати виконання Artisan команди model:prune у файлі routes/console.php вашого застосунку. Ви можете вільно вибрати відповідний інтервал, з яким ця команда повинна виконуватись:
use Illuminate\Support\Facades\Schedule;
Schedule::command('model:prune')->daily();
За лаштунками команда model:prune автоматично виявлятиме "Prunable" моделі у директорії app/Models вашого застосунку. Якщо ваші моделі знаходяться в іншому місці, ви можете використовувати опцію --model для вказівки імен класів моделей:
Schedule::command('model:prune', [
'--model' => [Address::class, Flight::class],
])->daily();
Якщо ви бажаєте виключити певні моделі з очищення, очищуючи всі інші виявлені моделі, ви можете використовувати опцію --except:
Schedule::command('model:prune', [
'--except' => [Address::class, Flight::class],
])->daily();
Ви можете протестувати свій запит prunable, виконавши команду model:prune з опцією --pretend. При використанні режиму імітації команда model:prune просто повідомить, скільки записів буде видалено, якщо команда буде виконана насправді:
php artisan model:prune --pretend
Моделі з м'яким видаленням будуть видалені назавжди (forceDelete), якщо вони відповідають запиту на видалення.
Масове Очищення
Коли моделі позначені трейтом Illuminate\Database\Eloquent\MassPrunable, моделі видаляються з бази даних за допомогою запитів масового видалення. Тому метод pruning не буде викликано, так само як і події моделі deleting та deleted не будуть відправлені. Це відбувається тому, що моделі ніколи фактично не отримуються перед видаленням, що робить процес очищення набагато ефективнішим:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\MassPrunable; class Flight extends Model { use MassPrunable; /** * Отримати запит для моделі, що підлягає очищенню. */ public function prunable(): Builder { return static::where('created_at', '<=', now()->subMonth()); } }
Реплікація Моделей
Ви можете створити незбережену копію існуючого екземпляра моделі, використовуючи метод replicate. Цей метод особливо корисний, коли у вас є екземпляри моделі, які мають багато спільних атрибутів:
use App\Models\Address;
$shipping = Address::create([
'type' => 'shipping',
'line_1' => '123 Example Street',
'city' => 'Victorville',
'state' => 'CA',
'postcode' => '90001',
]);
$billing = $shipping->replicate()->fill([
'type' => 'billing'
]);
$billing->save();
Щоб виключити один або кілька атрибутів з копіювання до нової моделі, ви можете передати масив до методу replicate:
$flight = Flight::create([
'destination' => 'LAX',
'origin' => 'LHR',
'last_flown' => '2020-03-04 11:00:00',
'last_pilot_id' => 747,
]);
$flight = $flight->replicate([
'last_flown',
'last_pilot_id'
]);
Області запитів
Глобальні області застосування
Глобальні області дозволяють додавати обмеження до всіх запитів для даної моделі. Власна функціональність Laravel м'якого видалення використовує глобальні області, щоб отримувати з бази даних лише "не видалені" моделі. Написання власних глобальних областей може забезпечити зручний, простий спосіб переконатися, що кожен запит для даної моделі отримує певні обмеження.
Генерація Scopes
Щоб згенерувати нову глобальну область, ви можете викликати команду Artisan make:scope, яка розмістить згенеровану область у каталозі app/Models/Scopes вашого застосунку:
php artisan make:scope AncientScope
Написання Глобальних Областей
Написання глобальної області є простим. Спочатку використайте команду make:scope для генерації класу, який реалізує інтерфейс Illuminate\Database\Eloquent\Scope. Інтерфейс Scope вимагає реалізувати один метод: apply. Метод apply може додавати обмеження where або інші типи виразів до запиту за потреби:
<?php namespace App\Models\Scopes; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; class AncientScope implements Scope { /** * Застосувати скоуп до вказаного конструктора запитів Eloquent. */ public function apply(Builder $builder, Model $model): void { $builder->where('created_at', '<', now()->subYears(2000)); } }
Якщо ваша глобальна область додає стовпці до виразу select запиту, ви повинні використовувати метод addSelect замість select. Це запобіжить ненавмисній заміні існуючого виразу select запиту.
Застосування Глобальних Областей
Щоб призначити глобальну область моделі, ви можете просто розмістити атрибут ScopedBy на моделі:
<?php
namespace App\Models;
use App\Models\Scopes\AncientScope;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
#[ScopedBy([AncientScope::class])]
class User extends Model
{
//
}
Або ви можете вручну зареєструвати глобальну область, перевизначивши метод моделі booted і викликавши метод моделі addGlobalScope. Метод addGlobalScope приймає екземпляр вашої області як єдиний аргумент:
<?php namespace App\Models; use App\Models\Scopes\AncientScope; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * Метод "booted" моделі. */ protected static function booted(): void { static::addGlobalScope(new AncientScope); } }
Після додавання області в приклад вище до моделі App\Models\User, виклик методу User::all() виконає наступний SQL-запит:
select * from `users` where `created_at` < 0021-02-18 00:00:00
Анонімні глобальні області застосування
Eloquent також дозволяє визначати глобальні області дії за допомогою замикань, що особливо корисно для простих областей дії, які не потребують окремого класу. При визначенні глобальної області дії за допомогою замикання, ви повинні надати ім'я області дії на ваш вибір як перший аргумент методу addGlobalScope:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * Метод "booted" моделі. */ protected static function booted(): void { static::addGlobalScope('ancient', function (Builder $builder) { $builder->where('created_at', '<', now()->subYears(2000)); }); } }
Видалення глобальних областей
Якщо ви хочете видалити глобальну область для даного запиту, ви можете використовувати метод withoutGlobalScope. Цей метод приймає ім'я класу глобальної області як єдиний аргумент:
User::withoutGlobalScope(AncientScope::class)->get();
Або, якщо ви визначили глобальну область дії за допомогою замикання, ви повинні передати рядкове ім'я, яке ви призначили глобальній області дії:
User::withoutGlobalScope('ancient')->get();
Якщо ви хочете видалити кілька або навіть усі глобальні області запиту, ви можете використовувати метод withoutGlobalScopes:
// Видалити всі глобальні області...
User::withoutGlobalScopes()->get();
// Видалити деякі з глобальних областей...
User::withoutGlobalScopes([
FirstScope::class, SecondScope::class
])->get();
Локальні області застосування
Локальні області дозволяють визначати загальні набори обмежень запитів, які ви можете легко повторно використовувати у вашому застосунку. Наприклад, вам може знадобитися часто отримувати всіх користувачів, які вважаються "популярними". Щоб визначити область, додайте атрибут Scope до методу Eloquent.
Скоупи завжди повинні повертати той самий екземпляр конструктора запитів або void:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Обмежте запит, щоб включати лише популярних користувачів.
*/
#[Scope]
protected function popular(Builder $query): void
{
$query->where('votes', '>', 100);
}
/**
* Обмежте запит, щоб включати лише активних користувачів.
*/
#[Scope]
protected function active(Builder $query): void
{
$query->where('active', 1);
}
}
Використання Локальної Області
Після визначення області ви можете викликати методи області під час запиту моделі. Ви навіть можете об'єднувати виклики до різних областей:
use App\Models\User;
$users = User::popular()->active()->orderBy('created_at')->get();
Поєднання декількох областей моделі Eloquent за допомогою оператора запиту or може вимагати використання замикань для досягнення правильного логічного групування:
$users = User::popular()->orWhere(function (Builder $query) {
$query->active();
})->get();
Однак, оскільки це може бути громіздким, Laravel надає метод "вищого порядку" orWhere, який дозволяє плавно з'єднувати скоупи разом без використання замикань:
$users = User::popular()->orWhere->active()->get();
Динамічні області застосування
Іноді ви можете захотіти визначити область, яка приймає параметри. Щоб почати, просто додайте ваші додаткові параметри до сигнатури методу області. Параметри області слід визначати після параметра $query:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Attributes\Scope; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * Обмежити запит лише користувачами вказаного типу. */ #[Scope] protected function ofType(Builder $query, string $type): void { $query->where('type', $type); } }
Після того як очікувані аргументи були додані до сигнатури методу області, ви можете передати аргументи при виклику області:
$users = User::ofType('admin')->get();
Атрибути, що очікують на обробку
Якщо ви хочете використовувати області для створення моделей, які мають ті ж атрибути, що й ті, які використовуються для обмеження області, ви можете використовувати метод withAttributes при побудові запиту області:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Attributes\Scope; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class Post extends Model { /** * Обмежити запит лише чернетками. */ #[Scope] protected function draft(Builder $query): void { $query->withAttributes([ 'hidden' => true, ]); } }
Метод withAttributes додасть умови where до запиту, використовуючи задані атрибути, і також додасть задані атрибути до будь-яких моделей, створених через область:
$draft = Post::draft()->create(['title' => 'In Progress']);
$draft->hidden; // true
Щоб вказати методу withAttributes не додавати умови where до запиту, ви можете встановити аргумент asConditions в false:
$query->withAttributes([
'hidden' => true,
], asConditions: false);
Порівняння моделей
Іноді вам може знадобитися визначити, чи є дві моделі "однаковими" чи ні. Методи is та isNot можуть бути використані для швидкої перевірки, чи мають дві моделі однаковий первинний ключ, таблицю та підключення до бази даних чи ні:
if ($post->is($anotherPost)) {
// ...
}
if ($post->isNot($anotherPost)) {
// ...
}
Методи is та isNot також доступні при використанні belongsTo, hasOne, morphTo та morphOne відносин. Цей метод особливо корисний, коли ви хочете порівняти пов'язану модель без виконання запиту для отримання цієї моделі:
if ($post->author()->is($user)) {
// ...
}
Події
Бажаєте транслювати ваші Eloquent події безпосередньо до вашого клієнтського застосунку? Ознайомтеся з трансляцією подій моделі у Laravel.
Eloquent моделі викликають кілька подій, дозволяючи вам підключатися до наступних моментів у життєвому циклі моделі: retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, trashed, forceDeleting, forceDeleted, restoring, restored і replicating.
Подія retrieved буде викликана, коли існуюча модель отримується з бази даних. Коли нова модель зберігається вперше, події creating та created будуть викликані. Події updating / updated будуть викликані, коли існуюча модель змінюється і викликається метод save. Події saving / saved будуть викликані, коли модель створюється або оновлюється - навіть якщо атрибути моделі не були змінені. Імена подій, що закінчуються на -ing, викликаються перед тим, як будь-які зміни в моделі будуть збережені, тоді як події, що закінчуються на -ed, викликаються після того, як зміни в моделі збережені.
Щоб почати прослуховувати події моделі, визначте властивість $dispatchesEvents у вашій Eloquent моделі. Ця властивість відображає різні точки життєвого циклу Eloquent моделі на ваші власні класи подій. Кожен клас події моделі повинен очікувати отримання екземпляра відповідної моделі через свій конструктор:
<?php namespace App\Models; use App\Events\UserDeleted; use App\Events\UserSaved; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class User extends Authenticatable { use Notifiable; /** * Карта подій для моделі. * * @var array<string, string> */ protected $dispatchesEvents = [ 'saved' => UserSaved::class, 'deleted' => UserDeleted::class, ]; }
Після визначення та відображення ваших Eloquent подій, ви можете використовувати слухачі подій для обробки подій.
Коли виконується масове оновлення або видалення запиту через Eloquent, події моделі saved, updated, deleting та deleted не будуть викликані для моделей, на які це впливає. Це тому, що моделі ніколи фактично не отримуються при виконанні масових оновлень або видалень.
Використання замикань
Замість використання класів подій, ви можете зареєструвати замикання, які виконуються, коли різні події моделі відправляються. Зазвичай, ви повинні реєструвати ці замикання в методі booted вашої моделі:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * Метод "booted" моделі. */ protected static function booted(): void { static::created(function (User $user) { // ... }); } }
Якщо потрібно, ви можете використовувати анонімні слухачі подій, що підтримують чергу при реєстрації подій моделі. Це вкаже Laravel виконувати слухач подій моделі у фоновому режимі, використовуючи чергу вашого застосунку:
use function Illuminate\Events\queueable;
static::created(queueable(function (User $user) {
// ...
}));
Спостерігачі
Визначення Спостерігачів
Якщо ви слухаєте багато подій на даній моделі, ви можете використовувати спостерігачів, щоб згрупувати всіх ваших слухачів в один клас. Класи спостерігачів мають імена методів, які відображають події Eloquent, які ви бажаєте слухати. Кожен з цих методів отримує модель, на яку вплинули, як єдиний аргумент. Команда Artisan make:observer є найпростішим способом створити новий клас спостерігача:
php artisan make:observer UserObserver --model=User
Ця команда розмістить новий спостерігач у вашому каталозі app/Observers. Якщо цей каталог не існує, Artisan створить його для вас. Ваш новий спостерігач виглядатиме наступним чином:
<?php
namespace App\Observers;
use App\Models\User;
class UserObserver
{
/**
* Обробка події "created" для користувача.
*/
public function created(User $user): void
{
// ...
}
/**
* Обробити подію "updated" для користувача.
*/
public function updated(User $user): void
{
// ...
}
/**
* Обробити подію "deleted" для користувача.
*/
public function deleted(User $user): void
{
// ...
}
/**
* Обробка події "restored" для користувача.
*/
public function restored(User $user): void
{
// ...
}
/**
* Обробити подію "forceDeleted" для користувача.
*/
public function forceDeleted(User $user): void
{
// ...
}
}
Щоб зареєструвати спостерігача, ви можете розмістити атрибут ObservedBy на відповідній моделі:
use App\Observers\UserObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
#[ObservedBy([UserObserver::class])]
class User extends Authenticatable
{
//
}
Або ви можете вручну зареєструвати спостерігача, викликавши метод observe на моделі, за якою ви бажаєте спостерігати. Ви можете зареєструвати спостерігачів у методі boot класу AppServiceProvider вашого застосунку:
use App\Models\User; use App\Observers\UserObserver; /** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { User::observe(UserObserver::class); }
Існують додаткові події, які спостерігач може прослуховувати, такі як saving та retrieved. Ці події описані в документації подій.
Спостерігачі та Транзакції Бази Даних
Коли моделі створюються в межах транзакції бази даних, ви можете захотіти вказати спостерігачу виконувати свої обробники подій лише після того, як транзакція бази даних буде зафіксована. Ви можете досягти цього, реалізувавши інтерфейс ShouldHandleEventsAfterCommit у вашому спостерігачі. Якщо транзакція бази даних не виконується, обробники подій будуть виконані негайно:
<?php namespace App\Observers; use App\Models\User; use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit; class UserObserver implements ShouldHandleEventsAfterCommit { /** * Обробити подію "created" для користувача. */ public function created(User $user): void { // ... } }
Приглушення подій
Ви можете час від часу потребувати тимчасово "відключити" всі події, що викликаються моделлю. Ви можете досягти цього, використовуючи метод withoutEvents. Метод withoutEvents приймає замикання як єдиний аргумент. Будь-який код, виконаний у межах цього замикання, не буде викликати події моделі, і будь-яке значення, повернене замиканням, буде повернене методом withoutEvents:
use App\Models\User;
$user = User::withoutEvents(function () {
User::findOrFail(1)->delete();
return User::find(2);
});
Збереження Окремої Моделі Без Подій
Іноді ви можете захотіти "зберегти" дану модель без відправки будь-яких подій. Ви можете досягти цього, використовуючи метод saveQuietly:
$user = User::findOrFail(1);
$user->name = 'Victoria Faith';
$user->saveQuietly();
Ви також можете "оновити", "видалити", "м'яко видалити", "відновити" та "реплікувати" дану модель без відправки будь-яких подій:
$user->deleteQuietly();
$user->forceDeleteQuietly();
$user->restoreQuietly();
