Документация Laravel 10.x
Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.
Laravel включает Eloquent, объектно-реляционный маппер (ORM), который делает взаимодействие с базой данных приятным. При использовании Eloquent каждая таблица базы данных имеет соответствующую "модель", которая используется для взаимодействия с этой таблицей. Помимо извлечения записей из таблицы базы данных, модели Eloquent позволяют вам вставлять, обновлять и удалять записи из таблицы.
Примечание Прежде чем начать, убедитесь, что в вашем файле конфигурации
config/database.php
вашего приложения настроено подключение к базе данных. Дополнительную информацию о настройке вашей базы данных можно найти в документации по конфигурации базы данных.
Если вы новичок в Laravel, не стесняйтесь приступить к Laravel Bootcamp. Laravel Bootcamp проведет вас через создание вашего первого приложения Laravel с использованием Eloquent. Это отличный способ получить обзор всего, что может предложить Laravel и Eloquent.
Для начала создадим модель Eloquent. Модели обычно находятся в каталоге app\Models
и расширяют класс Illuminate\Database\Eloquent\Model
. Вы можете использовать команду Artisan make:model, чтобы создать новую модель:
php artisan make:model Flight
Если вы хотите сгенерировать миграцию базы данных при создании модели, вы можете использовать опцию --migration
или -m
:
php artisan make:model Flight --migration
Вы можете генерировать различные другие типы классов при создании модели, такие как фабрики, сидеры, политики, контроллеры и формные запросы. Кроме того, эти опции могут быть объединены для создания нескольких классов сразу:
# Generate a model and a FlightFactory class...php artisan make:model Flight --factoryphp artisan make:model Flight -f # Generate a model and a FlightSeeder class...php artisan make:model Flight --seedphp artisan make:model Flight -s # Generate a model and a FlightController class...php artisan make:model Flight --controllerphp artisan make:model Flight -c # Generate a model, FlightController resource class, and form request classes...php artisan make:model Flight --controller --resource --requestsphp artisan make:model Flight -crR # Generate a model and a FlightPolicy class...php artisan make:model Flight --policy # Generate a model and a migration, factory, seeder, and controller...php artisan make:model Flight -mfsc # Shortcut to generate a model, migration, factory, seeder, policy, controller, and form requests...php artisan make:model Flight --all # Generate a pivot model...php artisan make:model Member --pivotphp artisan make:model Member -p
Иногда бывает сложно определить все доступные атрибуты и отношения модели, просто просматривая ее код. Вместо этого попробуйте команду Artisan model:show
, которая предоставляет удобный обзор всех атрибутов и отношений модели:
php artisan model:show Flight
Модели, созданные с помощью команды make:model
, будут помещены в каталог app/Models
. Давайте рассмотрим базовый класс модели и обсудим некоторые ключевые конвенции Eloquent:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Flight extends Model{ // ...}
Посмотрев на приведенный выше пример, вы, возможно, заметили, что мы не сказали Eloquent, какая таблица базы данных соответствует нашей модели Flight
. По соглашению, в качестве имени таблицы будет использоваться "змеиное написание", множественное имя класса, если явно не указано другое имя. Таким образом, в данном случае 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. Тем не менее вы можете добавлять дополнительные многоколоночные уникальные индексы в ваши таблицы базы данных, в дополнение к уникальному первичному ключу таблицы.
Вместо использования инкрементируемых целых чисел в качестве первичных ключей вашей модели 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'];}
Если вы хотите, вы можете выбрать использовать вместо UUID "ULID". ULID похожи на UUID, однако они имеют длину всего 26 символов. Как и упорядоченные UUID, ULID лексикографически сортируются для эффективного индексирования базы данных. Чтобы использовать ULID, вы должны использовать трейт 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 = 'sqlite';}
По умолчанию новый экземпляр модели не будет содержать значений атрибутов. Если вы хотите задать значения по умолчанию для некоторых атрибутов вашей модели, вы можете определить свойство $attributes
на вашей модели. Значения атрибутов, помещенные в массив $attributes
, должны быть в их "сыром", "хранящемся" формате, как если бы они только что были считаны из базы данных:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Flight extends Model{ /** * Значения атрибутов модели по умолчанию. * * @var array */ protected $attributes = [ 'options' => '[]', 'delayed' => false, ];}
Laravel предлагает несколько методов, которые позволяют настраивать поведение Eloquent и "строгость" в различных ситуациях.
Во-первых, метод preventLazyLoading
принимает необязательный логический аргумент, указывающий, следует ли предотвращать отложенную загрузку. Например, вы можете захотеть отключить отложенную загрузку только в средах, отличных от производственной, чтобы ваша производственная среда продолжала работать нормально, даже если отложенная загрузка отношений случайно присутствует в коде для продакшена. Обычно этот метод следует вызывать в методе boot
сервис-провайдера вашего приложения AppServiceProvider
:
use Illuminate\Database\Eloquent\Model; /** * Запуск любых служб приложения. */public function boot(): void{ Model::preventLazyLoading(! $this->app->isProduction());}
Также вы можете указать Laravel бросать исключение при попытке заполнения атрибута, который не был добавлен в массив fillable
, вызвав метод 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') ->take(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
.
Класс коллекции 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');
Метод 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
.
Аналогично методу 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']); // Получить рейс по имени или создать его с атрибутами имени, задержки и времени прибытия...$flight = Flight::firstOrCreate( ['name' => 'London to Paris'], ['delayed' => 1, 'arrival_time' => '11:30']); // Получить рейс по имени или создать новый экземпляр Flight...$flight = Flight::firstOrNew([ 'name' => 'London to Paris']); // Получить рейс по имени или создать его с атрибутами имени, задержки и времени прибытия...$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');
Конечно же, при использовании Eloquent нам необходимо не только извлекать модели из базы данных. Нам также нужно вставлять новые записи. К счастью, Eloquent делает это простым. Чтобы вставить новую запись в базу данных, вы должны создать новый экземпляр модели и установить атрибуты модели. Затем вызовите метод save
для экземпляра модели:
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller;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 по умолчанию защищены от уязвимостей массового присвоения. Чтобы узнать больше о массовом присвоении, пожалуйста, ознакомьтесь с документацией по массовому присвоению.
Метод save
также можно использовать для обновления моделей, которые уже существуют в базе данных. Чтобы обновить модель, вы должны извлечь ее и установить любые атрибуты, которые вы хотите обновить. Затем вы должны вызвать метод save
модели. Опять же, метка времени updated_at
будет автоматически обновлена, поэтому нет необходимости устанавливать ее значение вручную:
use App\Models\Flight; $flight = Flight::find(1); $flight->name = 'Paris to London'; $flight->save();
Обновления также могут выполняться для моделей, которые соответствуют данному запросу. В этом примере все рейсы, которые активны
и имеют пункт назначения
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->name = "Jack";$user->name; // Jack $user->getOriginal('name'); // John$user->getOriginal(); // Array of original attributes...
Вы можете использовать метод 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 */ protected $fillable = ['name'];}
После того как вы указали, какие атрибуты можно массово присваивать, вы можете использовать метод create
для вставки новой записи в базу данных. Метод create
возвращает только что созданный экземпляр модели:
$flight = Flight::create(['name' => 'London to Paris']);
Если у вас уже есть экземпляр модели, вы можете использовать метод fill
, чтобы заполнить его массивом атрибутов:
$flight->fill(['name' => 'Amsterdam to Frankfurt']);
При присвоении атрибутов JSON каждый ключ массового присваивания каждого столбца должен быть указан в массиве $fillable
вашей модели. В целях безопасности Laravel не поддерживает обновление вложенных атрибутов JSON при использовании свойства guarded
:
/** * Атрибуты, которые могут массово присваиваться. * * @var array */protected $fillable = [ 'options->enabled',];
Если вы хотите сделать все ваши атрибуты массово присваиваемыми, вы можете определить свойство $guarded
модели как пустой массив. Если вы решите разблокировать свою модель, вы должны особенно внимательно подходить к созданию массивов, передаваемых методам fill
, create
и update
Eloquent:
/** * Атрибуты, которые не могут массово присваиваться. * * @var array */protected $guarded = [];
По умолчанию атрибуты, которые не включены в массив $fillable
, молча отбрасываются при выполнении операций массового присвоения. В производстве это ожидаемое поведение; однако во время локальной разработки это может вызвать путаницу в том, почему изменения модели не вступают в силу.
Если вы хотите, вы можете научить Laravel выбрасывать исключение при попытке присвоить атрибут, который нельзя массово присваивать, вызвав метод preventSilentlyDiscardingAttributes
. Обычно этот метод следует вызывать в методе boot
одного из сервис-провайдеров вашего приложения:
use Illuminate\Database\Eloquent\Model; /** * Запуск любых служб приложения. */public function boot(): void{ Model::preventSilentlyDiscardingAttributes($this->app->isLocal());}
Иногда вам может потребоваться обновить существующую модель или создать новую модель, если соответствующей модели не существует. Как и метод firstOrCreate
, метод updateOrCreate
сохраняет модель, поэтому нет необходимости вручную вызывать метод save
.
В приведенном ниже примере, если существует рейс с местом departure
в Oakland
и местом destination
в San Diego
, его столбцы price
и discounted
будут обновлены. Если такой рейс не существует, будет создан новый рейс с атрибутами, полученными путем объединения массива первого аргумента с массивом второго аргумента:
$flight = Flight::updateOrCreate( ['departure' => 'Oakland', 'destination' => 'San Diego'], ['price' => 99, 'discounted' => 1]);
Если вы хотите выполнить несколько "upserts" в одном запросе, вы должны использовать метод upsert
вместо этого. Первый аргумент метода состоит из значений для вставки или обновления, в то время как второй аргумент перечисляет столбцы, уникально идентифицирующие записи в связанной таблице. Третий и последний аргумент метода - это массив столбцов, которые должны быть обновлены, если соответствующая запись уже существует в базе данных. Метод upsert
автоматически устанавливает метки времени created_at
и updated_at
, если метки времени включены для модели:
Flight::upsert([ ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]], ['departure', 'destination'], ['price']);
Внимание Все базы данных, кроме SQL Server, требуют, чтобы столбцы второго аргумента метода
upsert
имели "первичный" или "уникальный" индекс. Кроме того, драйвер базы данных MySQL игнорирует второй аргумент методаupsert
и всегда использует индексы "первичного" и "уникального" ключа таблицы для определения существующих записей.
Для удаления модели вы можете вызвать метод delete
для экземпляра модели:
use App\Models\Flight; $flight = Flight::find(1); $flight->delete();
Вы можете вызвать метод truncate
, чтобы удалить все записи в связанной таблице модели. Операция truncate
также сбросит любые автоматически увеличиваемые идентификаторы на связанной таблице модели:
Flight::truncate();
В приведенном выше примере мы получаем модель из базы данных перед вызовом метода delete
. Однако, если вы знаете первичный ключ модели, вы можете удалить модель, не явно извлекая ее, вызвав метод destroy
. В дополнение к принятию единичного первичного ключа, метод destroy
примет несколько первичных ключей, массив первичных ключей или коллекцию первичных ключей:
Flight::destroy(1); Flight::destroy(1, 2, 3); Flight::destroy([1, 2, 3]); Flight::destroy(collect([1, 2, 3]));
Внимание Метод
destroy
загружает каждую модель индивидуально и вызывает методdelete
, чтобы события моделиdeleting
иdeleted
правильно отправлялись для каждой модели.
Конечно же, вы можете создать запрос Eloquent для удаления всех моделей, соответствующих критериям вашего запроса. В этом примере мы удалим все рейсы, которые отмечены как неактивные. Как и массовые обновления, массовые удаления не будут отправлять события модели для удаленных моделей:
$deleted = Flight::where('active', 0)->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
в классе App\Console\Kernel
вашего приложения. Вы вольны выбирать подходящий интервал, с которым должна выполняться эта команда:
/** * Определить расписание команд приложения. */protected function schedule(Schedule $schedule): void{ $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 использует глобальные области видимости, чтобы извлекать только "неудаленные" модели из базы данных. Написание своих глобальных областей видимости может предоставить удобный и простой способ убедиться, что каждый запрос для данной модели получает определенные ограничения.
Для создания новой глобальной области видимости вы можете вызвать команду 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 запроса.
Чтобы назначить глобальную область видимости модели, вы должны переопределить метод 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();
Локальные области видимости позволяют определять общие наборы ограничений запросов, которые вы можете легко повторно использовать в вашем приложении. Например, вам может часто потребоваться извлекать всех пользователей, считающихся "популярными". Для определения области видимости добавьте к методу модели Eloquent префикс scope
.
Области видимости должны всегда возвращать тот же экземпляр построителя запросов или void
:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Builder;use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * Ограничить запрос только популярными пользователями. */ public function scopePopular(Builder $query): void { $query->where('votes', '>', 100); } /** * Ограничить запрос только активными пользователями. */ public function scopeActive(Builder $query): void { $query->where('active', 1); }}
После того как область видимости была определена, вы можете вызывать методы области видимости при запросе модели. Однако при вызове метода вы не должны включать префикс scope
. Вы можете даже объединять вызовы различных областей видимости:
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\Builder;use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * Ограничить запрос только пользователями заданного типа. */ public function scopeOfType(Builder $query, string $type): void { $query->where('type', $type); }}
После того как ожидаемые аргументы были добавлены в сигнатуру метода вашей области видимости, вы можете передавать аргументы при вызове области видимости:
$users = User::ofType('admin')->get();
Иногда вам может потребоваться определить, являются ли две модели "одинаковыми" или нет. Методы 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 */ 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{ /** * Обработать событие "создание" пользователя. */ public function created(User $user): void { // ... } /** * Обработать событие "обновление" пользователя. */ public function updated(User $user): void { // ... } /** * Обработать событие "удаление" пользователя. */ public function deleted(User $user): void { // ... } /** * Обработать событие "восстановление" пользователя. */ public function restored(User $user): void { // ... } /** * Обработать событие "полное удаление" пользователя. */ public function forceDeleted(User $user): void { // ... }}
Чтобы зарегистрировать наблюдателя, вам нужно вызвать метод observe
на модели, которую вы хотите наблюдать. Вы можете зарегистрировать наблюдателей в методе boot
сервис-провайдера App\Providers\EventServiceProvider
вашего приложения:
use App\Models\User;use App\Observers\UserObserver; /** * Зарегистрировать любые события для вашего приложения. */public function boot(): void{ User::observe(UserObserver::class);}
В качестве альтернативы вы можете перечислить свои наблюдатели внутри свойства $observers
в классе App\Providers\EventServiceProvider
вашего приложения:
use App\Models\User;use App\Observers\UserObserver; /** * Наблюдатели модели для вашего приложения. * * @var array */protected $observers = [ User::class => [UserObserver::class],];
Примечание Существуют дополнительные события, которые наблюдатель может прослушивать, такие как
saving
иretrieved
. Эти события описаны в разделе событий документации.
Когда модели создаются в пределах транзакции базы данных, иногда вам захочется указать наблюдателю выполнять свои обработчики событий только после фиксации транзакции базы данных. Вы можете сделать это, реализовав интерфейс ShouldHandleEventsAfterCommit
в вашем наблюдателе. Если транзакция базы данных не выполняется, обработчики событий будут выполнены немедленно:
<?php namespace App\Observers; use App\Models\User;use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit; class UserObserver implements ShouldHandleEventsAfterCommit{ /** * Обработать событие "создание" пользователя. */ 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();