コンテンツにスキップ

Eloquent:入門

イントロダクション

Laravelには、データベースとの対話を楽しくするオブジェクトリレーショナルマッパー(ORM)であるEloquentが含まれています。Eloquentを使用する場合、各データベーステーブルには、そのテーブルとの対話に使用される対応する「モデル」があります。データベーステーブルからレコードを取得することに加えて、Eloquentモデルでは、テーブルからレコードを挿入、更新、および削除することもできます。

lightbulb

開始する前に、アプリケーションの `config/database.php` 構成ファイルでデータベース接続を設定してください。データベースの設定の詳細については、データベース設定ドキュメントをご覧ください。

Laravelブートキャンプ

Laravelを初めて使用する場合は、Laravelブートキャンプに進んでください。Laravelブートキャンプでは、Eloquentを使用して最初のLaravelアプリケーションを構築する手順を説明します。LaravelとEloquentが提供するすべての機能を知るための素晴らしい方法です。

モデルクラスの生成

まず、Eloquentモデルを作成しましょう。モデルは通常、 `app\Models` ディレクトリにあり、 `Illuminate\Database\Eloquent\Model` クラスを拡張します。 `make:model` Artisanコマンドを使用して、新しいモデルを生成できます

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 --factory
php artisan make:model Flight -f
 
# Generate a model and a FlightSeeder class...
php artisan make:model Flight --seed
php artisan make:model Flight -s
 
# Generate a model and a FlightController class...
php artisan make:model Flight --controller
php artisan make:model Flight -c
 
# Generate a model, FlightController resource class, and form request classes...
php artisan make:model Flight --controller --resource --requests
php 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
php artisan make:model Flight -a
 
# Generate a pivot model...
php artisan make:model Member --pivot
php artisan make:model Member -p

モデルの検査

モデルのコードをざっと見ただけでは、モデルのすべての利用可能な属性とリレーションシップを判断するのが難しい場合があります。代わりに、 `model:show` Artisanコマンドを試してください。これは、すべてのモデルの属性とリレーションの便利な概要を提供します

php artisan model:show Flight

Eloquentモデルの規約

`make:model` コマンドによって生成されたモデルは、 `app/Models` ディレクトリに配置されます。基本的なモデルクラスを調べて、Eloquentの主要な規約について説明しましょう

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
// ...
}

テーブル名

上記の例を一目見て、 `Flight` モデルに対応するデータベーステーブルをEloquentに指示していないことに気付いたかもしれません。慣例により、明示的に別の名前が指定されていない限り、クラスの「スネークケース」、複数形の名前がテーブル名として使用されます。したがって、この場合、Eloquentは `Flight` モデルが `flights` テーブルにレコードを格納すると想定しますが、 `AirTrafficController` モデルは `air_traffic_controllers` テーブルにレコードを格納します。

モデルの対応するデータベーステーブルがこの規則に適合しない場合は、モデルで `table` プロパティを定義することにより、モデルのテーブル名を手動で指定できます

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'my_flights';
}

プライマリキー

Eloquentはまた、各モデルの対応するデータベーステーブルに `id` という名前のプライマリキカラムがあると想定しています。必要に応じて、モデルのプライマリキーとして機能する別のカラムを指定するために、モデルで保護された `$primaryKey` プロパティを定義できます

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'flight_id';
}

さらに、Eloquentはプライマリキーが増分する整数値であると想定しているため、Eloquentはプライマリキーを自動的に整数にキャストします。増分しない、または数値以外のプライマリキーを使用する場合は、 `false` に設定されたモデルにパブリック `$incrementing` プロパティを定義する必要があります

<?php
 
class Flight extends Model
{
/**
* Indicates if the model's ID is auto-incrementing.
*
* @var bool
*/
public $incrementing = false;
}

モデルのプライマリキーが整数でない場合は、モデルで保護された `$keyType` プロパティを定義する必要があります。このプロパティの値は `string` である必要があります

<?php
 
