この記事はLaravelのスコープについての記事です。
スコープを使うことで効率的にモデルに制約を設けることができるようになったり無駄なwhere句を使わないで済むようになります。
スコープには大きく、グローバルスコープとローカルスコープがあります。
その2つの違いや基本的な使い方を中心に紹介します。
使い方を知っておくと便利なのでぜひ参考にしてみてください。
この記事を読むメリット
- グローバルスコープ、ローカルスコープの違いを理解できる
- それぞれのスコープの使い方を理解でき、モデル取得を効率的にできるようになる
グローバルスコープ
まずはグローバルスコープについてです。
グローバルスコープとは、特定のモデルすべてにデフォルトで適用されるクエリ制約です。
1番分かりやすいグローバルスコープの例は、ソフトデリート機能です。
softDeletesを使っているモデルではデータ取得の際に、「deleted_at」がnullのもの、つまり削除されていないデータを取得するようになっています。
ソフトデリートについてはこちら。
https://readouble.com/laravel/8.x/ja/eloquent.html#soft-deleting
このようなモデルの共通のクエリ制約を独自に設定できるのがグローバルスコープです。
Userモデルクラスでグローバルスコープを適用させてみます。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder; // グローバルスコープ適用のために追加
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* グローバルスコープ
* @return void
*/
protected static function booted()
{
static::addGlobalScope('recent', function (Builder $builder) {
$builder->where('created_at', '>', '2000-01-01 00:00:00');
});
}
}
これでUserモデル取得時には必ず「where(‘created_at’, ‘>’, ‘2000-01-01 00:00:00’);」が適用されます。
addGlobaScopeメソッドの第一引数には自由に設定できます。
User::all()で取得すると以下のSQLクエリが実行されるようになります。
select * from `users` where `created_at` > 2000-01-01 00:00:00
all()などのデータ取得についてはこちらの記事を参考にしてみてください。
Laravelのデータ取得「find()」「first()」「get()」「all()」の違いを整理
グローバルスコープはモデルにデフォルトで適用される制約なので本当に適用させるべきか注意が必要です。
すでにモデル取得しているコードにも影響を与えるので、影響範囲などを確認する必要がありそうです。
グローバルスコープの削除
すでに存在しているグローバルスコープを適用せずモデルを取得したい場合は、以下の方法でグローバルスコープを削除できます。
User::withoutGlobalScope('recent')->get();
withoutGlobalScopeメソッドの引数にグローバルスコープで設定したスコープ名を入れます。
先ほど「 ‘recent’」で設定したのでそれを指定するとグローバルスコープが適用されないUserモデルが取得できます。
ここまでグローバルスコープを紹介しましたが、ちなみに私はsoftDeletes以外でグローバルスコープはまだ使ったことがありません。
この後に紹介するロースコープの方が使い勝手も良いと思います。
エンジニアにおすすめ書籍
エンジニアになりたて、これから勉強を深めていきたいという方におすすめの書籍はこちら!
ローカルスコープ
続いてはローカルスコープについてです。
ローカルスコープは先ほどのグローバルスコープとは異なり、デフォルトでの制約はなく必要な時に呼び出せるクエリ制約です。
また例を使って説明します。
ローカルスコープの設定
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 2000年以降のユーザーのみを取得するクエリのスコープ
*/
public function scopeRecent($query)
{
return $query->where('created_at', '>', '2000-01-01 00:00:00');
}
/**
* アクティブユーザーのみ取得するスコープ
*/
public function scopeActive($query)
{
$query->where('is_active', 1);
}
}
2つのスコープを設定してみました。
まず決まりとして、メソッドの最初に「scope」をつけます。
そのあとは自由にメソッド名を設定できます。
ローカルスコープを利用する
続いてローカルスコープを利用します。
use App\Models\User;
$users = User::()->recent()->get();
利用する際には「scope」は削除します。
これでグローバルスコープの際に取得したモデルと同じものが取得できます。
「scope」を忘れたり、利用する際につけているとうまく動作しないので注意が必要です。
use App\Models\User;
$users = User::()->recent()->active()->get();
ローカルスコープを連続して使用することも可能です。
「最近のユーザーかつ、アクティブなユーザーの取得」になります。
「または」で使いたい場合は以下のようにします。
use App\Models\User;
$users = User::()->recent()->orWhere->active()->get();
「orWhere」メソッドを間に入れることでorの条件で取得することができます。
スコープを使いこなしてコード量を減らして可読性をあげよう
いかがだったでしょうか?
私はスコープの存在を知らなかった時、「このメソッド存在しないんだけど!」と焦った記憶があります(笑)
スコープ自体の理解や、ローカルスコープでは「scope」を省略するということを知らないとこういった沼にハマりかねません。
しっかり理解しておくと困らないと思います。
また、スコープを使うと、繰り返し使うwhere句を減らすことができコード量を減らすかつ可読性を上げることができます。
ぜひ参考にしてみてください。