【问题标题】:Laravel advanced search query fixLaravel 高级搜索查询修复
【发布时间】:2018-08-01 18:11:45
【问题描述】:

我有一个包含多个输入和选择框的搜索表单,我需要帮助来获取我的查询中的条件,以便每个部分单独并同时工作。

这是我的刀片代码:

<form action="{{route('advancesearch')}}" method="post">
      {{csrf_field()}}
      <div class="sidebar-title">
        <span>Advanced Search</span>
        <i class="fa fa-caret-down show_sidebar_content" aria-hidden="true"></i>
      </div>
      <!-- ./sidebar-title -->

      <div id="tags-filter-content" class="sidebar-content">
        <div class="filter-tag-group">

          @foreach($options as $option)
          <div class="tag-group">
            <p class="title">
              <span class="filter-title show_filter_content">{{$option->title}} <span class="pull-right"><i class="fa fa-minus"></i></span></span>
            </p>
            <div class="filter-content">
              <div class="checkbox">
              @foreach($option->suboptions as $suboption)
              <label for="suboptions">
                <input name="suboptions[]" type="checkbox" value="{{$suboption->id}}">
                {{ucfirst($suboption->title)}}
              </label>
              @endforeach
            </div>
          </div>
          </div>
          @endforeach
          <!-- ./tag-group -->

          <div class="tag-group">
            <p class="title">
              <span class="filter-title show_filter_content">Brand <span class="pull-right"><i class="fa fa-minus"></i></span></span>
            </p>
            <div class="filter-content">
              <div class="checkbox">
              @foreach($brands as $brand)
              <label for="brands">
                <input name="brands[]" type="checkbox" value="{{$brand->id}}">
                {{$brand->title}}
              </label>
              @endforeach
            </div>
          </div>
          </div>
          <!-- ./tag-group -->

          <div class="tag-group">
            <p class="title">
              <span class="filter-title show_filter_content">Price Range <span class="pull-right"><i class="fa fa-minus"></i></span></span>
            </p>
            <div class="row filter-content">
              <div class="col-md-6">
                <div class="form-group">
                  <label for="min_price" hidden>Min</label>
                  <input type="text" name="min_price" class="form-control" placeholder="Rp Min">
                </div>
              </div>
              <div class="col-md-6">
                <div class="form-group">
                  <label for="max_price" hidden>Max</label>
                  <input type="text" name="max_price" class="form-control" placeholder="Rp Max">
                </div>
              </div>
            </div>
          </div>
          <!-- tag-group -->

          <div class="text-center mt-20">
            <button type="submit" class="btn btn-danger">TERPAKAN</button>
          </div>

        </div><!-- ./filter-tag-group -->
      </div><!-- ./sidebar-content -->
    </form>

这是我的路线:

Route::post('/advanced-search', 'frontend\SearchController@filter')->name('advancesearch');

最后我的函数代码是:

public function advancedsearch(Request $request) {
        $brands = Brand::all(); // uses for other part of the page. (not related to search function)
        $options = Option::all(); // uses for other part of the page. (not related to search function)
        $suboptions = DB::table('product_suboption'); // where my product_id and subopyion_id saves

        //search function
        $products = Product::where(function($query){
            //getting inputs
            $suboptions2 = Input::has('suboptions') ? Input::get('suboptions') : [];
            $min_price = Input::has('min_price') ? Input::get('min_price') : null;
            $max_price = Input::has('max_price') ? Input::get('max_price') : null;
            $brands2 = Input::has('brands') ? Input::get('brands') : [];

            //returning results
            $query->where('price','>=',$min_price)
                    ->where('price','<=',$max_price);
            })->get();

        return view('front.advancesearch', compact('products', 'brands', 'options'));
    }

我的模特关系:

product模特:

public function options(){
     return $this->belongsToMany(Option::class);
  }
  public function suboptions(){
     return $this->belongsToMany(Suboption::class, 'product_suboption', 'product_id', 'suboption_id');
  }
