もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

laravelでcriteria的なのを使いたい

はじめに

風邪引いてダウン中です。

kojirockです。

今回はめちゃめちゃ簡単なものです。

よくある検索機能で検索パラメータが複数あるようなのがあると思います。

今回、別ページでもその検索パラメータが使えるような要件がありました。

それぞれのAPIで検索パラメータによってwhere句をつけるif文の塊をコピペしてたのですが、criteria的な感じで共通化できないか?というお話をチームメンバーの方に相談されて、調べてやってみました。

結果、実態はscopeですが、なんとなく出来たので、忘れないようにブログにします。

やってみた

こちらのレポジトリを参考にさせていただきました。レポジトリには他にもAllOfCriteriaCollection.phpなどもあったのですが、もっと薄くてよかったので、Criteriaとして使えるものだけピンポイントだけ使わせてもらいました。

CriteriaInterface.php

<?php


namespace App\Criterias;

use Illuminate\Database\Eloquent\Builder;

interface CriteriaInterface
{
    /**
     * @param Builder $query
     */
    public function append(Builder $query);
}

CriteriaTrait.php

<?php

namespace App\Criterias;

use App\Eloquents\Builder\CustomBuilder;
use Illuminate\Database\Eloquent\Builder;

/**
 * Trait CriteriaTrait
 * @package App\Criterias
 * @method CustomBuilder query()
 * @method CustomBuilder newQuery()
 * @method CustomBuilder newModelQuery()
 */
trait CriteriaTrait
{
    /**
     * @param Builder $query
     * @param CriteriaInterface $criteria
     * @return Builder
     */
    public function scopeCriteria(Builder $query, CriteriaInterface $criteria): Builder
    {
        $criteria->append($query);
        return $query;
    }

    /**
     * Create a new Eloquent query builder for the model.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder|static
     */
    public function newEloquentBuilder($query): Builder
    {
        return new CustomBuilder($query);
    }
}

SearchUserCriteria.php

<?php


namespace App\Criterias;


use Illuminate\Database\Eloquent\Builder;

class SearchUserCriteria implements CriteriaInterface
{
    /**
     * @var array
     */
    private $parameters;

    /**
     * SearchUserCriteria constructor.
     * @param array $parameters
     */
    public function __construct(array $parameters)
    {
        $this->parameters = $parameters;
    }

    /**
     * @param Builder $query
     */
    public function append(Builder $query)
    {
        // 検索パラメータによってwhere句を追加
    }
}

CustomBuilder.php

<?php

declare(strict_types=1);


namespace App\Eloquents\Builder;

use App\Criterias\CriteriaInterface;
use Illuminate\Container\Container;
use Illuminate\Database\Concerns\BuildsQueries;
use Illuminate\Database\Eloquent\Builder;

/**
 * Class CustomCriteriaBuilder
 * @package App\Eloquents\Builder
 * @method \Illuminate\Database\Query\Builder criteria(CriteriaInterface $criteria)
 */
class CustomBuilder extends Builder
{
}

EloquentUser.php

<?php

declare(strict_types=1);

namespace App\Eloquents;

use App\Criterias\CriteriaTrait;
use Illuminate\Database\Eloquent\Model;

/**
 * Class EloquentUser
 * @package App\Eloquents
 */
class EloquentUser extends Model
{
    use CriteriaTrait;
    protected $table    = 'users';
    protected $fillable = ['email', 'password'];
    
    public function searchUsers(array $parameters): array
    {
        return self::query()
            ->criteria(new SearchUserCriteria($parameters))
            ->paginate(15);
    }
}

終わりに

補完を効かせなくてよければ、CustomBuilderを作る必要はないです。

自分の場合はストレスだったので作りました。

あと、CustomBuilderつくっとけば、たとえば Paginatorの返却クラスも上書きできたりするので、便利でした。

もっと他に良さげなやり方あれば教えて下さい。。。