質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.81%

【Laravel】組み合わせ検索の効率的な書き方について

受付中

回答 3

投稿

  • 評価
  • クリップ 1
  • VIEW 969

mikeross

score 18

いつもお世話になっています。

表題について、みなさんだったらどのようにソースコードを書くかというのを教えていただけると幸いです。
現在Laravelにて組み合わせ【プール】×【勤務地】検索フォームの実装をしています。

自分は以下のソースコードで、ひとまず動くようにしたのですが、
コントローラの書き方が異常にブサイクで、もっとイケメンになりたいです。

特に困っている部分はSearchController.phpにおける、
【 # ここからもっといい感じに書きたい】とコメントが書いてある行以下のif文になります。

ここまでは何とかして、無事検索も動くようになったのですが、
なんとも泥臭い書き方で、将来項目が増えた際に対応するのが大変な予感しかありません。

理想としては、
・キーワード検索と【プール】×【勤務地】の組み合わせ検索は同一フォームだけれども別々にしたい
・組み合わせ検索で【プール】×【勤務地】のどちらかがある場合、または両方のキーワードがある場合は、それぞれのキーワードを”×”で区切って、結合した状態で変数$keywordに格納したい

の2点になります。

# index.blade.php
      <h4 class="h5 font-weight-bold mt-4">プール</h4>
      @foreach( $pools as $key => $value )
         <label class="checkbox-inline mb-1 mr-2 text-secondary">{!! Form::checkbox('pool', $key, false) !!} {{ $value }}</label>
      @endforeach

      <h4 class="h5 font-weight-bold mt-4">都道府県</h4>
      @foreach( $prefectures as $key => $value )
         <label class="checkbox-inline mb-1 mr-2 text-secondary">{!! Form::checkbox('prefecture', $key, false) !!} {{ $value }}</label>
      @endforeach

   <h4 class="h5 font-weight-bold mt-4">キーワード検索</h4>
      {!! Form::text('pool_name', null, ['class' => 'form-control','placeholder'=>'キーワード検索']) !!}
# controller.php
class SearchController extends Controller
{

    private $pools;
    private $prefectures;
    private $query;
    private $paginate_num = 2;


    public function __construct()
    {
        $this->query       = Pool::query();
        $this->prefectures = Place::$prefectures;
    }


    public function combination_search( Request $request )
    {
        $pools              = $request['pool'];
        $request_prefecture = $request['prefecture'];

        # ここからもっといい感じに書きたい
        if( !empty( $request['pool_name'] ) ){

            # プール名
            $keyword    = $request['pool_name'];
            $good_place = $this->query->where( 'pool_name', 'like', '%' . $keyword . '%' )->paginate( $this->paginate_num );

        } elseif( !empty( $pools || !empty( $request_prefecture ) ) ) {

            if( !empty( $pools && !empty( $request_prefecture ) ) ){

                $keyword = $this->pools[ $pools ] . " × " . $this->prefectures[ $request_prefecture ];

            } elseif( !empty( $pools && empty( $request_prefecture ) ) ) {

                $keyword = $this->pools[ $pools ];

            } elseif( empty( $pools && !empty( $request_prefecture ) ) ) {

                $keyword = $this->prefectures[ $request_prefecture ];

            }

            $params = [
                'pool',
                'prefecture',
            ];

            foreach( $request->only( $params ) as $key => $value ){
                $this->query->where( $key, 'like', '%' . $value . '%' );
            }

            $good_place = $this->query->paginate( $this->paginate_num );

        } else {

            # 検索条件の指定がなかった場合=新着情報
            $keyword    = "新着";
            $good_place = Pool::paginate( $this->paginate_num );

        }

        return view( 'pool.list' )->with( [
            'good_place'     => $good_place,
            'keyword'        => $keyword,
            'pools'          => $this->pools,
            'contract_types' => $this->contract_types,
        ] );
    }

}

環境

PHP 7.1.23
Laravel Framework 5.8.7

以上となりますが、
お忙しいところ大変恐縮ですが、ご教授いただけると幸いです。
それでは宜しくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

