はじめに

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

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

Laravel ブートキャンプ

Laravel に不慣れな場合は、Laravel ブートキャンプに飛び込んでみてください。Laravel ブートキャンプでは、Eloquent を使用して最初の Laravel アプリケーションを構築する手順を案内します。これは、Laravel と Eloquent が提供するすべてを体験する素晴らしい方法です。

モデルクラスの生成

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

  1. php artisan make:model Flight

モデルを生成する際に データベースマイグレーションを生成したい場合は、--migration または -m オプションを使用できます:

  1. php artisan make:model Flight --migration

モデルを生成する際に、ファクトリ、シーダー、ポリシー、コントローラー、フォームリクエストなど、さまざまな他のタイプのクラスを生成することもできます。さらに、これらのオプションを組み合わせて、複数のクラスを一度に作成することもできます:

  1. # モデルと FlightFactory クラスを生成...
  2. php artisan make:model Flight --factory
  3. php artisan make:model Flight -f
  4. # モデルと FlightSeeder クラスを生成...
  5. php artisan make:model Flight --seed
  6. php artisan make:model Flight -s
  7. # モデルと FlightController クラスを生成...
  8. php artisan make:model Flight --controller
  9. php artisan make:model Flight -c
  10. # モデル、FlightController リソースクラス、およびフォームリクエストクラスを生成...
  11. php artisan make:model Flight --controller --resource --requests
  12. php artisan make:model Flight -crR
  13. # モデルと FlightPolicy クラスを生成...
  14. php artisan make:model Flight --policy
  15. # モデルとマイグレーション、ファクトリ、シーダー、コントローラーを生成...
  16. php artisan make:model Flight -mfsc
  17. # モデル、マイグレーション、ファクトリ、シーダー、ポリシー、コントローラー、およびフォームリクエストを生成するショートカット...
  18. php artisan make:model Flight --all
  19. php artisan make:model Flight -a
  20. # ピボットモデルを生成...
  21. php artisan make:model Member --pivot
  22. php artisan make:model Member -p

モデルの検査

時には、モデルのコードをざっと見ただけでは、すべての利用可能な属性や関係を特定するのが難しいことがあります。その代わりに、model:show Artisan コマンドを試してみてください。このコマンドは、モデルのすべての属性と関係の便利な概要を提供します:

  1. php artisan model:show Flight

Eloquent モデルの慣習

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

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class Flight extends Model
  5. {
  6. // ...
  7. }

テーブル名

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

モデルの対応するデータベーステーブルがこの慣習に合わない場合は、モデルに table プロパティを定義することで、モデルのテーブル名を手動で指定できます:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class Flight extends Model
  5. {
  6. /**
  7. * The table associated with the model.
  8. *
  9. * @var string
  10. */
  11. protected $table = 'my_flights';
  12. }

主キー

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

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class Flight extends Model
  5. {
  6. /**
  7. * The primary key associated with the table.
  8. *
  9. * @var string
  10. */
  11. protected $primaryKey = 'flight_id';
  12. }

さらに、Eloquent は主キーが増分整数値であると仮定します。つまり、Eloquent は主キーを自動的に整数にキャストします。非増分または非数値の主キーを使用する場合は、モデルに $incrementing プロパティを定義し、false に設定する必要があります:

  1. <?php
  2. class Flight extends Model
  3. {
  4. /**
  5. * Indicates if the model's ID is auto-incrementing.
  6. *
  7. * @var bool
  8. */
  9. public $incrementing = false;
  10. }

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

  1. <?php
  2. class Flight extends Model
  3. {
  4. /**
  5. * The data type of the primary key ID.
  6. *
  7. * @var string
  8. */
  9. protected $keyType = 'string';
  10. }

「複合」主キー

Eloquent は、各モデルに少なくとも 1 つの一意に識別できる「ID」が必要です。Eloquent モデルは「複合」主キーをサポートしていません。ただし、テーブルの一意に識別できる主キーに加えて、データベーステーブルに追加の複数列の一意インデックスを追加することは自由です。

UUID および ULID キー

Eloquent モデルの主キーとして自動増分整数の代わりに UUID を使用することを選択できます。UUID は、36 文字のユニバーサルに一意な英数字の識別子です。

モデルが自動増分整数キーの代わりに UUID キーを使用するようにするには、モデルに Illuminate\Database\Eloquent\Concerns\HasUuids トレイトを使用します。もちろん、モデルには UUID 相当の主キー列があることを確認する必要があります:

  1. use Illuminate\Database\Eloquent\Concerns\HasUuids;
  2. use Illuminate\Database\Eloquent\Model;
  3. class Article extends Model
  4. {
  5. use HasUuids;
  6. // ...
  7. }
  8. $article = Article::create(['title' => 'Traveling to Europe']);
  9. $article->id; // "8f8e8478-9035-4d23-b9a7-62f4d2612ce5"

デフォルトでは、HasUuids トレイトは、モデルのために 「順序付き」UUIDs を生成します。これらの UUID は、辞書順にソートできるため、インデックス付きデータベースストレージに対してより効率的です。

特定のモデルの UUID 生成プロセスをオーバーライドするには、モデルに newUniqueId メソッドを定義します。さらに、モデルにどの列が UUID を受け取るべきかを指定するには、モデルに uniqueIds メソッドを定義します:

  1. use Ramsey\Uuid\Uuid;
  2. /**
  3. * Generate a new UUID for the model.
  4. */
  5. public function newUniqueId(): string
  6. {
  7. return (string) Uuid::uuid4();
  8. }
  9. /**
  10. * Get the columns that should receive a unique identifier.
  11. *
  12. * @return array<int, string>
  13. */
  14. public function uniqueIds(): array
  15. {
  16. return ['id', 'discount_code'];
  17. }

