Laravelはオープンソースの使いやすいPHPフレームワークとして人気を博します。その便利な機能の1つがEloquentで、データベースのレコード処理を簡素化するオブジェクト関係マッピング(ORM)です。

Eloquentは、アプリケーションからのデータベースに対する作成、読み取り、更新、および削除操作を高速化します。Eloquentを使用することで、データベースのテーブルをミラーするモデルを用意し、それを使用してクエリを作成できます。

この記事では、Eloquentの最も便利な機能であるクエリスコープ、リレーション、ミューテタとアクセサ、コレクション、モデル削除、ファクトリの6つについてご説明します。それぞれの機能がどのような働きをするのか、実践的な例を用います。例を参考に、Laravel Eloquentの使い方について理解を深めてみてください。

1. Eloquentのクエリスコープ

アプリケーションを構築していると、条件を複数回使用する場面に遭遇することがあります。その都度コードを書き直すと、エラーの可能性が高くなり、コードが整頓されなくなります。Laravelでは、このような条件をスコープと呼ばれる再利用可能なステートメントでラップすることで、この問題を解決できます。

クエリスコープは、モデルにデータベースロジックを追加し、クエリロジックを再利用するメソッドです。

以下にクエリスコープの例を示します。完成した機能と進行中の機能を追跡する、チームで使用できるソフトウェア開発プラットフォームを作成したいとします。この条件を使って、以下のように進行中の機能だけを取得することができます。

$onGoing = Project::where('ongoing', 1)->get();

この条件は、統計ページなど、他のアプリケーションページでも必要になるかもしれません。クエリスコープを使うことで、上記の条件を再利用することができ、クエリを単純化してコードをすっきりさせることができます。

このクエリスコープの使い方は例えば以下の通りです。

class Features extends Model
{
    public function scopeOngoing($query)
    {
        return $query->where('ongoing', 0);
    }
}

次に、以下のコードを使ってそのスコープを実行します。

$onGoing = Feature::ongoing()->get();

スコープにはグローバルとローカルの2種類があります。

グローバルスコープ

グローバルスコープでは、モデル内のすべてのクエリに制約を追加することができます。例えば、モデル内のすべてのクエリに、チームリーダーの名前に基づいて機能をフィルタリングする条件を追加することができます。

ローカルスコープ

ローカルスコープでは、再利用性を考えて共通の制約を定義することができます。例えば、バグがある機能をアプリケーションに返させたいとします。コードでは、例えば次のようなローカルスコープを実装することができます。

namespace AppModels;
use IlluminateDatabaseEloquentModel;

class User extends Model
{
    public function scopeBugged($query)
    {
        return $query->where('bugged', '>', 1);
    }
}

上のコードは、未解決のバグのあるすべての機能を返します。

2. Eloquentのリレーション

Eloquentのリレーションを使うと、複数のテーブルを簡単に関連付けることができます。例えば、ECサイトの商品には在庫、価格、閲覧数、レビューが記載されていることがあります。Eloquentを使用すると、レコードが異なるテーブルにある場合でも、それぞれのリレーションを簡単に管理できます。

Eloquentモデルと同じように、リレーションをモデルクラスのメソッドとして定義できます。Eloquentでよく使われるリレーションには、1対1、1対多、多相などがあります。

1対1

商品モデルと在庫を関連付ける基本的な1対1のリレーションの例を以下に示します。

public function Inventory()
{
    return $this->hasOne('AppInventory');
}

上のコードでは、Inventory()メソッドが商品モデルのhasOne()メソッドを呼び出しています。これは、商品が購入可能かどうかをチェックするためのものです。

1対多

Eloquentでは、1対多のリレーションシップを定義することもできます。例えば、閲覧数に基づいて商品を取得したい場合です。1対多のリレーションを使用すると、ウェブサイトの訪問者が最も興味を寄せている商品を取得できます。hasOne()の1対多のリレーションであるbelongsTo()メソッドを使用することが可能です。

public function product()
{
    return $this->belongsTo('AppProduct');
}

上記のコードでは、Eloquentによりproduct_idが渡されたビュー数に紐づけられます。product_idは、その後、価格や在庫などの他のパラメータを取得するのに有用です。

ポリモーフィック

