Laravel advanced search query fix

后端 未结 8 1300
终归单人心
终归单人心 2021-01-28 21:34

I have a search form with multiple input and select boxes I need help to get if conditions in my query in order to each part works separately and all at once.

he

相关标签:
8条回答
  • 2021-01-28 21:51

    I suggest tu use each separeted and its help you to feature easaly manupulate code

    as your typical condition your sub_option come from third table last relation ship is used.

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

    for min price max price i assume your price in procuct table

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

    for brand as you say brand_id column in product table then

       if(count($request['brands'])) {
    
              $product->whereIn('brand_id',$request['brands']);
        } 
    
    0 讨论(0)
  • 2021-01-28 22:01

    You can use laravel orWhere and orWhereHas to get results separately and all at once, let's say you do not select min_price and max_price but you have selected brand then all products with this brnad should be return, your query will look like this

    $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 will have products collection If any of the condition stated in above query matched.

    Hope this helps.

    0 讨论(0)
  • 2021-01-28 22:05

    Here's how I'd do it. Note the use of when for simplifying optional where conditions (no need to set variables either), and the closure for constraining both the whereHas and the with (if you want to eager load the relationships).

    $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]);
        });
    
    0 讨论(0)
  • 2021-01-28 22:07

    This is just to give an idea. You can use a multiple ->where() and eager loading ->with() for your query. Take a look with this query below:

    $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.
    

    Finally, you can filter the products which matches the specifications, - the product with an empty specifications means this product does not match the criteria, therefore we'll have to remove it from the collection.

    $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
    
    0 讨论(0)
  • 2021-01-28 22:08

    This is the method I use to search using laravel eloquent with multiple input:

    $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();
    

    The method above will return all products if no input is submitted, and will filter the result based on the input if available, on top of this it's also a good practice to sanitize your inputs with validations before proceeding with the query

    0 讨论(0)
  • 2021-01-28 22:09

    it is very simple to make dynamic search by using treats we can use this for all models I made this dynamic as possible

    This is a trait that can be used by any models

    This function will remove code repetitions into your project

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

    Filter into relationship also

    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;
    }
    
    0 讨论(0)
提交回复
热议问题