希望する場合は、UUID の代わりに「ULID」を使用することもできます。ULID は UUID に似ていますが、長さは 26 文字です。順序付き UUID と同様に、ULID は効率的なデータベースインデックスのために辞書順にソート可能です。ULID を利用するには、モデルに Illuminate\Database\Eloquent\Concerns\HasUlids トレイトを使用する必要があります。また、モデルには ULID 相当の主キー列があることを確認する必要があります:

  1. use Illuminate\Database\Eloquent\Concerns\HasUlids;
  2. use Illuminate\Database\Eloquent\Model;
  3. class Article extends Model
  4. {
  5. use HasUlids;
  6. // ...
  7. }
  8. $article = Article::create(['title' => 'Traveling to Asia']);
  9. $article->id; // "01gd4d3tgrrfqeda94gdbtdk5c"

タイムスタンプ

デフォルトでは、Eloquent は created_at および updated_at 列がモデルの対応するデータベーステーブルに存在することを期待します。Eloquent は、モデルが作成または更新されるときに、これらの列の値を自動的に設定します。これらの列を Eloquent によって自動的に管理させたくない場合は、モデルに $timestamps プロパティを定義し、その値を false に設定する必要があります:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class Flight extends Model
  5. {
  6. /**
  7. * Indicates if the model should be timestamped.
  8. *
  9. * @var bool
  10. */
  11. public $timestamps = false;
  12. }

モデルのタイムスタンプの形式をカスタマイズする必要がある場合は、モデルに $dateFormat プロパティを設定します。このプロパティは、日付属性がデータベースにどのように保存されるか、およびモデルが配列または JSON にシリアライズされるときの形式を決定します:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class Flight extends Model
  5. {
  6. /**
  7. * The storage format of the model's date columns.
  8. *
  9. * @var string
  10. */
  11. protected $dateFormat = 'U';
  12. }

タイムスタンプを保存するために使用される列の名前をカスタマイズする必要がある場合は、モデルに CREATED_AT および UPDATED_AT 定数を定義できます:

  1. <?php
  2. class Flight extends Model
  3. {
  4. const CREATED_AT = 'creation_date';
  5. const UPDATED_AT = 'updated_date';
  6. }

モデルの updated_at タイムスタンプが変更されないようにモデル操作を行いたい場合は、withoutTimestamps メソッドに渡されたクロージャ内でモデルを操作できます:

  1. Model::withoutTimestamps(fn () => $post->increment('reads'));

データベース接続

デフォルトでは、すべての Eloquent モデルは、アプリケーションのために構成されたデフォルトのデータベース接続を使用します。特定のモデルとやり取りする際に使用する別の接続を指定したい場合は、モデルに $connection プロパティを定義する必要があります:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class Flight extends Model
  5. {
  6. /**
  7. * The database connection that should be used by the model.
  8. *
  9. * @var string
  10. */
  11. protected $connection = 'mysql';
  12. }

デフォルト属性値

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

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class Flight extends Model
  5. {
  6. /**
  7. * The model's default values for attributes.
  8. *
  9. * @var array
  10. */
  11. protected $attributes = [
  12. 'options' => '[]',
  13. 'delayed' => false,
  14. ];
  15. }

Eloquent の厳密性の設定

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

まず、preventLazyLoading メソッドは、遅延読み込みを防ぐかどうかを示すオプションのブール引数を受け取ります。たとえば、非本番環境でのみ遅延読み込みを無効にしたい場合があります。これにより、本番環境で遅延読み込みされた関係が誤って存在しても、通常通りに機能し続けます。通常、このメソッドはアプリケーションの boot メソッド内で呼び出されるべきです:

  1. use Illuminate\Database\Eloquent\Model;
  2. /**
  3. * Bootstrap any application services.
  4. */
  5. public function boot(): void
  6. {
  7. Model::preventLazyLoading(! $this->app->isProduction());
  8. }

また、preventSilentlyDiscardingAttributes メソッドを呼び出すことで、埋め込めない属性を埋め込もうとしたときに Laravel に例外をスローさせることができます。これにより、モデルの fillable 配列に追加されていない属性を設定しようとしたときに、ローカル開発中に予期しないエラーを防ぐのに役立ちます:

  1. Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction());

モデルの取得

モデルと その関連データベーステーブルを作成したら、データベースからデータを取得する準備が整いました。各 Eloquent モデルは、モデルに関連付けられたデータベーステーブルを流暢にクエリできる強力な クエリビルダー と考えることができます。モデルの all メソッドは、モデルに関連付けられたデータベーステーブルからすべてのレコードを取得します:

  1. use App\Models\Flight;
  2. foreach (Flight::all() as $flight) {
  3. echo $flight->name;
  4. }

クエリの構築

Eloquent all メソッドは、モデルのテーブル内のすべての結果を返します。ただし、各 Eloquent モデルは クエリビルダー として機能するため、クエリに追加の制約を追加し、get メソッドを呼び出して結果を取得できます:

  1. $flights = Flight::where('active', 1)
  2. ->orderBy('name')
  3. ->take(10)
  4. ->get();

Eloquent モデルはクエリビルダーであるため、Laravel の クエリビルダー が提供するすべてのメソッドを確認する必要があります。これらのメソッドは、Eloquent クエリを書く際に使用できます。

モデルのリフレッシュ

すでにデータベースから取得した Eloquent モデルのインスタンスがある場合、fresh および refresh メソッドを使用してモデルを「リフレッシュ」できます。fresh メソッドは、データベースからモデルを再取得します。既存のモデルインスタンスには影響しません:

  1. $flight = Flight::where('number', 'FR 900')->first();
  2. $freshFlight = $flight->fresh();

refresh メソッドは、データベースから新しいデータを使用して既存のモデルを再水和します。さらに、すべての読み込まれた関係もリフレッシュされます:

  1. $flight = Flight::where('number', 'FR 900')->first();
  2. $flight->number = 'FR 456';
  3. $flight->refresh();
  4. $flight->number; // "FR 900"

コレクション

ご覧のとおり、Eloquent メソッドの all および get は、データベースから複数のレコードを取得します。ただし、これらのメソッドはプレーンな PHP 配列を返しません。代わりに、Illuminate\Database\Eloquent\Collection のインスタンスが返されます。

Eloquent Collection クラスは、Laravel の基本 Illuminate\Support\Collection クラスを拡張しており、データコレクションと対話するための さまざまな便利なメソッドを提供します。たとえば、reject メソッドを使用して、呼び出されたクロージャの結果に基づいてコレクションからモデルを削除できます:

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

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

