[Laravel]Eloquentの高度な使い方

Laravel Laravel

遅延読み込み(Lazy Loading)

必要な時のみリレーションの情報を読み込みます。最も簡単な読み込み方です。

$user = User::find(1);

// Lazy load the user's posts when needed
$posts = $user->posts;
PHP

上記を実行すると、以下の2つのクエリが発行されます。

SELECT * FROM `users` WHERE `users`.`id` = 1;
SELECT * FROM `posts` WHERE `posts`.`user_id` = 1;
SQL

N+1問題

上記の例ではusersを1件だけ取得していますが、複数件取得した場合は、postsを取得するために取得したusersをforeachで回してpostsを取得する必要があります。

これは、いわゆるN+1問題というもので、posts取得(N) + users取得(1)件のクエリを発行してしまい、レコード数が多くなればなるほど重くなる問題があります。

これを行わないようにするのが以下のEager Loadingです。

積極的読み込み(Eager Loading)

先にリレーション先の情報を読み込みます。withメソッドを利用します。

$users = User::with(['posts' => function ($query) {
    $query->select('id', 'user_id', 'title');
}])->get();
PHP

上記では、以下のクエリが発行されます。

SELECT * FROM `users`;
SELECT `id`, `user_id`, `title` FROM `posts` WHERE `posts`.`user_id` IN (1, 2);
SQL

INの条件にusersで取得したレコードのIDが全部入るため、レコードが多くなっても上記2クエリのみの発行で済みます。

グローバルスコープ

グローバル・スコープを使用すると、指定されたモデルのすべてのクエリに自動的に適用される制約を定義できます。

// Define a global scope for soft-deleted records
protected static function boot()
{
    parent::boot();

    static::addGlobalScope('softDeleted', function ($builder) {
        $builder->where('deleted', 0);
    });
}
PHP

Selectのサブクエリ

サブクエリを利用できます。生のSQLを書く前にお試しください。

$latestPosts = Post::select('title', 'created_at')
    ->whereIn('id', function ($query) {
        $query->select('post_id')
            ->from('comments')
            ->where('approved', 1);
    })
    ->get();
PHP

ポリモーフィックリレーション

ポリモーフィックな関係によって、1つのモデルが複数の他のタイプのモデルに属することができます。

class Comment extends Model
{
    public function commentable()
    {
        return $this->morphTo();
    }
}
PHP

tapメソッドの利用

tapメソッドを使用すると、コールバック内でクエリビルダのインスタンスをタップできます。

$users = User::where('active', 1)
    ->tap(function ($query) {
        // Perform additional actions on the query builder
        $query->orderBy('name');
    })
    ->get();
PHP

生の式の記述

複雑な条件や計算で生の式を利用する場合。

$users = User::select(DB::raw('COUNT(*) as user_count, status'))
    ->where('status', '<>', 1)
    ->groupBy('status')
    ->get();
PHP

モデルのファクトリ

モデルのテストデータなどを作成する。[Laravel]Factory(ファクトリ)を使ってテストデータを作るで詳しい使い方を紹介しています。

use Illuminate\Database\Eloquent\Factories\Factory;

class UserFactory extends Factory
{
    protected $model = User::class;
    public function definition()
    {
        return [
            'name' => $this->faker->name,
            'email' => $this->faker->unique()->safeEmail,
        ];
    }
}
PHP

クエリスコープ

再利用可能なクエリスコープをモデルに定義することで、クエリを整理することができます。

グローバルスコープに対して、こちらはローカルスコープとも呼ばれます。

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

// Usage
$activeUsers = User::active()->get();
PHP

クエリのキャッシュ

重いクエリなどはキャッシュを利用して、パフォーマンスの改善を図りましょう。

$users = Cache::remember('all_users', 60, function () {
    return User::all();
});
PHP

whenメソッドを利用した動的リレーション

whenメソッドを利用して特定の条件の場合にリレーションを定義できます。

$relation = User::when($isAdmin, function ($query) {
    return $query->hasMany('App\Post');
}, function ($query) {
    return $query->hasMany('App\Comment');
})->get();
PHP

カスタム ピボット

多対多のリレーションシップを扱う場合は、カスタム ピボット モデルを使用して追加のフィールドや動作を追加します。

class RoleUser extends Pivot
{
    // Custom fields or methods
}
PHP

結果の分割

巨大なデータをバッチ処理する際に、分割しながら読み込んで処理ができます。

User::orderBy('id')->chunk(200, function ($users) {
    foreach ($users as $user) {
        // Process each user
    }
});
PHP

条件付きのリレーション

unlessメソッドを使って条件付きで関係を定義できます。

$relation = User::unless($isGuest, function ($query) {
    return $query->hasMany('App\Post');
})->get();
PHP

匿名グローバルスコープ

グローバルスコープに匿名クラスも利用できます。

protected static function boot()
{
    parent::boot();

    static::addGlobalScope(new class implements Scope
    {
        public function apply(Builder $builder, Model $model)
        {
            $builder->where('active', 1);
        }
    });
}
PHP

キーバリューにpluckを利用する

pluckメソッドを利用して効率的にキーバリューを作成できます。

$userRoles = User::pluck('role', 'id');
PHP

JSONカラムのクエリ

JSON型のカラムを利用して柔軟なデータを表現できます。

$users = User::where('meta->status', 'active')->get();
PHP

Eloquentのリソースコレクション

EloquentモデルをAPIリソースに変換し、クリーンで一貫性のあるデータ出力を実現します。

class UserCollection extends ResourceCollection
{
    public function toArray($request)
    {
        return [
            'data' => $this->collection,
            'meta' => ['key' => 'value'],
        ];
    }
}
PHP

リレーション先のカウント

関連するモデルのcountを取得できます。

$userCount = User::withCount('posts')->find(1);
echo $userCount->posts_count;
PHP

ピボットテーブルの名前を指定

多対多のリレーションシップにおけるピボット・テーブルのカスタムテーブル名を指定します。

class User extends Model
{
    public function roles()
    {
        return $this->belongsToMany('App\Role', 'user_roles');
    }
}
PHP

最後に

紹介したこれらの高度な使い方をマスターすると、複雑な要件でもデータベースとのやり取りで最適なパフォーマンスを発揮できるようになります。是非マスターして素晴らしいアプリケーションを作成しましょう。

コメント

タイトルとURLをコピーしました