🤫

LaravelのDBをデータ操作が楽にできる「スコープ」とは!

2021/03/29に公開

今回は、Laravelの「スコープ」という機能について解説していきます。

スコープという機能を知れば、Eloquentでのデータ操作の部分で、可読性や保守性がぐーんと上がります!
どのバージョンから使えるのか定かではないですが、、少なくともLaravel5.1くらいまではドキュメントを見つけることができましたので、
結構前からある機能みたいですね。

では、さっそくいきましょう!

スコープとは?(概要)

スコープを使用するとDBへのデータの追加・取得・更新・削除などの際のクエリに制約を追加できます。

特定のモデルの全てのクエリに制約を追加する「グローバルスコープ」と、特定のクエリに制約を追加する「ローカルスコープ」の2種類があります。

どんなときにスコープを使うのか

例えば、

  • 企業の全社員を取得する機能
  • 企業の2020年以降入社の社員を取得

この2つの機能を満たすため、Eloquentでデータを取得すると、このように where('company_id', $companyId) をそれぞれのメソッドで書かないといけなくなります。

// 企業の全社員を取得
public function getAllEmployee() {
    $companyId = 1;
    $employees = Employee::where('company_id', $companyId)->get();
}

// 企業の2020年以降入社の社員を取得
public function getRecentEmployee() {
    $companyId = 1
    $employees = Employee::where('company_id', $companyId)->where('joined_at', '>', '2020-01-01')->get(); // 企業の2020年以降入社の社員を取得
}

このように同じ制約を何回も書く場合にスコープが役目を果たします!

ローカルスコープの書き方

ローカルスコープでは、メソッド名の先頭にscopeとつけるだけ、Laravelが自動で「このメソッドはスコープ機能を使うために定義したものなんだ!」と認識してくれます。

では、先程の例に出したように where('company_id', $companyId) をローカルスコープに移動させたいと思います。

class Employee extends Model
{
    /**
     * 特定の会社に紐づく社員のクエリのスコープを設定
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeCompany($query, $companyId)
    {
        return $query->where('company_id', $companyId);
    }
}

このようにスコープを定義したら、先程記述したController部分のコードは下記のようになります。

// 企業の全社員を取得
public function getAllEmployee() {
    $companyId = 1
    $employees = Employee::company($companyId)->get();
}

// 企業の2020年以降入社の社員を取得
public function getRecentEmployee() {
    $companyId = 1
    $employees = Employee::company($companyId)->where('joined_at', '>', '2020-01-01')->get();
}

これで、意味のあるスコープ名をつけることで直感的にわかりやすくなったり、開発が楽になったり、何度も使用するクエリを1つにまとめることで保守が楽になったりします。

詳しくは公式のドキュメントを読んでみてくださいね!
(下の方にスコープについて書かれてあります)

Laravel 8.x Eloquentの準備

グローバルスコープは基本的に使わない(個人的意見)

グローバルスコープは処理がブラックボックスになりやすいため、私は基本的には使いません。
(なので、ここでは解説しません。)

ただ、機能として存在する理由がありまして、

実はLaravelの論理削除でこのグローバルスコープが使用されています。

論理削除は(softDelete)は、データを取得する際にわざわざ開発者が「削除されていないデータを取得する」というふうな制約をつけていないですよね。

例えば下記のようにallで全てのユーザーを取得しようとした場合でも、自動で論理削除済みのユーザーは取得されないようになっています。

User::all()

これにグローバルメソッドが使われているのです。
論理削除のように開発者が全く意識しなくても、データを自動でフィルタリングしたり、ソートしたりしたい場合にグローバルメソッドを使うと良いと思います。

Discussion