Laravel のすべてのコレクションは PHP の iterable インターフェースを実装しているため、コレクションを配列のようにループすることができます:

  1. foreach ($flights as $flight) {
  2. echo $flight->name;
  3. }

結果のチャンク処理

all または get メソッドを介して数万の Eloquent レコードを読み込もうとすると、アプリケーションがメモリ不足になる可能性があります。これらのメソッドを使用する代わりに、chunk メソッドを使用して、大量のモデルをより効率的に処理できます。

chunk メソッドは、Eloquent モデルのサブセットを取得し、それらを処理するためにクロージャに渡します。現在の Eloquent モデルのチャンクのみが一度に取得されるため、chunk メソッドは、大量のモデルを扱う際にメモリ使用量を大幅に削減します:

  1. use App\Models\Flight;
  2. use Illuminate\Database\Eloquent\Collection;
  3. Flight::chunk(200, function (Collection $flights) {
  4. foreach ($flights as $flight) {
  5. // ...
  6. }
  7. });

chunk メソッドに渡される最初の引数は、「チャンク」ごとに受け取りたいレコードの数です。2 番目の引数として渡されるクロージャは、データベースから取得される各チャンクに対して呼び出されます。クロージャに渡される各チャンクのレコードを取得するために、データベースクエリが実行されます。

chunk メソッドの結果を、結果を反復処理しながら更新する列に基づいてフィルタリングしている場合は、chunkById メソッドを使用する必要があります。これらのシナリオで chunk メソッドを使用すると、予期しない不整合な結果が生じる可能性があります。内部的に、chunkById メソッドは、常に前のチャンクの最後のモデルよりも id 列が大きいモデルを取得します:

  1. Flight::where('departed', true)
  2. ->chunkById(200, function (Collection $flights) {
  3. $flights->each->update(['departed' => false]);
  4. }, $column = 'id');

遅延コレクションを使用したチャンク処理

lazy メソッドは、背後でクエリをチャンクで実行するという点で、chunk メソッドと似ています。ただし、各チャンクをそのままコールバックに渡すのではなく、lazy メソッドは Eloquent モデルのフラットな LazyCollection を返し、結果を単一のストリームとして操作できるようにします:

  1. use App\Models\Flight;
  2. foreach (Flight::lazy() as $flight) {
  3. // ...
  4. }

lazy メソッドの結果を、結果を反復処理しながら更新する列に基づいてフィルタリングしている場合は、lazyById メソッドを使用する必要があります。内部的に、lazyById メソッドは、常に前のチャンクの最後のモデルよりも id 列が大きいモデルを取得します:

  1. Flight::where('departed', true)
  2. ->lazyById(200, $column = 'id')
  3. ->each->update(['departed' => false]);

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

カーソル

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

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

cursor メソッドは、常にメモリに 1 つの Eloquent モデルしか保持しないため、リレーションシップをイager ロードすることはできません。リレーションシップをイager ロードする必要がある場合は、lazy メソッドを使用することを検討してください。

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

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

cursorIlluminate\Support\LazyCollection インスタンスを返します。遅延コレクションを使用すると、通常の Laravel コレクションで利用可能な多くのコレクションメソッドを使用しながら、同時にメモリに 1 つのモデルしか読み込むことができます:

  1. use App\Models\User;
  2. $users = User::cursor()->filter(function (User $user) {
  3. return $user->id > 500;
  4. });
  5. foreach ($users as $user) {
  6. echo $user->id;
  7. }

cursor メソッドは、通常のクエリよりもはるかに少ないメモリを使用しますが(同時に 1 つの Eloquent モデルしかメモリに保持しないため)、最終的にはメモリが不足します。これは、PHP の PDO ドライバーが内部的にすべての生のクエリ結果をバッファにキャッシュするためです。非常に多くの Eloquent レコードを扱う場合は、lazy メソッドを使用することを検討してください。

高度なサブクエリ

サブクエリの選択

Eloquent は、関連テーブルから情報を単一のクエリで取得できる高度なサブクエリサポートも提供しています。たとえば、フライトの destinations テーブルと目的地の flights テーブルがあると仮定しましょう。flights テーブルには、フライトが目的地に到着した時刻を示す arrived_at 列があります。

クエリビルダーの select および addSelect メソッドで利用可能なサブクエリ機能を使用して、単一のクエリを使用して、destinations とその目的地に最も最近到着したフライトの名前を選択できます:

  1. use App\Models\Destination;
  2. use App\Models\Flight;
  3. return Destination::addSelect(['last_flight' => Flight::select('name')
  4. ->whereColumn('destination_id', 'destinations.id')
  5. ->orderByDesc('arrived_at')
  6. ->limit(1)
  7. ])->get();

サブクエリの順序付け

さらに、クエリビルダーの orderBy 関数はサブクエリをサポートしています。フライトの例を引き続き使用すると、最後のフライトがその目的地に到着した時刻に基づいてすべての目的地をソートするためにこの機能を使用できます。これも、単一のデータベースクエリを実行しながら行うことができます:

  1. return Destination::orderByDesc(
  2. Flight::select('arrived_at')
  3. ->whereColumn('destination_id', 'destinations.id')
  4. ->orderByDesc('arrived_at')
  5. ->limit(1)
  6. )->get();

単一モデル / 集約の取得

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

  1. use App\Models\Flight;
  2. // Retrieve a model by its primary key...
  3. $flight = Flight::find(1);
  4. // Retrieve the first model matching the query constraints...
  5. $flight = Flight::where('active', 1)->first();
  6. // Alternative to retrieving the first model matching the query constraints...
  7. $flight = Flight::firstWhere('active', 1);

時には、結果が見つからない場合に他のアクションを実行したい場合があります。findOr および firstOr メソッドは、単一のモデルインスタンスを返すか、結果が見つからない場合は指定されたクロージャを実行します。クロージャによって返される値は、メソッドの結果と見なされます:

  1. $flight = Flight::findOr(1, function () {
  2. // ...
  3. });
  4. $flight = Flight::where('legs', '>', 3)->firstOr(function () {
  5. // ...
  6. });

