スコープとは
Laravelに組み込まれている検索機能。
DBのレコードを絞り込む時に、設定条件を予め決めることができます。
つまり、where句による絞り込みが設定できます。
そのため、
・絞り込み忘れで意図しないデータが出てくる
ようなことを避けることができるのがメリットになると思います。
スコープ機能は、ローカルスコープと、グローバルスコープの2種類があります。
ローカルスコープとは
「ローカル」という名前からも想像されるかもしれませんが、必要な時だけ使えるメソッドになります。毎回使わなくてOK、hogeのケースだけ使いたい!ならこちらとなります。
使い方を見てみましょう。
例えば、Userのモデルに投票(vote)カラムが100以上の場合の方を絞り込みたいとします。
お作法としては、scopeHogeのように、scopeをつけて大文字にします。
第1引数は$queryを、第2引数はお好みの引数を入れることができます。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 人気のあるユーザーのみを含むようにクエリのスコープを設定
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
}
呼び出すときは、このように記述します。
use App\Models\User;
$users = User::popular()->orderBy('created_at')->get();
こちらもお作法として、scopeは書かず、小文字のメソッドで書きますので注意してください。
これで、whereを書かずにすみます。
また、メソッドチェーン(->)で設定した関数をつなぐことができます。
グローバルスコープとは
対して、グローバルスコープは、特定のモデルのすべて(※)のクエリに絞り込みができます。
※後述しますが、「ここだけは効かせなくない」場合に絞り込みを外すこともできます。融通ききますね…!
スコープクラスを作成する必要がありますが、Laravelはどこで作成してもOKだそうです。
例として、作成日時が20世紀のデータを抽出するようにしてみます。
ファイルはどの場所でもいいのですが、Appディレクトリ直下にScopeディレクトリを作るとします。
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class AncientScope implements Scope
{
/**
* 指定のEloquentクエリビルダにスコープを適用
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->where('created_at', '<', now()->subYears(2000));
}
実際に適用させるには
static::addGlobalScope(new AncientScope);
のように記述し、bootedメソッドにオーバーライドする形で新しくaddGlobalScope関数の引数にAncientScopeクラスをセットします。
<?php
namespace App\Models;
use App\Scopes\AncientScope;//作ったクラスを呼び出す!
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* モデルの「起動」メソッド
*
* @return void
*/
protected static function booted()
{
static::addGlobalScope(new AncientScope);
}
// 参考 このようにクロージャを使って書くこともできる!
// protected static function booted()
// {
// static::addGlobalScope('ancient', function (Builder $builder) {
// $builder->where('created_at', '<', now()->subYears(2000));
// });
// }
}
こうすると、User::all()を書くと、実際に狙い通りにwhereで絞り込まれたクエリが実行されます。
もし、この場合だけUser::all()で絞り込まないで欲しい!という場合には
User::withoutGlobalScope(AncientScope::class)->get();
と、withoutGlobalScopeメソッドを書くと、全てのグローバルスコープが適用されなくなりますし、
// グローバルスコープの一部を削除
User::withoutGlobalScopes([
FirstScope::class, SecondScope::class
])->get();
と記述すれば、いらないものだけを指定することもできます。融通効きますね!
まとめ
スコープを活用することで、絞り込みの記述をスッキリでき、抜け漏れを防ぐことにもつなげられます。
リーダブルにはこちらに記述があります。
コメント