LoginSignup
10
9

More than 3 years have passed since last update.

Laravelのクエリスコープを試してみた

Posted at

クエリスコープを使用するとクエリの使い回しができます。例えば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を付けることです。

Article.php
public function scopePublished($query)
{
    return $query->where('published', 1);
}

ローカルスコープの利用

定義したスコープ名からscopeプレフィックスを取り除いたメソッド名で呼び出せます。

ArticlesController.php
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メソッドを実装するクラスを作ります。

PublishedScope.php
<?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メソッドを呼び出します。

Article.php
protected static function booted()
{
    static::addGlobalScope(new PublishedScope);
}

ローカルスコープと違い特に何も指定しなくても、Article Eloquent モデルを使用したクエリにはpublished = 1が追加されます。

ArticlesController.php
public function index()
{
    $articles = Article::with('tags')->get();
    return view('articles.index', ['articles' => $articles]);
}

グローバルスコープの削除

あるケースではグローバルスコープのクエリを使用したくないということがあると思います。その場合はwithoutGlobalScopeメソッドを使用することでグローバルスコープを削除できます。

ArticlesController.php
use App\Scopes\PublishedScope;

public function index()
{
    $articles = Article::withoutGlobalScope(PublishedScope::class)->with('tags')->get();
    return view('articles.index', ['articles' => $articles]);
}
10
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
9