見つからない例外

モデルが見つからない場合に例外をスローしたい場合があります。これは、ルートやコントローラーで特に便利です。findOrFail および firstOrFail メソッドは、クエリの最初の結果を取得します。ただし、結果が見つからない場合は、Illuminate\Database\Eloquent\ModelNotFoundException がスローされます:

  1. $flight = Flight::findOrFail(1);
  2. $flight = Flight::where('legs', '>', 3)->firstOrFail();

ModelNotFoundException がキャッチされない場合、404 HTTP レスポンスが自動的にクライアントに返されます:

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

モデルの取得または作成

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

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

  1. use App\Models\Flight;
  2. // Retrieve flight by name or create it if it doesn't exist...
  3. $flight = Flight::firstOrCreate([
  4. 'name' => 'London to Paris'
  5. ]);
  6. // Retrieve flight by name or create it with the name, delayed, and arrival_time attributes...
  7. $flight = Flight::firstOrCreate(
  8. ['name' => 'London to Paris'],
  9. ['delayed' => 1, 'arrival_time' => '11:30']
  10. );
  11. // Retrieve flight by name or instantiate a new Flight instance...
  12. $flight = Flight::firstOrNew([
  13. 'name' => 'London to Paris'
  14. ]);
  15. // Retrieve flight by name or instantiate with the name, delayed, and arrival_time attributes...
  16. $flight = Flight::firstOrNew(
  17. ['name' => 'Tokyo to Sydney'],
  18. ['delayed' => 1, 'arrival_time' => '11:30']
  19. );

集約の取得

Eloquent モデルと対話する際に、countsummax、および Laravel クエリビルダー が提供する他の 集約メソッド を使用することもできます。これらのメソッドは、Eloquent モデルインスタンスの代わりにスカラー値を返します:

  1. $count = Flight::where('active', 1)->count();
  2. $max = Flight::where('active', 1)->max('price');

モデルの挿入と更新

挿入

もちろん、Eloquent を使用する際には、データベースからモデルを取得するだけではありません。新しいレコードを挿入する必要もあります。幸いなことに、Eloquent はそれを簡単にします。データベースに新しいレコードを挿入するには、新しいモデルインスタンスをインスタンス化し、モデルの属性を設定します。その後、モデルインスタンスで save メソッドを呼び出します:

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Flight;
  5. use Illuminate\Http\RedirectResponse;
  6. use Illuminate\Http\Request;
  7. class FlightController extends Controller
  8. {
  9. /**
  10. * Store a new flight in the database.
  11. */
  12. public function store(Request $request): RedirectResponse
  13. {
  14. // Validate the request...
  15. $flight = new Flight;
  16. $flight->name = $request->name;
  17. $flight->save();
  18. return redirect('/flights');
  19. }
  20. }

この例では、受信した HTTP リクエストから name フィールドを name 属性に割り当てます。save メソッドを呼び出すと、レコードがデータベースに挿入されます。モデルの created_at および updated_at タイムスタンプは、save メソッドが呼び出されると自動的に設定されるため、手動で設定する必要はありません。

また、create メソッドを使用して、単一の PHP ステートメントを使用して新しいモデルを「保存」することもできます。挿入されたモデルインスタンスは、create メソッドによって返されます:

  1. use App\Models\Flight;
  2. $flight = Flight::create([
  3. 'name' => 'London to Paris',
  4. ]);

ただし、create メソッドを使用する前に、モデルクラスに fillable または guarded プロパティを指定する必要があります。これらのプロパティは、すべての Eloquent モデルがデフォルトでマスアサインメントの脆弱性から保護されているため、必須です。マスアサインメントについて詳しくは、マスアサインメントのドキュメントを参照してください。

更新

save メソッドは、すでにデータベースに存在するモデルを更新するためにも使用できます。モデルを更新するには、それを取得し、更新したい属性を設定します。その後、モデルの save メソッドを呼び出す必要があります。再度、updated_at タイムスタンプは自動的に更新されるため、その値を手動で設定する必要はありません:

  1. use App\Models\Flight;
  2. $flight = Flight::find(1);
  3. $flight->name = 'Paris to London';
  4. $flight->save();

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

以下の例では、departure の場所が Oakland で、destination の場所が San Diego のフライトが存在する場合、その price および discounted 列が更新されます。そのようなフライトが存在しない場合、最初の引数配列と第二引数配列をマージした属性を持つ新しいフライトが作成されます:

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

マス更新

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

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

update メソッドは、更新すべき列を表す列と値のペアの配列を期待します。update メソッドは、影響を受けた行数を返します。

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

属性変更の検査

Eloquent は、モデルの内部状態を検査し、モデルが元々取得されたときから属性がどのように変更されたかを判断するために、isDirtyisClean、および wasChanged メソッドを提供します。

isDirty メソッドは、モデルが取得されて以来、モデルの属性が変更されたかどうかを判断します。isDirty メソッドに特定の属性名または属性の配列を渡すことで、属性が「汚れている」かどうかを判断できます。isClean メソッドは、モデルが取得されて以来、属性が変更されていないかどうかを判断します。このメソッドは、オプションの属性引数も受け取ります:

  1. use App\Models\User;
  2. $user = User::create([
  3. 'first_name' => 'Taylor',
  4. 'last_name' => 'Otwell',
  5. 'title' => 'Developer',
  6. ]);
  7. $user->title = 'Painter';
  8. $user->isDirty(); // true
  9. $user->isDirty('title'); // true
  10. $user->isDirty('first_name'); // false
  11. $user->isDirty(['first_name', 'title']); // true
  12. $user->isClean(); // false
  13. $user->isClean('title'); // false
  14. $user->isClean('first_name'); // true
  15. $user->isClean(['first_name', 'title']); // false
  16. $user->save();
  17. $user->isDirty(); // false
  18. $user->isClean(); // true

