【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の種類:ローカルスコープの使い方
ローカルスコープとはモデルクラス内にメソッドとして定義し、使用する際にそのメソッドを呼び出すことでデータ範囲を指定する(条件で絞り込む)できるスコープです。
ローカルスコープのポイント
- モデル内のメソッドとして定義
- 適応させるには適宜メソッドを呼び出す
- メソッド名は先頭に「scope〜」
- 引数を渡したい場合は第二引数に指定
- 実際のスコープ条件はwhereメソッドで定義
- スコープメソッド呼出時は「scope〜」を除去
- スコープメソッド呼出時の第一引数がメソッドの第二引数が渡る
まずはモデルクラス内に「指定された数値以上のみのidのレコードを返す」scopeOverId
スコープを作成してみます。スコープメソッドの第一引数の$query
には\Illuminate\Database\Eloquent\Builder
のBuilderインスタンスが自動で格納されます。データベース情報を取得できるインスタンスなのでそのまま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の種類:グローバルスコープの使い方
使用時に適宜呼び出しが必要なローカルスコープとは違いグローバルスコープは一度定義すればモデルから取り出す全てのレコードにデータ範囲(条件で絞り込む)を反映させてくれます。
グローバルスコープのポイント
- モデル内のbootメソッドとして定義
- addGlobalScopeメソッドをオーバーライド
- 設定すればモデルで取り出すレコード全てに反映
- モデル内でuse Illuminate\Database\Eloquent\Builder;で読み込む
- スコープクラスを作成し複数のモデルに反映も可能
- その際はuse App\Scopes\スコープ名;で読み込む;
モデル内にグローバルスコープを定義する
モデル内に定義する際は上部に以下の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
の引数にはインスタンス化したスコープクラスを渡すだけです。
<?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以上のみ」という条件が適応されます。
まだまだ勉強中ですので至らぬ点や間違っているところがございましたらご指摘いただけると嬉しいです。
ご覧いただきありがとうございました!