アプリケーション開発において、リレーションは必ずしも単純ではありません。複数のモデルタイプに属するモデルもあるかもしれません。例えば、商品モデルと在庫モデルはどちらも画像モデルに対して多相的な関係を持つことができます。

ポリモーフィックなリレーションによって、在庫と商品の両方に同じ画像リストを使うことができます。以下はポリモーフィック(多相)リレーションを実装したコードの例です。

class Image extends Model
{
    /**
     * Getting the shared image.
     */
    public function myimage()
    {
        return $this->morphTo();
    }
}

class Product extends Model
{
    /**
     * Get the image to use on the product's page.
     */
    public function image()
    {
        return $this->morphOne(Image::class, 'myimage');
    }
}

class Inventory extends Model
{
    /**
     * Get the image to use on the inventory page.
     */
    public function image()
    {
        return $this->morphOne(Image::class, 'myimage');
    }
}

上のコードでは、morphTo()メソッドを使ってポリモーフィックモデルの親を取得しています。

これは、あくまでもこのトピックに関する氷山の一角に過ぎません。詳しくは、Laravel Eloquentリレーションについての詳しい解説をご参照ください。

3. Eloquentのミューテタとアクセサ

ミューテタとアクセサを使用すると、データを保存したり取得したりする際にデータを変更できます。ミューテタは保存前にデータを変更し、アクセサは取得に際してデータを変更します。

例えば、データベースに名前を小文字で保存したい場合には、その変換を実行するミューテタを作成します。アプリのページでユーザーの姓と名を1つの名前として表示したい場合には、そのためのアクセサを作成することができます。

以下は、名前を保存する前に大文字にするミューテタの例です。

class User extends Model
{
    /**
     * 姓と名を大文字にするミューテタ
     */
    public function setFirstNameAttribute($value)
    {
        $this->attributes['first_name'] = ucfirst($value);
    }

    public function setLastNameAttribute($value)
    {
        $this->attributes['last_name'] = ucfirst($value);
    }
}

以下は、ユーザーの姓と名を結合するアクセサの例です。

class User extends Model
{
    /**
     * 姓と名を結合するアクセサ
     */
    public function getFullNameAttribute()
    {
        return ucfirst($this->first_name) . ' ' . ucfirst($this->last_name);
    }
}

4. Eloquentのコレクション

Eloquentコレクションは、複数のモデルの結果を返すメソッドを扱います。このクラスはIlluminateDatabaseEloquentCollectionにあります。

配列と同様に、コレクションを繰り返し処理することができます。以下は単純な反復です。

use AppModelsProduct;

$products = Product::where('availability', 1)->get();

foreach ($products as $product) {
   echo $product->name;
}

コレクションは配列よりも強力で、より複雑な処理を行うことができます。例えば、利用可能なすべての商品の一覧を表示し、「active」でないものはすべてスキップすることができます。

$names = Product::all()->reject(function ($product) {
   return $product->active === false;
})->map(function ($product) {
   return $product->name;
});

以下は、コレクションクラスにある複数メソッドの一部です。

contains

contains()メソッドは、以下のコードのように、指定したモードが含まれているかどうかをチェックします。

$products->contains(1);
$products->contains(Product::find(1));

All

all()メソッドは、コレクションに含まれるモデルを返します。

$collection = Product::all();

コレクションクラスでは、他にも多くのメソッドがサポートされています。

5. Eloquentのモデルの削除

Eloquentでは、クエリの構築に役立つモデルを作成できます。しかし、時にはアプリケーションを効率的にするためにモデルの削除が必要になります。これを行うには、モデルのインスタンスでdeleteを呼び出すことができます。

use AppModelsStock;

$stock = Stock::find(1);
$stock->delete();

上記のコードにより、アプリケーションからモデルStockが削除されます。これは永久的な削除で、元に戻すことはできません。

ソフトデリート(論理削除)

Eloquentのもう一つの機能はモデルのソフトデリートです。モデルをソフトデリート(論理削除)しても、データベースから削除されるわけではありません。

deleted_atを使うことで、ソフトデリートの日時を示すフラグを立てます。これは、データベースレコードの一部を永久に削除することなく、不完全なものなどを除外したい場合に便利な機能です。余分な条件を追加することなく、Eloquentのクエリ結果をクリーンアップすることができます。