wasChanged メソッドは、モデルが現在のリクエストサイクル内で最後に保存されたときに、属性が変更されたかどうかを判断します。必要に応じて、特定の属性名を渡して、特定の属性が変更されたかどうかを確認できます:

  1. $user = User::create([
  2. 'first_name' => 'Taylor',
  3. 'last_name' => 'Otwell',
  4. 'title' => 'Developer',
  5. ]);
  6. $user->title = 'Painter';
  7. $user->save();
  8. $user->wasChanged(); // true
  9. $user->wasChanged('title'); // true
  10. $user->wasChanged(['title', 'slug']); // true
  11. $user->wasChanged('first_name'); // false
  12. $user->wasChanged(['first_name', 'title']); // true

getOriginal メソッドは、モデルが取得されて以来の変更に関係なく、モデルの元の属性を含む配列を返します。必要に応じて、特定の属性名を渡して、特定の属性の元の値を取得できます:

  1. $user = User::find(1);
  2. $user->name; // John
  3. $user->email; //
  4. $user->name = "Jack";
  5. $user->name; // Jack
  6. $user->getOriginal('name'); // John
  7. $user->getOriginal(); // Array of original attributes...

マスアサインメント

create メソッドを使用して、単一の PHP ステートメントを使用して新しいモデルを「保存」できます。挿入されたモデルインスタンスは、メソッドによって返されます:

  1. use App\Models\Flight;
  2. $flight = Flight::create([
  3. 'name' => 'London to Paris',
  4. ]);

ただし、create メソッドを使用する前に、モデルクラスに fillable または guarded プロパティを指定する必要があります。これらのプロパティは、すべての Eloquent モデルがデフォルトでマスアサインメントの脆弱性から保護されているため、必須です。

マスアサインメントの脆弱性は、ユーザーが予期しない HTTP リクエストフィールドを渡し、そのフィールドが予期しないデータベースの列を変更する場合に発生します。たとえば、悪意のあるユーザーが HTTP リクエストを介して is_admin パラメータを送信し、それがモデルの create メソッドに渡され、ユーザーが管理者に昇格することを可能にします。

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

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class Flight extends Model
  5. {
  6. /**
  7. * The attributes that are mass assignable.
  8. *
  9. * @var array
  10. */
  11. protected $fillable = ['name'];
  12. }

マスアサイン可能な属性を指定したら、create メソッドを使用してデータベースに新しいレコードを挿入できます。create メソッドは、新しく作成されたモデルインスタンスを返します:

  1. $flight = Flight::create(['name' => 'London to Paris']);

すでにモデルインスタンスがある場合は、fill メソッドを使用して、属性の配列でそれを埋めることができます:

  1. $flight->fill(['name' => 'Amsterdam to Frankfurt']);

マスアサインメントと JSON 列

JSON 列を割り当てる場合、各列のマスアサイン可能なキーは、モデルの $fillable 配列に指定する必要があります。セキュリティ上の理由から、Laravel は guarded プロパティを使用してネストされた JSON 属性の更新をサポートしていません:

  1. /**
  2. * The attributes that are mass assignable.
  3. *
  4. * @var array
  5. */
  6. protected $fillable = [
  7. 'options->enabled',
  8. ];

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

すべての属性をマスアサイン可能にしたい場合は、モデルの $guarded プロパティを空の配列として定義できます。モデルのガードを解除することを選択した場合は、Eloquent の fillcreate、および update メソッドに渡す配列を常に手作りするように特に注意してください:

  1. /**
  2. * The attributes that aren't mass assignable.
  3. *
  4. * @var array
  5. */
  6. protected $guarded = [];

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

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

希望する場合は、preventSilentlyDiscardingAttributes メソッドを呼び出すことで、埋め込むことができない属性を埋め込もうとしたときに Laravel に例外をスローさせることができます。通常、このメソッドはアプリケーションの boot メソッド内で呼び出されるべきです:

  1. use Illuminate\Database\Eloquent\Model;
  2. /**
  3. * Bootstrap any application services.
  4. */
  5. public function boot(): void
  6. {
  7. Model::preventSilentlyDiscardingAttributes($this->app->isLocal());
  8. }

アップサート

Eloquent の upsert メソッドは、単一の原子操作でレコードを更新または作成するために使用できます。このメソッドの最初の引数は挿入または更新する値で構成され、2 番目の引数は関連するテーブル内のレコードを一意に識別する列をリストします。メソッドの 3 番目で最終的な引数は、データベースに一致するレコードが既に存在する場合に更新されるべき列の配列です。upsert メソッドは、モデルでタイムスタンプが有効になっている場合、created_at および updated_at タイムスタンプを自動的に設定します:

  1. Flight::upsert([
  2. ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
  3. ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
  4. ], uniqueBy: ['departure', 'destination'], update: ['price']);

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

モデルの削除

モデルを削除するには、モデルインスタンスで delete メソッドを呼び出します:

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

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

  1. Flight::truncate();

プライマリキーによる既存モデルの削除

上記の例では、delete メソッドを呼び出す前にデータベースからモデルを取得しています。ただし、モデルのプライマリキーがわかっている場合は、destroy メソッドを呼び出すことで、明示的に取得せずにモデルを削除できます。単一のプライマリキーを受け入れるだけでなく、destroy メソッドは複数のプライマリキー、プライマリキーの配列、またはプライマリキーの コレクション を受け入れます:

  1. Flight::destroy(1);
  2. Flight::destroy(1, 2, 3);
  3. Flight::destroy([1, 2, 3]);
  4. Flight::destroy(collect([1, 2, 3]));

ソフトデリートモデルを利用している場合は、forceDestroy メソッドを介してモデルを永久に削除できます:

  1. Flight::forceDestroy(1);

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

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

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

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

Eloquent を介してマス削除ステートメントを実行する際、deleting および deleted モデルイベントは削除されたモデルに対してディスパッチされません。これは、削除ステートメントを実行する際にモデルが実際に取得されないためです。

ソフトデリート