class Flight extends Model
{
/**
* The data type of the primary key ID.
*
* @var string
*/
protected $keyType = 'string';
}

「複合」プライマリキー

Eloquentでは、各モデルにプライマリキーとして機能できる一意に識別する「ID」が少なくとも1つ必要です。「複合」プライマリキーは、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は、辞書順にソートできるため、インデックス付きデータベースストレージの効率が向上します。

モデルで `newUniqueId` メソッドを定義することにより、特定のモデルのUUID生成プロセスをオーバーライドできます。さらに、モデルで `uniqueIds` メソッドを定義することにより、UUIDを受け取るカラムを指定できます

use Ramsey\Uuid\Uuid;
 
/**
* Generate a new UUID for the model.
*/
public function newUniqueId(): string
{
return (string) Uuid::uuid4();
}
 
/**
* Get the columns that should receive a unique identifier.
*
* @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によって自動的に管理したくない場合は、値が `false` のモデルで `$timestamps` プロパティを定義する必要があります

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
}

モデルのタイムスタンプの形式をカスタマイズする必要がある場合は、モデルで `$dateFormat` プロパティを設定します。このプロパティは、日付属性がデータベースに格納される方法と、モデルが配列またはJSONにシリアル化されたときの形式を決定します

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* The storage format of the model's date columns.
*
* @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
{
/**
* The database connection that should be used by the model.
*
* @var string
*/
protected $connection = 'mysql';
}

デフォルト属性値

デフォルトでは、新しくインスタンス化されたモデルインスタンスには属性値が含まれていません。モデルの属性にデフォルト値を定義したい場合は、モデルに$attributesプロパティを定義できます。$attributes配列に配置される属性値は、データベースから読み取られたかのように、生の「保存可能な」形式である必要があります。

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* The model's default values for attributes.
*
* @var array
*/
protected $attributes = [
'options' => '[]',
'delayed' => false,
];
}

Eloquentの厳密性の設定

Laravelは、さまざまな状況でEloquentの動作と「厳密さ」を設定できるいくつかのメソッドを提供しています。

まず、preventLazyLoadingメソッドは、遅延読み込みを防止するかどうかを示すオプションのブール値引数を受け入れます。たとえば、本番環境以外の環境でのみ遅延読み込みを無効にして、本番コードに遅延読み込みされたリレーションシップが誤って存在する場合でも、本番環境が正常に機能し続けるようにすることができます。通常、このメソッドは、アプリケーションのAppServiceProviderbootメソッドで呼び出す必要があります。

use Illuminate\Database\Eloquent\Model;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Model::preventLazyLoading(! $this->app->isProduction());
}

また、preventSilentlyDiscardingAttributesメソッドを呼び出すことで、fillableでない属性に値を設定しようとすると例外をスローするようにLaravelに指示できます。これは、モデルの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();
lightbulb

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"

コレクション

見てきたように、allgetなどのEloquentメソッドは、データベースから複数のレコードを取得します。ただし、これらのメソッドはプレーンなPHP配列を返しません。代わりに、Illuminate\Database\Eloquent\Collectionのインスタンスが返されます。

EloquentのCollectionクラスは、LaravelのベースIlluminate\Support\Collectionクラスを拡張したもので、データコレクションの操作に役立つさまざまなメソッドを提供します。たとえば、rejectメソッドを使用して、呼び出されたクロージャの結果に基づいてコレクションからモデルを削除できます。

$flights = Flight::where('destination', 'Paris')->get();
 
$flights = $flights->reject(function (Flight $flight) {
return $flight->cancelled;
});

Laravelのベースコレクションクラスによって提供されるメソッドに加えて、Eloquentコレクションクラスは、Eloquentモデルのコレクションとの対話を specifically意図したいくつかの追加メソッドを提供します。

LaravelのコレクションはすべてPHPの反復可能インターフェースを実装しているため、コレクションを配列のようにループできます。

foreach ($flights as $flight) {
echo $flight->name;
}

結果のチャンク化