public function brand(){
     return $this->belongsTo(Brand::class);
  }

Option模特:

public function suboptions(){
     return $this->hasMany(Suboption::class, 'option_id');
  }

  public function products(){
     return $this->belongsToMany(Product::class);
  }

Suboption模特:

public function option(){
     return $this->belongsTo(Option::class, 'option_id');
  }

  public function products(){
     return $this->belongsToMany(Product::class);
  }

Brand型号:

public function products(){
     return $this->hasMany(Product::class);
}

注意

我的 brands 搜索来自产品表,其中每个产品都有 brand_id 列。

但是

我的suboptions 来自名为product_suboption 的第三张表(如您在我的模型代码中所见),我保存product_idsuboption_id

【问题讨论】:

  • 您的复选框输入字段没有设置名称属性,因此您无法从中获取值,因为它们没有被发送。也许这会有所帮助。
  • @Reduxx 更新了我的代码。

标签: php laravel laravel-5 eloquent laravel-eloquent


【解决方案1】:

通过使用处理来进行动态搜索非常简单,我们可以将其用于所有模型我尽可能使这个动态搜索

这是任何模型都可以使用的特征

此功能将删除项目中的重复代码

public function scopeSearch($query, $keyword, $columns = [], $relativeTables = [])
{
    if (empty($columns)) {
        $columns = array_except(
            Schema::getColumnListing($this->table), $this->guarded
        );
    }   

    $query->where(function ($query) use ($keyword, $columns) {
        foreach ($columns as $key => $column) {
            $clause = $key == 0 ? 'where' : 'orWhere';
            $query->$clause($column, "LIKE", "%$keyword%");

            if (!empty($relativeTables)) {
                $this->filterByRelationship($query, $keyword, $relativeTables);
            }
        }
    });

    return $query;
}

也过滤到关系中

private function filterByRelationship($query, $keyword, $relativeTables)
{
    foreach ($relativeTables as $relationship => $relativeColumns) {
        $query->orWhereHas($relationship, function($relationQuery) use ($keyword, $relativeColumns) {
            foreach ($relativeColumns as $key => $column) {
                $clause = $key == 0 ? 'where' : 'orWhere';
                $relationQuery->$clause($column, "LIKE", "%$keyword%");
            }
        });
    }

    return $query;
}

【讨论】:

  • 欢迎提出任何问题,我将为您解释
【解决方案2】:

已解决

在玩了数周的代码后,我终于为自己找到了正确的结果(在我的情况下,它对其他人有效,可能与其他建议的答案一起有效)

public function advancedsearch(Request $request) {
    $options = Option::all();
    $brands = Brand::all();
    $brandss = Input::has('brands') ? Input::get('brands') : [];
    $suboption = Input::has('suboptions') ? (int)Input::get('suboptions') : [];
    $min_price = Input::has('min_price') ? (int)Input::get('min_price') : null;
    $max_price = Input::has('max_price') ? (int)Input::get('max_price') : null;

    //codes
    if(count($request['suboptions'])){
      $products = DB::table('products')
      ->join('product_suboption', function ($join) {
        $suboption = Input::has('suboptions') ? Input::get('suboptions') : [];
            $join->on('products.id', '=', 'product_suboption.product_id')
                 ->where('product_suboption.suboption_id', '=', $suboption);
        })
      ->paginate(12);
    }

    elseif(count($request['brands'])){
      $products = DB::table('products')
      ->whereIn('products.brand_id', $brandss)
      ->paginate(12);
    }

    elseif(count($request['min_price']) && count($request['max_price'])){
      $products = DB::table('products')
      ->whereBetween('price', [$min_price, $max_price])
      ->paginate(12);
    }


    return view('front.advancesearch', compact('products', 'brands', 'options'));
    }

注意:正如您在我的代码 (int)Input::get('min_price')(int)Input::get('max_price').