データベースからレコードを実際に削除することに加えて、Eloquent はモデルを「ソフトデリート」することもできます。モデルがソフトデリートされると、実際にはデータベースから削除されません。代わりに、モデルに deleted_at 属性が設定され、モデルが「削除された」日時が示されます。モデルにソフトデリートを有効にするには、Illuminate\Database\Eloquent\SoftDeletes トレイトをモデルに追加します:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. use Illuminate\Database\Eloquent\SoftDeletes;
  5. class Flight extends Model
  6. {
  7. use SoftDeletes;
  8. }

SoftDeletes トレイトは、deleted_at 属性を DateTime / Carbon インスタンスに自動的にキャストします。

また、データベーステーブルに deleted_at 列を追加する必要があります。Laravel の スキーマビルダー には、この列を作成するためのヘルパーメソッドが含まれています:

  1. use Illuminate\Database\Schema\Blueprint;
  2. use Illuminate\Support\Facades\Schema;
  3. Schema::table('flights', function (Blueprint $table) {
  4. $table->softDeletes();
  5. });
  6. Schema::table('flights', function (Blueprint $table) {
  7. $table->dropSoftDeletes();
  8. });

モデルで delete メソッドを呼び出すと、deleted_at 列が現在の日付と時刻に設定されます。ただし、モデルのデータベースレコードはテーブルに残ります。ソフトデリートを使用するモデルをクエリすると、ソフトデリートされたモデルはすべてのクエリ結果から自動的に除外されます。

特定のモデルインスタンスがソフトデリートされているかどうかを判断するには、trashed メソッドを使用できます:

  1. if ($flight->trashed()) {
  2. // ...
  3. }

ソフトデリートされたモデルの復元

時には、ソフトデリートされたモデルを「復元」したい場合があります。ソフトデリートされたモデルを復元するには、モデルインスタンスで restore メソッドを呼び出します。restore メソッドは、モデルの deleted_at 列を null に設定します:

  1. $flight->restore();

クエリ内で restore メソッドを使用して複数のモデルを復元することもできます。再度、他の「マス」操作と同様に、復元されたモデルのモデルイベントはディスパッチされません:

  1. Flight::withTrashed()
  2. ->where('airline_id', 1)
  3. ->restore();

restore メソッドは、リレーションシップ クエリを構築する際にも使用できます:

  1. $flight->history()->restore();

モデルの永久削除

時には、データベースからモデルを完全に削除する必要があります。forceDelete メソッドを使用して、ソフトデリートされたモデルをデータベーステーブルから永久に削除できます:

  1. $flight->forceDelete();

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

  1. $flight->history()->forceDelete();

ソフトデリートされたモデルのクエリ

ソフトデリートされたモデルの含め方

上記のように、ソフトデリートされたモデルはクエリ結果から自動的に除外されます。ただし、withTrashed メソッドをクエリに呼び出すことで、ソフトデリートされたモデルをクエリの結果に含めることができます:

  1. use App\Models\Flight;
  2. $flights = Flight::withTrashed()
  3. ->where('account_id', 1)
  4. ->get();

withTrashed メソッドは、リレーションシップ クエリを構築する際にも呼び出すことができます:

  1. $flight->history()->withTrashed()->get();

ソフトデリートされたモデルのみを取得

onlyTrashed メソッドは ソフトデリートされたモデルのみ を取得します:

  1. $flights = Flight::onlyTrashed()
  2. ->where('airline_id', 1)
  3. ->get();

モデルのプルーニング

時には、もはや必要のないモデルを定期的に削除したい場合があります。これを実現するには、定期的にプルーニングしたいモデルに Illuminate\Database\Eloquent\Prunable または Illuminate\Database\Eloquent\MassPrunable トレイトを追加します。モデルにトレイトのいずれかを追加した後、もはや必要のないモデルを解決する Eloquent クエリビルダーを返す prunable メソッドを実装します:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Builder;
  4. use Illuminate\Database\Eloquent\Model;
  5. use Illuminate\Database\Eloquent\Prunable;
  6. class Flight extends Model
  7. {
  8. use Prunable;
  9. /**
  10. * Get the prunable model query.
  11. */
  12. public function prunable(): Builder
  13. {
  14. return static::where('created_at', '<=', now()->subMonth());
  15. }
  16. }

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

  1. /**
  2. * Prepare the model for pruning.
  3. */
  4. protected function pruning(): void
  5. {
  6. // ...
  7. }

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

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

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

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

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

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

prunable クエリをテストするには、model:prune コマンドを --pretend オプションで実行します。擬似実行中、model:prune コマンドは、コマンドが実際に実行された場合にプルーニングされるレコードの数を報告します:

  1. php artisan model:prune --pretend

ソフトデリートされたモデルは、プルーニング可能なクエリに一致する場合、永久に削除されます (forceDelete)。

マスプルーニング

モデルが Illuminate\Database\Eloquent\MassPrunable トレイトでマークされている場合、モデルはマス削除クエリを使用してデータベースから削除されます。したがって、pruning メソッドは呼び出されず、deleting および deleted モデルイベントはディスパッチされません。これは、モデルが削除前に実際に取得されないため、プルーニングプロセスがはるかに効率的になるためです:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Builder;
  4. use Illuminate\Database\Eloquent\Model;
  5. use Illuminate\Database\Eloquent\MassPrunable;
  6. class Flight extends Model
  7. {
  8. use MassPrunable;
  9. /**
  10. * Get the prunable model query.
  11. */
  12. public function prunable(): Builder
  13. {
  14. return static::where('created_at', '<=', now()->subMonth());
  15. }
  16. }

モデルの複製

既存のモデルインスタンスの未保存のコピーを replicate メソッドを使用して作成できます。このメソッドは、同じ属性を多く共有するモデルインスタンスがある場合に特に便利です:

  1. use App\Models\Address;
  2. $shipping = Address::create([
  3. 'type' => 'shipping',
  4. 'line_1' => '123 Example Street',
  5. 'city' => 'Victorville',
  6. 'state' => 'CA',
  7. 'postcode' => '90001',
  8. ]);
  9. $billing = $shipping->replicate()->fill([
  10. 'type' => 'billing'
  11. ]);
  12. $billing->save();