allメソッドまたはgetメソッドを使用して数万のEloquentレコードをロードしようとすると、アプリケーションのメモリが不足する可能性があります。これらのメソッドを使用する代わりに、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メソッドに渡される最初の引数は、「チャンク」ごとに受信するレコードの数です。2番目の引数として渡されるクロージャは、データベースから取得された各チャンクに対して呼び出されます。クロージャに渡されるレコードの各チャンクを取得するために、データベースクエリが実行されます。

結果の反復中に更新する列に基づいて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メソッドは、各チャンクをコールバックに直接渡すのではなく、Eloquentモデルのフラット化されたLazyCollectionを返します。これにより、結果を単一のストリームとして操作できます。

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]);

lazyByIdDescメソッドを使用して、 `id`の降順に基づいて結果をフィルタリングできます。

カーソル

lazyメソッドと同様に、cursorメソッドを使用して、数万のEloquentモデルレコードを反復処理する際のアプリケーションのメモリ消費量を大幅に削減できます。

cursorメソッドは単一のデータベースクエリのみを実行します。ただし、個々のEloquentモデルは、実際に反復処理されるまでハイドレートされません。したがって、カーソルを反復処理している間、一度にメモリに保持されるEloquentモデルは1つだけです。

exclamation

cursorメソッドは、一度にメモリに単一のEloquentモデルのみを保持するため、リレーションシップを事前にロードできません。リレーションシップを事前にロードする必要がある場合は、代わりにlazyメソッドを使用することを検討してください。

内部的には、cursorメソッドはPHP ジェネレーターを使用してこの機能を実装します。

use App\Models\Flight;
 
foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) {
// ...
}

cursorIlluminate\Support\LazyCollectionインスタンスを返します。遅延コレクションを使用すると、一般的なLaravelコレクションで使用可能な多くのコレクションメソッドを使用できますが、一度に1つのモデルのみをメモリにロードします。

use App\Models\User;
 
$users = User::cursor()->filter(function (User $user) {
return $user->id > 500;
});
 
foreach ($users as $user) {
echo $user->id;
}

cursorメソッドは、通常のクエリよりもはるかに少ないメモリを使用しますが(一度に1つのEloquentモデルのみをメモリに保持することにより)、最終的にはメモリが不足します。これは、PHPのPDOドライバーがすべての生のクエリ結果をバッファに内部的にキャッシュするためです。非常に多数の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();

単一モデル/集計の取得

特定のクエリに一致するすべてのレコードを取得することに加えて、findfirst、またはfirstWhereメソッドを使用して単一のレコードを取得することもできます。モデルのコレクションを返す代わりに、これらのメソッドは単一のモデルインスタンスを返します。

use App\Models\Flight;
 
// Retrieve a model by its primary key...
$flight = Flight::find(1);
 
// Retrieve the first model matching the query constraints...
$flight = Flight::where('active', 1)->first();
 
// Alternative to retrieving the first model matching the query constraints...
$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がキャッチされない場合、404 HTTPレスポンスがクライアントに自動的に送り返されます。

use App\Models\Flight;
 
Route::get('/api/flights/{id}', function (string $id) {
return Flight::findOrFail($id);
});

モデルの取得または作成

firstOrCreateメソッドは、指定された列/値のペアを使用してデータベースレコードを見つけようとします。データベースでモデルが見つからない場合、最初の配列引数とオプションの2番目の配列引数をマージした結果の属性を持つレコードが挿入されます。

firstOrNewメソッドは、firstOrCreateと同様に、指定された属性に一致するレコードをデータベースで見つけようとします。ただし、モデルが見つからない場合は、新しいモデルインスタンスが返されます。 firstOrNewによって返されるモデルはまだデータベースに保存されていないことに注意してください。保存するには、手動でsaveメソッドを呼び出す必要があります。

use App\Models\Flight;
 
// Retrieve flight by name or create it if it doesn't exist...
$flight = Flight::firstOrCreate([
'name' => 'London to Paris'
]);
 