特别感谢 Ravindra Bhandericount($request[''] 建议。

【讨论】:

    【解决方案3】:

    这是我使用 laravel eloquent 进行多输入搜索的方法:

    $input = Input::all(); //group all the inputs into single array
    $product = Product::with('options','suboptions','brand');
    
    //looping through your input to filter your product result
    foreach ($input as $key => $value)
    {
        if ($value!='') {
           if ($key == "max_price")
                $product = $product->where('price','<=', $value);
           elseif ($key == "min_price")
                $product = $product->where('price','>=', $value);
           elseif ($key == "brands")
                $product = $product->whereIn('brand_id', $value); //assuming that your Input::get('brands') is in array format
           elseif ($key == "suboptions")
                $product = $product->whereIn('suboption_id', $value);
        }
    }
    $product = $product->get();
    

    如果没有提交任何输入,上述方法将返回所有产品,并根据输入(如果可用)过滤结果,除此之外,在继续查询之前使用验证清理输入也是一个好习惯

    【讨论】:

    • Illegal operator and value combination.
    • 如果您提供有关错误的更多详细信息(例如导致错误的行)会很有帮助,我的猜测是提交的输入中有空值或空白值。我已经为此编辑了答案
    【解决方案4】:

    我建议您使用每个分隔符,它可以帮助您轻松处理代码

    作为您的典型条件,您的 sub_option 来自第三个表,最后使用关系。

     if(count($request['suboptions'])) {
    
             $product->whereHas('options',function($options) use ($request) {
    
                       $options->whereHas('suboptions',function($suboption)use($request) {
    
                             $suboption->whereIn('id',$request['suboptions']);
                      });
             }); 
     }
    

    对于最低价格最高价格,我假设您在产品表中的价格

       if(! empty($request['min_price'])) {
    
              $product->where('price','>=',$request['min_price']);
        }
    
     if(! empty($request['max_price'])) {
    
              $product->where('price','<=',$request['max_price']);
        }
    

    对于品牌,如您所说的产品表中的brand_id 列

       if(count($request['brands'])) {
    
              $product->whereIn('brand_id',$request['brands']);
        } 
    

    【讨论】:

    • 感谢您的回复,但请查看我的代码,我的表单中没有输入产品名称,对于我的选项,请阅读我的最新更新note 部分。
    • 它是一个例子,实际上你可以根据我的回答进行开发
    【解决方案5】:

    我建议一种不同的方法。

    在您的控制器上,将其更改为:

    public function advancedsearch(Request $request) {
    
    $suboptions2 = request->suboptions ? request->suboptions : null;
    $min_price = request->min_price ? request->min_price : null;
    $max_price = request->max_price ? request->max_price : null;
    $brands2 = request->brands ? request->brands : null;
    
    $query = Product::select('field_1', 'field_2', 'field_3')
    ->join('brands as b', 'b.id', '=', 'products.brand_id')
    ...(others joins);
    
    // here we do the search query
    if($suboptions2){
        $query->where('suboptions_field', '=', $suboptions);
    }
    
    if($min_price && $max_price){
        $query->where(function($q2) {
                    $q2->where('price', '>=', $min_price)
                        ->where('price', '<=', $max_price)
                });
    
    }
    
    if($brands2){
        $query->where('products.brand_id', '=', $brands2);
    }
    
    // others queries
    
    // finish it with this
    $query->get();
    
    return view('front.advancesearch', compact('products', 'brands', 'options'));
    

    我发现这样做非常有用,因为它可以很容易地实现额外的查询。

    【讨论】:

      【解决方案6】:

      我会这样做。请注意使用 when 来简化可选的 where 条件(也不需要设置变量),以及用于约束 whereHaswith 的闭包(如果您想立即加载关系)。

      $products = Product::query()
          ->when($request->min_price, function ($query, $min_price) {
              return $query->where('price', '>=', $min_price);
          })
          ->when($request->max_price, function ($query, $max_price) {
              return $query->where('price', '<=', $max_price);
          })
          ->when($request->suboptions, function ($query, $suboptions) {
              $suboptionsConstraint = function ($q) use ($suboptions) {
                  return $q->whereIn('id', $suboptions);
              };
              return $query->whereHas('suboptions', $suboptionsContraint)
                  ->with(['suboptions' => $suboptionsContraint]);
          })
          ->when($request->brands, function ($query, $brands) {
              $brandsConstraint = function ($q) use ($brands) {
                  return $q->whereIn('id', $brands);
              };
              return $query->whereHas('brands', $brandsConstraint)
                  ->with(['brands' => $brandsConstraint]);
          });
      

      【讨论】:

      • Illegal operator and value combination. $products = Product::where('price', '&gt;=', $request-&gt;min_price)
      • @mafortis 那是因为价格也是可选的,对吧?查看更新的答案
      • 是的,我在 `return $q->whereIn('id', $suboptions);` 上收到此错误 Invalid argument supplied for foreach() 附注:我认为您应该阅读我的问题更新(注意)。
      • @mafortis 您是否总是收到该错误或suboptions 的特定值?
      • 我的页面无法加载。
      【解决方案7】:

      您可以使用 laravel orWhereorWhereHas 分别一次性获得结果,假设您没有选择 min_pricemax_price 但您选择了 brand 那么所有具有此 brnad 的产品都应该返回时,您的查询将如下所示

      $products = Product::orWhere('price','>=',$min_price)
      ->orWhere('price','<=',$max_price)
      ->orWhereHas('brand',function($query){
          $query->whereIn('id', $brand_ids);
      })
      ->orWhereHas('suboptions',function($query){
          $query->whereIn('id', $suboptions_ids);
      })
      ->orWhereHas('subspecifications',function($query){
          $query->whereIn('id', $subspecifications_ids);
      })->get(); 
      

      $products 将收集产品如果上述查询中所述的任何条件匹配。

      希望这会有所帮助。

      【讨论】:

      • 我收到Undefined variable: min_price
      • SearchControllerfilter 方法中,您必须从提交的表单中获取输入,即$min_price = $request-&gt;min_price,然后将它们传递给查询。在$request-&gt;min_price min_price 是您输入的名称。
      • orwhere 不起作用,我们必须至少有1 where 然后说orwhere
      【解决方案8】:

      这只是提供一个想法。您可以使用多个-&gt;where() 和预先加载-&gt;with() 进行查询。 看看下面的这个查询:

      $products = Product::where('price', '>=', $min_price) // you get the max and min price 
              ->where('id', '<=', $max_price)->select('id')
              ->with([
                  "brand" => function ($query) {
                      $query->whereIn('id', $brand_ids); // [1, 2, 3,...]
                  },
                  "specifications" => function ($query) {
                      $query->where('some_column', '=', 'possible-value'); // single condition
                  },
                  "specifications.subspecifications" => function ($query) {
                      $query->where([
                          'some_column' => 'possible-value',
                          'another_column' => 'possible-value'
                      ]); // you can also pass arrays of condition
                  }
              ])->get(); // This will return the products with the price set by the user
                         // Since we're just using ->with(), this will also return those products
                         // that doesn't match the other criteria specifications) so we 
                         // still need to filter it.
      

      最后,您可以过滤与specifications匹配的产品, - product 和空的 specifications 表示该产品不符合条件,因此我们必须将其从集合中删除。

      $filtered =  $products->filter(function ($product, $key) {
          return count($product->brand) > 0 && count($product->specifications) > 0;
          // add your other boolean conditions here
      });
      
      dd($filtered->toArray()); // your filtered products to return
      

      【讨论】:

        猜你喜欢
        • 2018-10-21
        • 2015-06-09
        • 1970-01-01
        • 2015-01-14
        • 1970-01-01
        • 2015-04-17
        • 2016-03-27
        • 2014-06-08
        • 1970-01-01
        相关资源
        最近更新 更多