新しいモデルに複製されない属性を 1 つ以上除外するには、replicate メソッドに配列を渡すことができます:

  1. $flight = Flight::create([
  2. 'destination' => 'LAX',
  3. 'origin' => 'LHR',
  4. 'last_flown' => '2020-03-04 11:00:00',
  5. 'last_pilot_id' => 747,
  6. ]);
  7. $flight = $flight->replicate([
  8. 'last_flown',
  9. 'last_pilot_id'
  10. ]);

クエリスコープ

グローバルスコープ

グローバルスコープを使用すると、特定のモデルのすべてのクエリに制約を追加できます。Laravel の独自の ソフトデリート 機能は、グローバルスコープを利用してデータベースから「削除されていない」モデルのみを取得します。独自のグローバルスコープを書くことで、特定のモデルのすべてのクエリに特定の制約を受ける便利で簡単な方法を提供できます。

スコープの生成

新しいグローバルスコープを生成するには、make:scope Artisan コマンドを呼び出すことで、生成されたスコープがアプリケーションの app/Models/Scopes ディレクトリに配置されます:

  1. php artisan make:scope AncientScope

グローバルスコープの作成

グローバルスコープを書くのは簡単です。まず、make:scope コマンドを使用して Illuminate\Database\Eloquent\Scope インターフェースを実装するクラスを生成します。Scope インターフェースでは、1 つのメソッド apply を実装する必要があります。apply メソッドは、必要に応じてクエリに where 制約やその他のタイプの句を追加できます:

  1. <?php
  2. namespace App\Models\Scopes;
  3. use Illuminate\Database\Eloquent\Builder;
  4. use Illuminate\Database\Eloquent\Model;
  5. use Illuminate\Database\Eloquent\Scope;
  6. class AncientScope implements Scope
  7. {
  8. /**
  9. * Apply the scope to a given Eloquent query builder.
  10. */
  11. public function apply(Builder $builder, Model $model): void
  12. {
  13. $builder->where('created_at', '<', now()->subYears(2000));
  14. }
  15. }

クエリの選択句に列を追加している場合は、addSelect メソッドを使用する必要があります。select を使用すると、クエリの既存の選択句が意図せず置き換えられるのを防ぐことができます。

グローバルスコープの適用

モデルにグローバルスコープを割り当てるには、モデルに ScopedBy 属性を単純に置くだけです:

  1. <?php
  2. namespace App\Models;
  3. use App\Models\Scopes\AncientScope;
  4. use Illuminate\Database\Eloquent\Attributes\ScopedBy;
  5. #[ScopedBy([AncientScope::class])]
  6. class User extends Model
  7. {
  8. //
  9. }

または、モデルの booted メソッドをオーバーライドしてグローバルスコープを手動で登録し、モデルの addGlobalScope メソッドを呼び出すことができます。addGlobalScope メソッドは、スコープのインスタンスを唯一の引数として受け取ります:

  1. <?php
  2. namespace App\Models;
  3. use App\Models\Scopes\AncientScope;
  4. use Illuminate\Database\Eloquent\Model;
  5. class User extends Model
  6. {
  7. /**
  8. * The "booted" method of the model.
  9. */
  10. protected static function booted(): void
  11. {
  12. static::addGlobalScope(new AncientScope);
  13. }
  14. }

上記の例で App\Models\User モデルにスコープを追加した後、User::all() メソッドを呼び出すと、次の SQL クエリが実行されます:

  1. select * from `users` where `created_at` < 0021-02-18 00:00:00

匿名グローバルスコープ

Eloquent は、クロージャを使用してグローバルスコープを定義することもでき、これは特に独自のクラスを必要としない単純なスコープに便利です。クロージャを使用してグローバルスコープを定義する場合は、addGlobalScope メソッドの最初の引数として独自のスコープ名を提供する必要があります:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Builder;
  4. use Illuminate\Database\Eloquent\Model;
  5. class User extends Model
  6. {
  7. /**
  8. * The "booted" method of the model.
  9. */
  10. protected static function booted(): void
  11. {
  12. static::addGlobalScope('ancient', function (Builder $builder) {
  13. $builder->where('created_at', '<', now()->subYears(2000));
  14. });
  15. }
  16. }

グローバルスコープの削除

特定のクエリのグローバルスコープを削除したい場合は、withoutGlobalScope メソッドを使用できます。このメソッドは、グローバルスコープのクラス名を唯一の引数として受け取ります:

  1. User::withoutGlobalScope(AncientScope::class)->get();

また、クロージャを使用してグローバルスコープを定義した場合は、グローバルスコープに割り当てた文字列名を渡す必要があります:

  1. User::withoutGlobalScope('ancient')->get();

クエリのグローバルスコープをいくつか、またはすべて削除したい場合は、withoutGlobalScopes メソッドを使用できます:

  1. // Remove all of the global scopes...
  2. User::withoutGlobalScopes()->get();
  3. // Remove some of the global scopes...
  4. User::withoutGlobalScopes([
  5. FirstScope::class, SecondScope::class
  6. ])->get();

ローカルスコープ

ローカルスコープを使用すると、アプリケーション全体で簡単に再利用できる一般的なクエリ制約のセットを定義できます。たとえば、「人気」と見なされるすべてのユーザーを頻繁に取得する必要があるかもしれません。スコープを定義するには、Eloquent モデルメソッドに scope プレフィックスを付けます。

スコープは常に同じクエリビルダーインスタンスまたは void を返す必要があります:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Builder;
  4. use Illuminate\Database\Eloquent\Model;
  5. class User extends Model
  6. {
  7. /**
  8. * Scope a query to only include popular users.
  9. */
  10. public function scopePopular(Builder $query): void
  11. {
  12. $query->where('votes', '>', 100);
  13. }
  14. /**
  15. * Scope a query to only include active users.
  16. */
  17. public function scopeActive(Builder $query): void
  18. {
  19. $query->where('active', 1);
  20. }
  21. }

ローカルスコープの利用

スコープが定義されたら、モデルをクエリする際にスコープメソッドを呼び出すことができます。ただし、メソッドを呼び出す際に scope プレフィックスを含めないでください。さまざまなスコープへの呼び出しを連鎖させることもできます:

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

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

  1. $users = User::popular()->orWhere(function (Builder $query) {
  2. $query->active();
  3. })->get();

