クエリスコープを使用するとクエリの使い回しができます。例えばCMSを構築していてほとんどのクエリに対して公開済みの記事を取得する制約を付けている場合、クエリスコープを使用することで自動的にこの制約を追加することができます。
Laravelのクエリスコープには、「グローバルスコープ」と「ローカルスコープ」の2種類あります。グローバルスコープはEloquentモデルを使用すると自動的に条件が追加され、ローカルスコープはあらかじめクエリを作成しておき条件を追加したいときに呼び出すことで使用できます。
事前準備
作成した記事の中からpublished = 1
の記事を取得する条件をグローバルスコープ・ローカルスコープを用いて使用してみます。その準備としてarticles
テーブルにpublished
カラムを追加しpublished = 1
のレコードを作成しておきます。
id | use_id | title | slug | body | published | created_at | updated_at |
---|---|---|---|---|---|---|---|
1 | 1 | My first article | First article | My first article is edited | 0 | 2021-05-01 00:48:03 | 2021-05-01 04:02:55 |
2 | 2 | Jack's article | Jack's article | This article is made by Jack | 1 | 2021-05-01 00:48:43 | 2021-05-01 |
ローカルスコープ
published = 1
の条件をローカルスコープを使用して追加してみます。
ローカルスコープ定義
モデルにスコープを定義します。命名規約はメソッド名にscope
を付けることです。
public function scopePublished($query)
{
return $query->where('published', 1);
}
ローカルスコープの利用
定義したスコープ名からscope
プレフィックスを取り除いたメソッド名で呼び出せます。
public function index()
{
$articles = Article::published()->with('tags')->get();
return view('articles.index', ['articles' => $articles]);
}
発行されたsqlにはきちんと"published" = 1
が追加されています。
select * from "articles" where "published" = 1
グローバルスコープ
今度はグローバルスコープを用いて条件を追加してみます。
グローバルスコープの作成
Illuminate\Database\Eloquent\Scope
インターフェースのapply
メソッドを実装するクラスを作ります。
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class PublishedScope 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('published', '=', 1);
}
}
グローバルスコープの適用
モデルのbooted
メソッドをオーバーライドしてaddGlobalScope
メソッドを呼び出します。
protected static function booted()
{
static::addGlobalScope(new PublishedScope);
}
ローカルスコープと違い特に何も指定しなくても、Article
Eloquent モデルを使用したクエリにはpublished = 1
が追加されます。
public function index()
{
$articles = Article::with('tags')->get();
return view('articles.index', ['articles' => $articles]);
}
グローバルスコープの削除
あるケースではグローバルスコープのクエリを使用したくないということがあると思います。その場合はwithoutGlobalScope
メソッドを使用することでグローバルスコープを削除できます。
use App\Scopes\PublishedScope;
public function index()
{
$articles = Article::withoutGlobalScope(PublishedScope::class)->with('tags')->get();
return view('articles.index', ['articles' => $articles]);
}