// Retrieve flight by name or create it with the name, delayed, and arrival_time attributes...
$flight = Flight::firstOrCreate(
['name' => 'London to Paris'],
['delayed' => 1, 'arrival_time' => '11:30']
);
 
// Retrieve flight by name or instantiate a new Flight instance...
$flight = Flight::firstOrNew([
'name' => 'London to Paris'
]);
 
// Retrieve flight by name or instantiate with the name, delayed, and arrival_time attributes...
$flight = Flight::firstOrNew(
['name' => 'Tokyo to Sydney'],
['delayed' => 1, 'arrival_time' => '11:30']
);

集計の取得

Eloquentモデルを操作する場合、Laravel クエリビルダによって提供されるcountsummax、およびその他の集計メソッドを使用することもできます。ご想像のとおり、これらのメソッドは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
{
/**
* Store a new flight in the database.
*/
public function store(Request $request): RedirectResponse
{
// Validate the request...
 
$flight = new Flight;
 
$flight->name = $request->name;
 
$flight->save();
 
return redirect('/flights');
}
}

この例では、受信HTTPリクエストのnameフィールドをApp\Models\Flightモデルインスタンスのname属性に割り当てます。 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();

場合によっては、既存のモデルを更新するか、一致するモデルが存在しない場合は新しいモデルを作成する必要があります。 firstOrCreate メソッドと同様に、updateOrCreate メソッドはモデルを永続化するため、save メソッドを手動で呼び出す必要はありません。

以下の例では、departureOaklanddestinationSan Diego のフライトが存在する場合、その pricediscounted カラムが更新されます。そのようなフライトが存在しない場合は、最初の引数の配列と2番目の引数の配列をマージした結果の属性を持つ新しいフライトが作成されます。

$flight = Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);

一括更新

更新は、特定のクエリに一致するモデルに対しても実行できます。この例では、activedestinationSan Diego のすべてのフライトが遅延としてマークされます。

Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);

update メソッドは、更新する必要があるカラムと値のペアの配列を想定しています。 update メソッドは、影響を受けた行数を返します。

exclamation

Eloquent を介して一括更新を実行する場合、更新されたモデルに対して savingsavedupdating、および updated モデルイベントは発生しません。これは、一括更新を実行する際にモデルが実際に取得されないためです。

属性の変更の確認

Eloquent は、isDirtyisClean、および 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; // [email protected]
 
$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 リクエストフィールドを渡し、そのフィールドが予期しないデータベースのカラムを変更した場合に発生します。たとえば、悪意のあるユーザーが HTTP リクエストを介して is_admin パラメータを送信し、それがモデルの create メソッドに渡されることで、ユーザーが自身を管理者に昇格させる可能性があります。

そのため、まず、マスアサインメント可能にするモデル属性を定義する必要があります。これは、モデルの $fillable プロパティを使用して行うことができます。たとえば、Flight モデルの name 属性をマスアサインメント可能にしましょう。

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* The attributes that are mass assignable.
*
* @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 は guarded プロパティを使用する場合、ネストされた JSON 属性の更新をサポートしていません。

/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'options->enabled',
];

マスアサインメントの許可

すべての属性をマスアサインメント可能にする場合は、モデルの $guarded プロパティを空の配列として定義できます。モデルの保護を解除する場合は、Eloquent の fillcreate、および update メソッドに渡される配列を常に手動で作成するように特に注意する必要があります。

/**
* The attributes that aren't mass assignable.
*
* @var array<string>|bool
*/
protected $guarded = [];

マスアサインメントの例外

デフォルトでは、$fillable 配列に含まれていない属性は、マスアサインメント操作を実行するときにサイレントに破棄されます。本番環境では、これは予期された動作です。ただし、ローカル開発中は、モデルの変更が有効にならない理由について混乱を招く可能性があります。

必要に応じて、preventSilentlyDiscardingAttributes メソッドを呼び出すことによって、fillable でない属性を入力しようとするときに例外をスローするように Laravel に指示できます。通常、このメソッドは、アプリケーションの AppServiceProvider クラスの boot メソッドで呼び出す必要があります。

