【Laravel】Eloquent(エロクアント)のscopeとは?ローカルとグローバルの違い

この記事からわかること

  • Laravelデータベース操作機能
  • Eloquent(エロクアント)scopeとは?
  • 使い方設定方法メリット
  • ローカルスコープとグローバルスコープの違い

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

データベース操作をよりphpらしく、より自由に行えるLaravel独自の機能「Eloquent(エロクアント)」。

今回はEloquent(エロクアント)のscopeと呼ばれる機能の使い方をまとめていきたいと思います。

参照記事:公式:エロクアント〜スコープ〜

scopeとは?

Eloquent(エロクアント)のscopeを理解する前にEloquent(エロクアント)がphpのクラス(モデルクラスと呼ぶ)としてデータベースを扱えるようにしていることへの理解が必要です。

データベース情報を操作する際はモデルクラスのインスタンスとして使用します。インスタンスなので元クラスを独自に拡張して定義したメソッドやプロパティも扱えるのがEloquent(エロクアント)のメリットでもあります。

そのモデルクラスにscope(スコープ)と呼ばれるメソッドを定義できます。scope(スコープ)とは言葉通り「範囲」という意味でデータベースから取得するデータ範囲を指定することができます。具体的には「idが3以上のデータのみ」や「priceが2000円以下のみ」など条件をつけることでマッチしたデータのみを抽出できるwhere句がモデルクラス単位で指定できるイメージです。

Eloquent(エロクアント)で定義できるスコープは2種類に分かれています。

scopeの種類:ローカルスコープの使い方

ローカルスコープとはモデルクラス内にメソッドとして定義し、使用する際にそのメソッドを呼び出すことでデータ範囲を指定する(条件で絞り込む)できるスコープです。

ローカルスコープのポイント

まずはモデルクラス内に「指定された数値以上のみのidのレコードを返す」scopeOverIdスコープを作成してみます。スコープメソッドの第一引数の$queryには\Illuminate\Database\Eloquent\BuilderBuilderインスタンスが自動で格納されます。データベース情報を取得できるインスタンスなのでそのままwhereメソッドを繋いで条件を定義します。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use HasFactory;

    public function scopeOverId($query,$num)
    {
        return $query->where('id',$num);
    } 
}

引数を追加したい場合は第二引数に$numのように指定します。

コントローラ側では適応させたい時のみメソッドを呼び出して使用します。その際は「scope〜の除去」/「先頭文字は小文字に」/「呼出時の第一引数がメソッドの第二引数に渡る」点に注意しながら呼び出します。

コントローラ側

class appController extends Controller
{
    public function index(Request $request)
    {
        // メソッド名は「scope〜」除く
        // 先頭文字は小文字に Over→over
        // 呼出時の第一引数がメソッドの第二引数に渡る 1→ $numへ
        $records = User::overId(5);
        return view('index', ['users' => $records]);
    }
}

bladeテンプレート側

<body>
  <h1>Idが5以上のユーザー名一覧</h1>
  @foreach ($users as $user)
    <p>{{$user->name}}</p>
  @endforeach
</body>

ローカルスコープをあらかじめ複数定義しておけばメソッドチェーンで条件による絞り込みを重ねることができます。

// 「idが5以上」かつ「名前の長さが4以上」みたいな
$records = User::overId(5)->nameLengthOver(4);

scopeの種類:グローバルスコープの使い方

使用時に適宜呼び出しが必要なローカルスコープとは違いグローバルスコープは一度定義すればモデルから取り出す全てのレコードにデータ範囲(条件で絞り込む)を反映させてくれます。

グローバルスコープのポイント

モデル内にグローバルスコープを定義する

モデル内に定義する際は上部に以下のuse文を組み込みます。

use Illuminate\Database\Eloquent\Builder;

スコープ処理はbootメソッド(ブートストラップ:自動実行処理)に記述します。bootメソッドとはphpクラスの__constructに似たモデル作成時に初期化処理などを記述するためのメソッドです。

<?php

namespace App\Models;

// 追加
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class User extends Model{
    use HasFactory;

    protected static function booted(){

        static::addGlobalScope('overId',function(Builder $builder){
            $builder->where('id',">","4");
        });
    }
}

継承(extends)しているModel(スーパークラス)のbootメソッドを潰さないようにするために上記のようにbootedメソッドとして定義もしくは下記のようにparent::boot();でオーバーライドします。

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

実際のスコープ条件はローカルと同じくwhereメソッドを使用し、モデル内にデフォルトで定義されている静的メソッドのaddGlobalScopeに上書きして定義します。

static::addGlobalScope('スコープ名',function(Builder $builder){
    $builder->where(条件);
});

これでモデル単位でのグローバルスコープの設定は完了です。このモデルを介してレコードを取得する際は自動でoverIdスコープが適応されます。

スコープクラスとしてグローバルスコープを定義する

モデル単位でグローバルスコープを定義しましたが複数のモデルで同じスコープを適応させたい場合はスコープクラスを作成します。作成場所に指定はありませんが公式からは「app」>「Scopes」というフォルダを作成しその中に管理するのがオススメされています。今回は「OverIdScope.php」を作成してみます。

<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class OverIdScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        $builder->where('id', '>', 5);
    }
}

スコープクラスの中にはapplyという名前でメソッドを定義するのがルールです。その中にwhereメソッドで条件を記述します。

これでグローバルスコープの作成は完了です。あとは組み込みたいモデルクラスに適応されるように修正します。

修正はモデルクラスの中にグローバルスコープを読み込み、bootメソッドの中addGlobalScopeの引数にはインスタンス化したスコープクラスを渡すだけです。

&lt;?php

namespace App\Models;

// 追加
use App\ Scopes\OverIdScope;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class User extends Model{
    use HasFactory;

    protected static function booted(){

        static::addGlobalScope(new OverId Scope);
    }
}

グローバルスコープをスコープクラスを使っての適応がこれでできるようになりました。今回はこのモデルを介するレコード取得処理全てに「idか5以上のみ」という条件が適応されます。

まだまだ勉強中ですので至らぬ点や間違っているところがございましたらご指摘いただけると嬉しいです。

ご覧いただきありがとうございました!

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index