概要
LaravelだとGlobalScopeを作成して、scopeメソッドをModelクラスから分離させることができる。
しかし、Eloquentに依存してしまうため、DBファサード等を使用してQueryBuilderのみを使用する場合に、クエリの分割ができない。
そのため、Illuminate\Database\Query\Builder
クラスを継承したQueryBuilderクラスを作成し、EloquentからでもQueryBuilderのみでも利用できるようにする。
実装
QueryBuilderクラスの作成
Illuminate\Database\Query\Builder
したクラスを作成する。
<?php
namespace App\Models\Builder;
use Illuminate\Database\Query\Builder;
class UserBuilder extends Builder
{
public $from = 'users';
public function verified()
{
return $this->whereNotNull('email_verified_at');
}
}
EloquentModel修正
EloquentModelで作成したQueryBuilderクラスを呼び出せるようにする。
Illuminate\Database\Eloquent
クラスのnewBaseQueryBuilder
メソッドをオーバーライドし、先程作成したQueryBuilderクラスを返却するように変更する。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Builder\UserBuilder;
class User extends Model
{
/**
* @return \App\Models\Builder\UserBuilder
*/
public function newBaseQueryBuilder()
{
return new UserBuilder($this->getConnection());
}
Builderクラスにマクロを定義
Illuminate\Database\Query\Builder
クラスに、queryメソッドをマクロとして定義する。
queryメソッドでは、コネクションを引数に自身のインスタンス生成し、返却するように修正する。
<?php
namespace App\Providers;
use Illuminate\Database\Query\Builder;
use DB;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Builder::macro('query', function () {
return new self(DB::connection());
});
}
使用例
例1. EloquentModelで使用する場合
scopeメソッドのように利用するだけでOK!
<?php
namespace App\Http\Controllers;
use App\Models\User;
class UserController extends Controller
{
public function index()
{
$users = User::verified()->get();
}
}
例2. QueryBuilderのみで使用する場合
queryメソッドを呼んだあとに、チェーンして呼べばOK!
<?php
namespace App\Http\Controllers;
use App\Models\Builder\UserBuilder;
class UserController extends Controller
{
public function index()
{
$users = UserBuilder::query()->verified()->get();
}
}
まとめ
クエリ生成に関する部分をクラスとして独立させたことで、EloquentでもQueryBuilderでも使用できるようにしました。