use Illuminate\Database\Eloquent\Model;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Model::preventSilentlyDiscardingAttributes($this->app->isLocal());
}

アップサート

Eloquent の upsert メソッドは、単一のアトミック操作でレコードを更新または作成するために使用できます。メソッドの最初の引数は、挿入または更新する値で構成され、2 番目の引数は、関連付けられたテーブル内でレコードを一意に識別するカラムをリストします。メソッドの 3 番目と最後の引数は、一致するレコードがデータベースに既に存在する場合に更新する必要があるカラムの配列です。 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']);
exclamation

SQL Server を除くすべてのデータベースでは、upsert メソッドの 2 番目の引数にあるカラムに「プライマリ」または「一意」のインデックスが必要です。さらに、MariaDB および MySQL データベースドライバは、upsert メソッドの 2 番目の引数を無視し、常にテーブルの「プライマリ」および「一意」のインデックスを使用して既存のレコードを検出します。

モデルの削除

モデルを削除するには、モデルインスタンスで delete メソッドを呼び出すことができます。

use App\Models\Flight;
 
$flight = Flight::find(1);
 
$flight->delete();

truncate メソッドを呼び出して、モデルに関連付けられているすべてのデータベースレコードを削除できます。 truncate 操作は、モデルに関連付けられたテーブルの自動インクリメント ID もリセットします。

Flight::truncate();

主キーによる既存のモデルの削除

上記の例では、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);
exclamation

destroy メソッドは各モデルを個別にロードし、delete メソッドを呼び出すため、deleting および deleted イベントが各モデルに対して適切にディスパッチされます。

クエリを使用したモデルの削除

もちろん、Eloquent クエリを構築して、クエリの条件に一致するすべてのモデルを削除できます。この例では、非アクティブとしてマークされているすべてのフライトを削除します。一括更新と同様に、一括削除は、削除されたモデルのモデルイベントをディスパッチしません。

$deleted = Flight::where('active', 0)->delete();
exclamation

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;
}
lightbulb

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();

Eloquent リレーションシップクエリを構築するときにも、forceDelete メソッドを使用できます。

$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 トレイトを追加します。トレイトのいずれかをモデルに追加した後、不要になったモデルを解決する Eloquent クエリビルダーを返す prunable メソッドを実装します。

<?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;
 
/**
* Get the prunable model query.
*/
public function prunable(): Builder
{
return static::where('created_at', '<=', now()->subMonth());
}
}

モデルを Prunable としてマークする場合、モデルに pruning メソッドを定義することもできます。このメソッドは、モデルが削除される前に呼び出されます。このメソッドは、モデルがデータベースから完全に削除される前に、保存されたファイルなど、モデルに関連付けられた追加のリソースを削除するのに役立ちます。

/**
* Prepare the model for pruning.
*/
protected function pruning(): void
{
// ...
}

プルーニング可能なモデルを構成した後、アプリケーションの routes/console.php ファイルで model:prune Artisan コマンドをスケジュールする必要があります。このコマンドを実行する適切な間隔を自由に選択できます。

use Illuminate\Support\Facades\Schedule;
 
Schedule::command('model:prune')->daily();

舞台裏では、model:prune コマンドは、アプリケーションの app/Models ディレクトリ内の「Prunable」モデルを自動的に検出します。モデルが別の場所にある場合は、--model オプションを使用してモデルクラス名を指定できます。

Schedule::command('model:prune', [
'--model' => [Address::class, Flight::class],
])->daily();

特定のモデルを除外して、他の検出されたすべてのモデルをプルーニングする場合は、--except オプションを使用できます。

Schedule::command('model:prune', [
'--except' => [Address::class, Flight::class],
])->daily();

--pretend オプションを付けて model:prune コマンドを実行することで、prunable クエリをテストできます。プレテンドモードでは、model:prune コマンドは、コマンドが実際に実行された場合にプルーニングされるレコード数を報告するだけです。

php artisan model:prune --pretend
exclamation