やっつけですが書いてみました。
要件がわからない部分もあるので意図が変わってしまってる場所があるかもしれませんがご参考程度にどうぞ。

    public function combination_search(Request $request)
    {
        $request_pool = $request->input('pool');
        $request_prefecture = $request->input('prefecture');
        $pool_name = $request->input('pool_name');

        $keyword = $this->create_keyword($pool_name, $request_pool, $request_prefecture);
        $good_place = $this->search_good_place($pool_name, $request_pool, $request_prefecture);

        return view('pool.list')->with([
            'good_place' => $good_place,
            'keyword' => $keyword,
            'pools' => $this->pools,
            'prefectures' => $this->prefectures,
        ]);
    }

    private function create_keyword($pool_name, $request_pool, $request_prefecture)
    {
        if ($pool_name) {
            return $pool_name;
        }

        if ($request_pool && $request_prefecture) {
            return $this->pools[$request_pool] . " × " . $this->prefectures[$request_prefecture];
        }

        if ($request_pool) {
            return $this->pools[$request_pool];
        }

        if ($request_prefecture) {
            return $this->prefectures[$request_prefecture];
        }

        return '新着';
    }

    private function search_good_place($pool_name, $request_pool, $request_prefecture)
    {
        if ($pool_name) {
            return $this->query
                ->where('pool_name', 'like', '%' . $pool_name . '%')
                ->paginate($this->paginate_num);
        }

        if ($request_pool) {
            $this->query->where('pool', 'like', '%' . $request_pool . '%');
        }
        if ($request_prefecture) {
            $this->query->where('prefecture', 'like', '%' . $request_pool . '%');
        }

        return $this->query->paginate($this->paginate_num);
    }

直してみたポイントは、

  • $requestからの値の受け取りは配列アクセスではなくて->input()を使う
  • $requestにアクセスする場所は限定して、変数に一旦格納してから使う
  • keywordを作成する部分と検索する部分を別の関数に切り出す
  • 切り出した関数内ではガード節を使ってネストを浅くする

あたりです。

その他、

  • poolとprefectureってcheckboxだけど複数入ってくるのを考慮しなくていいのかな?
  • !empty( $pools || !empty( $request_prefecture ) ) とかの条件って !empty( $pools ) || !empty( $request_prefecture ) の間違い?
  • 'contract_types' => $this->contract_types, が無かったんでprefecturesの間違い?
  • $this->pools$this->prefecturesには、IDをキーにして名前でも入っているのかと思ったんですが、検索するときの、poolとprefectureのクエリ、LIKE検索で良いのでしょうか?(IDとかで検索するのではない?)

あたり気になったのですが、適当に解釈しました。


将来項目が増えた際に対応するのが大変な予感しかありません。

とのことですが、私は項目が増えたときのことは増えたときに考えれば良い派です。
ですので、今回私が提示したコードは、とりあえず現状のコードの意図がわかりやすくなるように分割しただけです。

ただ、もし項目が増えたときは、ここに書き加えて対応してもいいですが、
正直なところ、Controller内にクエリが入ってくるのはあまり良い設計とは言えませんので、
別途検索サービスのような別クラスを作ったりして、コントローラーはシンプルにしたほうが保守性は上がると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

ちゃんと見てないけど、半分ぐらいモデルに持っていけるのでは?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

複雑な条件分岐の場合は、複雑に書くしかありませんが、別のコントローラやコマンドで使いたくなった時のために、モデルのローカルスコープに処理を移すことが考えられます。
Laravel 5.8 Eloquent:利用の開始 ローカルスコープ

コードを短く書きたい場合は、クエリビルダーのwhenメソッドを使うとメソッドチェーンで簡潔にかけます。

$this->query->when(条件判定A,function($q){
    $q->where(追加のWhere句A)
  })->when(条件判定B,function($q){
    $q->where(追加のWhere句B)
  })->paginate();


Laravel 5.8 データベース:クエリビルダ 条件節

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.81%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る