ただし、これは面倒な場合があるため、Laravel はクロージャを使用せずにスコープを流暢に連鎖させることができる「高次の」 orWhere メソッドを提供します:

  1. $users = User::popular()->orWhere->active()->get();

動的スコープ

時には、パラメータを受け入れるスコープを定義したい場合があります。始めるには、スコープメソッドのシグネチャに追加のパラメータを追加します。スコープパラメータは、$query パラメータの後に定義する必要があります:

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Builder;
  4. use Illuminate\Database\Eloquent\Model;
  5. class User extends Model
  6. {
  7. /**
  8. * Scope a query to only include users of a given type.
  9. */
  10. public function scopeOfType(Builder $query, string $type): void
  11. {
  12. $query->where('type', $type);
  13. }
  14. }

期待される引数がスコープメソッドのシグネチャに追加されたら、スコープを呼び出すときに引数を渡すことができます:

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

モデルの比較

時には、2 つのモデルが「同じ」かどうかを判断する必要があります。is および isNot メソッドを使用して、2 つのモデルが同じプライマリキー、テーブル、およびデータベース接続を持っているかどうかを迅速に確認できます:

  1. if ($post->is($anotherPost)) {
  2. // ...
  3. }
  4. if ($post->isNot($anotherPost)) {
  5. // ...
  6. }

is および isNot メソッドは、belongsTohasOnemorphTo、および morphOne リレーションシップ を使用する際にも利用可能です。このメソッドは、クエリを発行して関連モデルを取得せずに、関連モデルを比較したい場合に特に役立ちます:

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

イベント

Eloquent イベントをクライアントサイドアプリケーションに直接ブロードキャストしたいですか?Laravel の モデルイベントブロードキャスティング をチェックしてください。

Eloquent モデルは、モデルのライフサイクルの次の瞬間にフックすることを可能にするいくつかのイベントをディスパッチします: retrievedcreatingcreatedupdatingupdatedsavingsaveddeletingdeletedtrashedforceDeletingforceDeletedrestoringrestored、および replicating

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

モデルイベントをリッスンし始めるには、Eloquent モデルに $dispatchesEvents プロパティを定義します。このプロパティは、Eloquent モデルのライフサイクルのさまざまなポイントを独自の イベントクラス にマッピングします。各モデルイベントクラスは、コンストラクタを介して影響を受けたモデルのインスタンスを受け取ることを期待する必要があります:

  1. <?php
  2. namespace App\Models;
  3. use App\Events\UserDeleted;
  4. use App\Events\UserSaved;
  5. use Illuminate\Foundation\Auth\User as Authenticatable;
  6. use Illuminate\Notifications\Notifiable;
  7. class User extends Authenticatable
  8. {
  9. use Notifiable;
  10. /**
  11. * The event map for the model.
  12. *
  13. * @var array<string, string>
  14. */
  15. protected $dispatchesEvents = [
  16. 'saved' => UserSaved::class,
  17. 'deleted' => UserDeleted::class,
  18. ];
  19. }

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

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

クロージャの使用

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

  1. <?php
  2. namespace App\Models;
  3. use Illuminate\Database\Eloquent\Model;
  4. class User extends Model
  5. {
  6. /**
  7. * The "booted" method of the model.
  8. */
  9. protected static function booted(): void
  10. {
  11. static::created(function (User $user) {
  12. // ...
  13. });
  14. }
  15. }

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

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

オブザーバー

オブザーバーの定義

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

  1. php artisan make:observer UserObserver --model=User

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

  1. <?php
  2. namespace App\Observers;
  3. use App\Models\User;
  4. class UserObserver
  5. {
  6. /**
  7. * Handle the User "created" event.
  8. */
  9. public function created(User $user): void
  10. {
  11. // ...
  12. }
  13. /**
  14. * Handle the User "updated" event.
  15. */
  16. public function updated(User $user): void
  17. {
  18. // ...
  19. }
  20. /**
  21. * Handle the User "deleted" event.
  22. */
  23. public function deleted(User $user): void
  24. {
  25. // ...
  26. }
  27. /**
  28. * Handle the User "restored" event.
  29. */
  30. public function restored(User $user): void
  31. {
  32. // ...
  33. }
  34. /**
  35. * Handle the User "forceDeleted" event.
  36. */
  37. public function forceDeleted(User $user): void
  38. {
  39. // ...
  40. }
  41. }

オブザーバーを登録するには、対応するモデルに ObservedBy 属性を置くことができます:

  1. use App\Observers\UserObserver;
  2. use Illuminate\Database\Eloquent\Attributes\ObservedBy;
  3. #[ObservedBy([UserObserver::class])]
  4. class User extends Authenticatable
  5. {
  6. //
  7. }

または、観察したいモデルで observe メソッドを呼び出すことでオブザーバーを手動で登録できます。アプリケーションの boot メソッドでオブザーバーを登録できます:

  1. use App\Models\User;
  2. use App\Observers\UserObserver;
  3. /**
  4. * Bootstrap any application services.
  5. */
  6. public function boot(): void
  7. {
  8. User::observe(UserObserver::class);
  9. }

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

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

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

  1. <?php
  2. namespace App\Observers;
  3. use App\Models\User;
  4. use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;
  5. class UserObserver implements ShouldHandleEventsAfterCommit
  6. {
  7. /**
  8. * Handle the User "created" event.
  9. */
  10. public function created(User $user): void
  11. {
  12. // ...
  13. }
  14. }

イベントのミュート

モデルによって発火されたすべてのイベントを一時的に「ミュート」する必要がある場合があります。これを withoutEvents メソッドを使用して実現できます。withoutEvents メソッドは、唯一の引数としてクロージャを受け取ります。このクロージャ内で実行されるコードは、モデルイベントをディスパッチせず、クロージャによって返される値は withoutEvents メソッドによって返されます:

  1. use App\Models\User;
  2. $user = User::withoutEvents(function () {
  3. User::findOrFail(1)->delete();
  4. return User::find(2);
  5. });

Eloquent: 始めに