ソフトデリートされたモデルは、`prunable` クエリに一致する場合、完全に削除(`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;
 
/**
* Get the prunable model query.
*/
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();

新しいモデルに複製されないように1つ以上の属性を除外するには、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独自のソフトデリート機能は、グローバルスコープを利用して、データベースから「削除されていない」モデルのみを取得します。独自のグローバルスコープを作成すると、特定のモデルに対するすべてのクエリが特定の制約を受け取るようにするための便利で簡単な方法を提供できます。

スコープの生成

新しいグローバルスコープを生成するには、make:scope Artisanコマンドを呼び出すことができます。これにより、生成されたスコープがアプリケーションの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
{
/**
* Apply the scope to a given Eloquent query builder.
*/
public function apply(Builder $builder, Model $model): void
{
$builder->where('created_at', '<', now()->subYears(2000));
}
}
lightbulb

グローバルスコープがクエリのselect句に列を追加する場合は、select の代わりに addSelect メソッドを使用する必要があります。これにより、クエリの既存の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
{
/**
* The "booted" method of the model.
*/
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
{
/**
* The "booted" method of the model.
*/
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メソッドを使用できます。

// Remove all of the global scopes...
User::withoutGlobalScopes()->get();
 
// Remove some of the global scopes...
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
{
/**
* Scope a query to only include popular users.
*/
public function scopePopular(Builder $query): void
{
$query->where('votes', '>', 100);
}
 
/**
* Scope a query to only include active users.
*/
public function scopeActive(Builder $query): void
{
$query->where('active', 1);
}
}

ローカルスコープの活用

スコープが定義されると、モデルをクエリするときにスコープメソッドを呼び出すことができます。ただし、メソッドを呼び出すときに`scope`プレフィックスを含めるべきではありません。さまざまなスコープへの呼び出しをチェーンすることもできます。

use App\Models\User;
 
$users = User::popular()->active()->orderBy('created_at')->get();

`or` クエリ演算子を使用して複数のEloquentモデルスコープを組み合わせるには、正しい論理グループ化を実現するためにクロージャを使用する必要がある場合があります。

$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
{
/**
* Scope a query to only include users of a given type.
*/
public function scopeOfType(Builder $query, string $type): void
{
$query->where('type', $type);
}
}

必要な引数がスコープメソッドのシグネチャに追加されると、スコープを呼び出すときに引数を渡すことができます。

$users = User::ofType('admin')->get();

モデルの比較

2つのモデルが「同じ」かどうかを判断する必要がある場合があります。 `is` および `isNot` メソッドを使用して、2つのモデルが同じ主キー、テーブル、およびデータベース接続を持っているかどうかをすばやく確認できます。

if ($post->is($anotherPost)) {
// ...
}
 
if ($post->isNot($anotherPost)) {
// ...
}

`is` および `isNot` メソッドは、`belongsTo`、`hasOne`、`morphTo`、および `morphOne` リレーションシップを使用する場合にも使用できます。このメソッドは、そのモデルを取得するためのクエリを発行せずに、関連するモデルを比較したい場合に特に役立ちます。

if ($post->author()->is($user)) {
// ...
}

イベント

lightbulb

Eloquentイベントをクライアントサイドアプリケーションに直接ブロードキャストしますか? Laravelのモデルイベントブロードキャストをご覧ください。

Eloquentモデルはいくつかのイベントをディスパッチし、モデルのライフサイクルの次の瞬間にフックすることができます:`retrieved`、`creating`、`created`、`updating`、`updated`、`saving`、`saved`、`deleting`、`deleted`、`trashed`、`forceDeleting`、`forceDeleted`、`restoring`、`restored`、および`replicating`。

`retrieved`イベントは、既存のモデルがデータベースから取得されたときにディスパッチされます。新しいモデルが初めて保存されると、`creating`および`created`イベントがディスパッチされます。既存のモデルが変更され、`save`メソッドが呼び出されると、`updating` / `updated`イベントがディスパッチされます。 `saving` / `saved`イベントは、モデルの属性が変更されていない場合でも、モデルが作成または更新されたときにディスパッチされます。 `-ing`で終わるイベント名は、モデルへの変更が永続化される前にディスパッチされ、`-ed`で終わるイベント名は、モデルへの変更が永続化された後にディスパッチされます。

モデルイベントのリスニングを開始するには、Eloquentモデルに`$dispatchesEvents`プロパティを定義します。このプロパティは、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;
 
/**
* The event map for the model.
*
* @var array<string, string>
*/
protected $dispatchesEvents = [
'saved' => UserSaved::class,
'deleted' => UserDeleted::class,
];
}

Eloquentイベントを定義してマッピングした後、イベントリスナーを使用してイベントを処理できます。

exclamation

Eloquentを介して一括更新または削除クエリを発行する場合、影響を受けるモデルに対して`saved`、`updated`、`deleting`、および`deleted`モデルイベントはディスパッチされません。これは、一括更新または削除を実行するときにモデルが実際に取得されないためです。

クロージャの使用

カスタムイベントクラスを使用する代わりに、さまざまなモデルイベントがディスパッチされたときに実行されるクロージャを登録できます。通常、これらのクロージャはモデルの`booted`メソッドに登録する必要があります。

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
static::created(function (User $user) {
// ...
});
}
}

必要に応じて、モデルイベントを登録するときにキュー可能な匿名イベントリスナーを使用できます。これにより、Laravelはアプリケーションのキューを使用してバックグラウンドでモデルイベントリスナーを実行するように指示します。

use function Illuminate\Events\queueable;
 
static::created(queueable(function (User $user) {
// ...
}));

オブザーバー

オブザーバーの定義

特定のモデルで多くのイベントをリッスンしている場合は、オブザーバーを使用してすべてのリスナーを単一のクラスにグループ化できます。オブザーバークラスには、リッスンするEloquentイベントを反映したメソッド名があります。これらの各メソッドは、影響を受けるモデルを唯一の引数として受け取ります。 `make:observer` Artisanコマンドは、新しいオブザーバークラスを作成する最も簡単な方法です。

php artisan make:observer UserObserver --model=User

このコマンドは、新しいオブザーバーを`app/Observers`ディレクトリに配置します。このディレクトリが存在しない場合、Artisanはそれを自動的に作成します。新しいオブザーバーは次のようになります。

<?php
 
namespace App\Observers;
 
use App\Models\User;
 
class UserObserver
{
/**
* Handle the User "created" event.
*/
public function created(User $user): void
{
// ...
}
 
/**
* Handle the User "updated" event.
*/
public function updated(User $user): void
{
// ...
}
 
/**
* Handle the User "deleted" event.
*/
public function deleted(User $user): void
{
// ...
}
 
/**
* Handle the User "restored" event.
*/
public function restored(User $user): void
{
// ...
}
 
/**
* Handle the User "forceDeleted" event.
*/
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`メソッドを呼び出すことによって、オブザーバーを手動で登録できます。アプリケーションの`AppServiceProvider`クラスの`boot`メソッドにオブザーバーを登録できます。

use App\Models\User;
use App\Observers\UserObserver;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
User::observe(UserObserver::class);
}
lightbulb

オブザーバーがリッスンできる追加のイベント(`saving`や`retrieved`など)があります。これらのイベントについては、イベントドキュメントで説明されています。

オブザーバーとデータベーストランザクション

データベーストランザクション内でモデルが作成されている場合、データベーストランザクションがコミットされた後にのみイベントハンドラーを実行するようにオブザーバーに指示することができます。これは、オブザーバーに`ShouldHandleEventsAfterCommit`インターフェースを実装することで実現できます。データベーストランザクションが進行中でない場合、イベントハンドラーはすぐに実行されます。

<?php
 
namespace App\Observers;
 
use App\Models\User;
use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;
 
class UserObserver implements ShouldHandleEventsAfterCommit
{
/**
* Handle the User "created" event.
*/
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();