Laravel高级搜索查询修复

时间:2018-02-22 01:25:12

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

我有一个带有多个输入和选择框的搜索表单我需要帮助才能得到我的查询中的条件,以便每个部分单独工作并且一次完成。

这是我的刀片代码:

<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列。

BUT

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

8 个答案:

答案 0 :(得分:4)

这只是为了提出一个想法。您可以为查询使用多个->where()和热切加载->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匹配的产品,   - productspecifications表示此产品与标准不符,因此我们必须将其从集合中删除。

$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

答案 1 :(得分:2)

使用对待方式进行动态搜索非常简单,我们可以将其用于我使之尽可能动态的所有模型

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

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

{
 "uuid": "a4d9dae0-377a-11ea-81da-89b3a58be8ac",
 "name": "sc45_FicheProduitHygienePourFournisseurImpNegParFournisseur",
 "children": [
      "a67a1d60-377a-11ea-81da-89b3a58be8ac",
      "a9755c00-377a-11ea-81da-89b3a58be8ac",
      "ac70c1b0-377a-11ea-81da-89b3a58be8ac",
      "afa056c0-377a-11ea-81da-89b3a58be8ac",
      "b813e1f0-377a-11ea-81da-89b3a58be8ac",
      "bb12f120-377a-11ea-81da-89b3a58be8ac",
      "c4b07d60-377a-11ea-81da-89b3a58be8ac",
      "d73983f0-377a-11ea-81da-89b3a58be8ac",
      "dab8e6b0-377a-11ea-81da-89b3a58be8ac",
      "ef101d90-377a-11ea-81da-89b3a58be8ac",
      "f2923f70-377a-11ea-81da-89b3a58be8ac",
      "2d2a1860-377b-11ea-81da-89b3a58be8ac",
      "30966850-377b-11ea-81da-89b3a58be8ac",
      "59391640-377b-11ea-81da-89b3a58be8ac",
      "5cd61320-377b-11ea-81da-89b3a58be8ac",
      "7dc21d40-377b-11ea-81da-89b3a58be8ac",
      "816338d0-377b-11ea-81da-89b3a58be8ac",
      "89cd9c40-377b-11ea-81da-89b3a58be8ac",
      "93b26ab0-377b-11ea-81da-89b3a58be8ac",
      "96adf770-377b-11ea-81da-89b3a58be8ac"
 ],
 "start": 1579081047950,
 "stop": 1579081461682
}

也过滤到关系中

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;
}

答案 2 :(得分:1)

您可以使用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将有产品集合如果上述查询中陈述的任何条件匹配。

希望这有帮助。

答案 3 :(得分:1)

以下是我的表现。注意使用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]);
    });

答案 4 :(得分:1)

我建议使用每个separeted并帮助您使用easaly manupulate代码

作为典型条件,您的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']);
    } 

答案 5 :(得分:0)

我建议采用不同的方法。

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

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 :(得分:0)

这是我使用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();

如果没有提交任何输入,上面的方法将返回所有产品,并将根据输入过滤结果(如果可用),除此之外,它还是一个很好的做法,可以在继续之前使用验证来清理输入。查询

答案 7 :(得分:0)

解决

经过数周的代码播放后,我为自己找到了正确的结果(在我的情况下,它可以通过这种方式为其他人工作,也可以与其他建议的答案一起使用)

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)中看到的那样,(int)Input::get('min_price')解决了我的大多数定价问题   (int)Input::get('max_price')

特别感谢 Ravindra Bhanderi 提出的count($request['']建议。