ソフトデリートを有効にするには、softDeletesをモデルに追加し、関連するデータベーステーブルにdeleted_atの列を追加します。

モデルにソフトデリートを追加する

以下のように、IlluminateDatabaseEloquentSoftDeletesを追加することで、モデルのソフトデリートを有効にできます。

namespace AppModels;
use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentSoftDeletes;

class Flight extends Model
{
   use SoftDeletes;
}

delete_at カラムを追加する方法

ソフトデリートを使い始める前に、データベースにdelete_atカラムが必要になります。このカラムはLaravelのSchemabビルダヘルパメソッドを使用して追加します。

use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;

Schema::table('users', function (Blueprint $table) {
   $table->softDeletes();
});

Schema::table('users', function (Blueprint $table) {
   $table->dropSoftDeletes();
});

これにより、ソフトデリートが成功した場合に日付と時刻の情報が更新されるdelete_atカラムが追加されます。

ソフトデリートしたモデルを対象とする方法

クエリの結果にソフトデリートしたモデルを含めるには、withTrashed()メソッドをクエリに追加します。以下に例を示します。

$stocks = Stock::withTrashed()->where('stock_id', 20)->get();

上記のクエリには、deleted_at属性を持つモデルも含まれます。

ソフトデリートしたモデルのみを取得する方法

Eloquentでは、ソフトデリートしたモデルのみを取得することもできます。これを行うには、例えばonlyTrashed()メソッドを呼び出します。

$Stock = Stock::onlyTrashed()->where('stock_id', 1)->get();

ソフトデリートしたモデルを復元する方法

restore()メソッドを呼び出すことで、ソフトデリートしたモデルを復元することもできます。

$stocks = Stock::withTrashed()->where('stock_id', 20)->restore();

これによりソフトデリートしたモデルのdelete_atフィールドがnullに変わります。モデルがソフトデリートされていない場合には、フィールドは変更されません。

6. Eloquentのファクトリ

Laravelのモデルファクトリは、アプリケーションのテストやデータベースのシードに使用するダミーデータを作成するのに有用です。これを実装するには、以下の例に示すように、ファクトリクラスでモデルを作成します。このコードにより、ダミーのサプライヤー名と価格を生成するモデルファクトリを作成できます。

namespace DatabaseFactories;
use IlluminateDatabaseEloquentFactoriesFactory;
use IlluminateSupportStr;

class StockFactory extends Factory
{
    public function definition()
    {
        return [
            'supplier_name' => fake()->name(),
            'price' => fake()->numberBetween($min = 1500, $max = 6000),
        ];
    }
}

上の例のdefinition()メソッドは、モデル構築時にLaravelが使用する属性値を返します。fakeヘルパは、ファクトリがPHPのライブラリであるFakerにアクセスするのを助ける役割を果たします。

まとめ

Eloquentは、Laravelでのアプリケーション開発作業をシンプルにする代物です。リレーションのような実装を活用することで、単純なクエリでも複雑なクエリでも、同じように効果的に構築できます。ファクトリを使用してすぐにダミーデータを生成できるお手軽さは、アプリケーションのテストを効率的に実施したい開発者に欠かせない選択肢です。さらに、Eloquentスコープは複雑なクエリをシンプルにし、コードを整頓するのに効果を発揮します。

この記事では主要な機能のうち6つだけを取り上げましたが、Eloquentには他にも強力な機能がたくさんあります。共有や再利用が捗るモデルの使用により、Eloquentは開発者の間で人気のある機能であり、Eloquentクエリのシンプルさにより、Laravelは初心者でも開発しやすいフレームワークとなっています。

Kinstaのウェブアプリケーションホスティングプラットフォームは利用者の技術レベルに関係なく、開発作業の効率化を支援します。KinstaのLaravelテンプレート(すぐに使える活用例) もあわせてご確認ください。Google Cloudのプレミアムティアネットワークを使いながら、アプリケーションを素早く簡単に立ち上げることができます。

Steve Bonisteel Kinsta

Kinstaのテクニカルエディター。救急車や消防車を追いかける記者としてキャリアをスタート。1990年代後半からインターネット関連の技術情報を担当している。