Larevelでデバッグしている時にDBからのデータ取得でactive()やpopular()という関数が記述してあるのに、調べてもLaravel公式のメソッドやヘルパーとしては定義されていない。
アプリケーションのファイル全てで active() や function active() を検索しても、そのような名前の関数はどこにも定義されていない、、そんな時の対処法です。
対処法
最初に結論から言うと、その関数は scopeActiveやscopePopularというように、前置詞にscopeをつけた状態で定義されています。
基本的には同じファイルに定義されています。
例えば、active()という関数が使われている場合は、以下のような関数が定義されています。
public function scopeActive($query) {
return $query->where('active', 1);
}
この関数の中身は、Eloquentモデルのwhere句を使って、activeカラムで値が1のデータのみを絞り込む処理です。
あれこの関数どこにも定義されてないけど??と思ったら、scopeをつけて検索してみると見つかるかもしれません。
ローカルスコープ
関数名の前にscopeをつけて定義した関数をローカルスコープと言います。
Laravelに用意されている便利な機能の一つで、何度も使う関数を定義することができます。
ローカルスコープの使い方
ローカルスコープの使い方はとても簡単です。以下の2つの手順を踏むだけです。
- モデルファイルの中に「scope + 関数名」 の関数を定義する。(関数名は冒頭大文字)
- 呼び出す。(scopeなし、小文字)
▼(1)関数の定義
public function scope関数名($querry) {
return $query->処理;
}
▼(2)呼び出し
モデル::関数名()
実例:popular
モデルファイルの中に「scope + 関数名」 の関数を定義する
例えば、voteというカラムで値が100以上の行のみを抜き出すpopularという関数を定義すると次のようになります。
public function scopePopular($query)
{
return $query->where('votes', '>=', 100);
}
呼び出す
呼び出す時はscopeをつけずに、popular()のみで呼び出します。
App\User::popular()->get();
これは次の処理と同じです。
App\User::where('votes', '>=', 100)->get();
複数数の関数をつなげる
ローカルスコープで定義した関数は他のメソッドと同様にメソッドチェーンでつなぐことができます。
$users = App\User::popular()->active()->orderBy('created_at')->get();
動的なローカルスコープ
任意の引数を渡して動的にレコードの絞り込みをすることもできます。
第1引数には前の処理で取得したデータが入るので、第2引数以降で渡したい引数を定義するだけです。
public function scopeOfType($query, $type)
{
return $query->where('type', $type);
}
上記の処理は、typeという名前のカラムから、変数$typeで渡した値と一致するデータを持つ行のみを抽出する処理になります。
グローバルスコープ
ローカルスコープの他にグローバルスコープという機能も用意されています。
ローカルスコープは使いたい時に関数として呼び出すのに対し、グローバルスコープは対象のモデルからデータを取得する時に必ず実行する処理になります。
ユーザーログインを必要とする場合など、必ず対象のユーザーidでデータを絞り込む場合などに使えます。
グローバルスコープの使い方
グローバルスコープの使い方は簡単でmodelファイルにに決まった書き方で記述するのみです。
指定してする必要があるのは2点のみです。
- グローバルスコープ名(addGlobalScopeの第1引数)
- 実行したい処理
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class モデル名 extends Model
{
protected static function boot()
{
parent::boot();
static::addGlobalScope('グローバルスコープ名', function (Builder $builder) {
$builder->実行したい処理;
});
}
}
グローバルスコープの実例
例えば、Userモデルでageカラムの値が20以上のユーザーのみを必ず抜き出す場合は以下のようになります。
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class User extends Model
{
protected static function boot()
{
parent::boot();
static::addGlobalScope('age', function (Builder $builder) {
$builder->where('age', '>', 200);
});
}
}
これで、Uesrモデル経由でデータを取得する際にwhere('age', '>', 200)
が必ず実行されるようになります。
グローバルスコープを部分的に外す
特定の処理でグローバルスコープを外すことも簡単にできます。
モデル名::withoutGlobalScope('グローバルスコープ名')->get();
例
グローバルスコープの実例で示したageというグローバルスコープを外したい場合は次のようにします。
モデル名::withoutGlobalScope('age')->get();
これでグローバルスコープで指定したwhere('age', '>', 200)
が実行されなくなります。