【Laravel-admin】検索条件のカスタマイズ

Laravelで簡単に管理画面が構築できるライブラリLaravel-adminには大変お世話になっております。

基本的な管理画面であればほんとに爆速で構築できますし、カスタマイズも公式のマニュアル(英語)が充実しているので大抵のことは実現できます。

今回はマニュアルに記載のなかった部分として、検索項目・検索条件で追加したカスタマイズをご紹介

Larave-adminの検索画面

Laravel-adminの管理画面には3つの機能があります。

検索機能が定義されているのは、このうちのgridになります。

機能詳細
grid検索条件や検索結果を定義
★今回のテーマ
showデータの詳細表示
formデータの登録・編集

基本機能を使っただけでも、下記のような検索画面と検索結果が表示できます。

検索画面

この検索項目を実現するためのソースはたったのこれだけです。

        $grid->filter(function($filter){

            $filter->like('sei', '姓');
            $filter->like('mei', '名');
            $filter->like('mail', 'メールアドレス');

            $filter->like('tel', '電話番号');

            $filter->where(function ($query) {
                if($this->input[0] == '1'){
                    $date = Carbon::yesterday();

                    $query->where('created_at','>=',$date);
                }
            }, '新規入会者')->checkbox([
                '1' => '新規入会者'
            ]);

        });

基本の検索条件

簡単な検索条件であれば一行で記述できます。それぞれの検索条件はAND条件になります。

検索条件を一部をご紹介いたします。

イコール

$filter->equal('column', $label);

like検索

$filter->like('column', $label);

between

$filter->between('column', $label)->date();
$filter->between('column', $label)->datetime();
$filter->between('column', $label)->time();

where

$filter->where(function ($query) {
    $query->where('title', 'like', "%{$this->input}%");
}, 'Text');

orWhere

$filter->where(function ($query) {
    $query->where('title', 'like', "%{$this->input}%")
        ->orWhere('content', 'like', "%{$this->input}%");
}, 'Text');

応用編

複雑な条件になってくると、記述方法が難しくなってきます。

SQLでは書けるんだけど、フレームワークでの書き方が分からないというのがこの辺りからかと思います。

orの複合条件

date1とdate2について、「当日~入力日付」の間の日付であるかどうかを検索対象にしています。

$filter->where(function ($query) {
 $today = new Carbon();
 $query->where(function ($query) use($today){
  $query->where('date1','>=',$today->format('y-m-d'))->where('date1', '<=',$this->input);
 })->orWhere(function ($query) use($today){
  $query->where('date2','>=',$today->format('y-m-d'))->where('date2', '<=',$this->input);
 });
}, '日付')->time();

        });

入力内容をフックして検索

例えば、ハッシュ化している「パスワード」を検索したいときなど

入力した値をそのまま使うのではなく変換処理を行ってから検索文字列として利用したい場合に利用します。

$filter->where(function ($query) {
  $query->whereHas('user', function ($query) {
  $hash = hash('sha256',preg_replace("/( | )/", "", $this->input));
  $query->where('password', '=', "{$hash}");
  });
}, '姓');

検索条件のカスタマイズ

Laravel-adminに定義されていない検索条件を追加して利用したい場合は、検索条件そのものを追加します。

サンプルとして、「orBetween」という検索条件を追加します。

orBetween

複数のカラムに対してor検索でBetween条件を利用したい

検索条件については、下記ディレクトリに定義されています。

/vendor/encore/laravel-admin/src/Grid/Filter

今回はBetween機能を拡張してカスタマイズしたいので、これをコピーして少し修正します。

<?php

namespace App\Admin\Filter;

use Encore\Admin\Admin;
use Encore\Admin\Grid\Filter\AbstractFilter;
use Encore\Admin\Grid\Filter\Between;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;

class CustomBetweenOrFilter extends AbstractFilter
{
    /**
     * {@inheritdoc}
     */
    protected $view = 'admin::filter.between';

    /**
     * Format id.
     *
     * @param string $column
     *
     * @return array|string
     */
    public function formatId($column)
    {
        $id = str_replace('.', '_', $column);

        return ['start' => "{$id}_start", 'end' => "{$id}_end"];
    }

    /**
     * Format two field names of this filter.
     *
     * @param string $column
     *
     * @return array
     */
    protected function formatName($column)
    {
        $columns = explode('.', $column);

        if (count($columns) == 1) {
            $name = $columns[0];
        } else {
            $name = array_shift($columns);

            foreach ($columns as $column) {
                $name .= "[$column]";
            }
        }

        return ['start' => "{$name}[start]", 'end' => "{$name}[end]"];
    }

    /**
     * Get condition of this filter.
     *
     * @param array $inputs
     *
     * @return mixed
     */
    public function condition($inputs)
    {
        if ($this->ignore) {
            return;
        }

        if (!Arr::has($inputs, $this->column)) {
            return;
        }

        $this->value = Arr::get($inputs, $this->column);

        $value = array_filter($this->value, function ($val) {
            return $val !== '';
        });

        if (empty($value)) {
            return;
        }


        if (!isset($value['start'])) {
            $this->query = 'orWhere'; //追加
            return $this->buildCondition($this->column, '<=', $value['end']);
        }

        if (!isset($value['end'])) {
            $this->query = 'orWhere'; //追加
            return $this->buildCondition($this->column, '>=', $value['start']);
        }

        $this->query = 'orWhereBetween'; // 右記から修正$this->query = 'whereBetween';

        return $this->buildCondition($this->column, $this->value);
    }

    /**
     * @param array $options
     *
     * @return $this
     */
    public function datetime($options = [])
    {
        $this->view = 'admin::filter.betweenDatetime';

        $this->setupDatetime($options);

        return $this;
    }

    /**
     * @param array $options
     */
    protected function setupDatetime($options = [])
    {
        $options['format'] = Arr::get($options, 'format', 'YYYY-MM-DD HH:mm:ss');
        $options['locale'] = Arr::get($options, 'locale', config('app.locale'));

        $startOptions = json_encode($options);
        $endOptions = json_encode($options + ['useCurrent' => false]);

        $script = <<<EOT
            $('#{$this->id['start']}').datetimepicker($startOptions);
            $('#{$this->id['end']}').datetimepicker($endOptions);
            $("#{$this->id['start']}").on("dp.change", function (e) {
                $('#{$this->id['end']}').data("DateTimePicker").minDate(e.date);
            });
            $("#{$this->id['end']}").on("dp.change", function (e) {
                $('#{$this->id['start']}').data("DateTimePicker").maxDate(e.date);
            });
EOT;

        Admin::script($script);
    }

}

追加した条件の利用方法

今回新たに追加したclassをuse句を使い利用します。

  $filter->use(new CustomBetweenOrFilter('date1', '日付1'))->date();
  $filter->use(new CustomBetweenOrFilter('date2', '日付2'))->date();

これで「date1カラムが期間内日付である、もしくはdate2カラムが期間内日付である」といった条件の検索が可能です。

まとめ

Laravel-adminに限らずですが、ライブラリなど導入すると基本的なことを実現するにはとても楽になるのですが、少し複雑なことをしようとすると文献が少なく苦労することが多いですね。

ソースを正しく解析すればどうカスタマイズすればいいか分かることが多いので、ソースを読み取る技術をもっと磨いていきたいですね

コメントを残す