Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Melodia's avatar

How can I make a more refine search result

I am learning query strings and have an input form with multiple entries for search. One of them is max_price ad suburb.

When I search for all properties in Kenilworth, I see that the filter does not work because it shows me all the properties.

http://owlcrest.local/search?suburb=%22Kenilworth%22

At the moment this is what I have in my controller:

public function search_page(Request $request)
{
    $property = (new Property)->newQuery();
   
    if($request->exists('suburb')){
        $property->orderBy('id', 'desc');
    }

    if($request->has('max_price')){
        $property->where('price', '<=', $request->max_price);
    }

    return $property->get();
 
}

The route looks like this:

Route::get('/search', 'AdminPropertyController@search_page')->name('search-page');

So, I want to implement a search that handles all type of requests. A user should be capable of searching with max_price only, min_price, suburb, or all.

How can I go about that?

My function initially looked like the one below, and I had posted a question about that before, but it didnt give me correct results

public function search_page(Request $request)
{
    // validation ...
    $query = Property::query();

    if ($city = $request->input('city_suburb') ?? '')
    {
        $query->where('city', $city)
              ->orWhere('suburb', $city);
    }

    if ($type = $request->input('type') ?? '')
    {
        $query->where('type', $type);
    }

    // if none of them is null
    if (! (is_null($request->input('min_price')) && is_null($request->input('max_price')))) {
        // fetch all between min & max values
        $query->whereBetween('price', [$request->input('min_price'), $request->input('max_price')]);
    }
    // if just min_price is available (is not null)
    elseif (! is_null($request->input('min_price'))) {
        // fetch all greater than or equal to min_price
        $query->where('price', '>=', $request->input('min_price'));
    }
    // if just max_value is available (is not null)
    elseif (! is_null($request->input('max_price'))) {
        // fetch all lesser than or equal to max_value
        $query->where('price', '<=', $request->input('max_price'));
    }

    $properties = $query->orderBy('id', 'asc')->paginate(9);

    return view('pages.search', compact('properties'));     
}

Hope that someone can support on that.

0 likes
18 replies
Snapey's avatar

this

    if($request->exists('suburb')){
        $property->orderBy('id', 'desc');
    }

only orders the results.

When a user enters different searches, are you wanting them to be AND (the reply must contain all the search terms) or OR (the reply contains any of the search terms).

I don't know how you are sending kenilworth but it shouldn't have quotes around it? (those %22's)

1 like
Melodia's avatar

@Snapey I just changed my search:

  http://owlcrest.local/search?suburb=Kenilworth

But it still return all the properties from the table instead.

"When a user enters different searches, are you wanting them to be AND (the reply must contain all the search terms) or OR (the reply contains any of the search terms)."

The reply must contain any of the search term. Because I could search for a property by city only. Or could search property by city where min price is 200000dollars. Or I can just search for property by min price, and etc. Hope I managed to explain properly

"I don't know how you are sending kenilworth but it shouldn't have quotes around it? (those %22's)"

At the moment I am entering the url manually, just fo rtesting purposes.

Melodia's avatar

So, I now changed my method to this:

public function search_page(Request $request)
{
    $property = (new Property)->newQuery();
   
    if($request->exists('suburb')){
        $property->whereSuburb($request->suburb);
    }

    if($request->has('max_price')){
        $property->where('price', '<=', $request->max_price);
    }

    return $property->get();
 
}  

But this is what happens: The below works:

http://owlcrest.local/search?suburb=Kenilworth

But the below does not really work:

http://owlcrest.local/search?max_price=200

It returns me properties which the price are even 1554511

And if I search for this:

http://owlcrest.local/search?suburb=Kenilworth?max_price=52954

It does not return anything, which is not really right.

Melodia's avatar

@D9705996 Yes I watched the tutorial and my code is based on the first minutes of that tutorial if you take a look at that. But it feels weird to follow the advanced when the basic is not working.

Snapey's avatar

change if($request->has('max_price')){ to exists like the other query?

Melodia's avatar

I remember to have changed to that but it did not work. But I followed Jeffrey's tutorial and just trying to implement the way he explained.

All works, but only if I type the query in the url my self. If I user the form, it gives me this error:

 Too few arguments to function App\PropertyFilters::min_price(), 0 passed and exactly 1 expected

And the url converts into these:

http://owlcrest.local/search?city_suburb=&type=apartment&min_price=&max_price=&bedrooms=&bathrooms=

Which seems like all records are compulsory

My form looks like this:

{!! Form::open(['method' => 'GET', 'action'=>'AdminPropertyController@search_page']) !!}
    <div class="row flex wrap">

        <div class="col-sm-10">
            <div class="search-input">
                {!! Form::text('city_suburb', null, ['class'=>'form-controla', 'placeholder'=>'Search for suburb']) !!}         
            </div>

            <div class="select-boxes">
                {!! Form::select('type', [''=>'Type of property'] + ['apartment'=>'Apartment', 'house'=>'House', 'plot'=>'Plot', 'smallholding'=>'Smallholding', 'commercial'=>'Commercial', 'townhouse'=>'Townhouse'], null, ['class'=>'form-']) !!}   

                {!! Form::select('min_price', [''=>'Minimum'] + [450000 => 'R 450 000', 5000000 => 'R 500 000', 1000000 => 'R 1 000 000',2000000 => 'R 2 000 000', 3000000 => 'R 3 000 000', 4000000 => 'R 4 000 000',5000000 => 'R 5 000 000', 6000000 => 'R 6 000 000',

], null, ['class'=>'form-']) !!} {!! Form::select('max_price', [''=>'Maximum'] + [450000 => 'R 450 000', 500000 => 'R 500 000', 1000000 => 'R 1 000 000',2000000 => 'R 2 000 000', 3000000 => 'R 3 000 000', 4000000 => 'R 4 000 000',5000000 => 'R 5 000 000', 6000000 => 'R 6 000 000', ], null, ['class'=>'form-']) !!} {!! Form::select('bedrooms', [''=>'Bedroom'] + [2=>'1+', 3=>'2+', 20=>'20+'], null, ['class'=>'form-']) !!} {!! Form::select('bathrooms', [''=>'bathroom'] + [2=>'1+', 3=>'2+', 20=>'20+'], null, ['class'=>'form-']) !!}

        <div class="col-sm-2">
            {!! Form::submit('Search', ['class'=>'search bg-brown']) !!}
            <button class="clear-filter button">Clear Filter</button>
        </div>                  
    </div>

{!! Form::close() !!}

My function in the search_page became like the one below:

public function search_page(PropertyFilters $filters)
{
   return Property::filter($filters)->get();
 
} 

PropertyFilters.php

class PropertyFilters extends QueryFilter
{

    public function type($type){
        return $this->builder->where('type', $type);
    }

    public function min_price($price){
        return $this->builder->where('price', '>=', $price);
    }

    public function max_price($price){
        return $this->builder->where('price', '<=', $price);
    }

    public function bathrooms($bathrooms){
        return $this->builder->where('bathrooms', $bathrooms);
    }

    public function bedrooms($bedrooms){
        return $this->builder->where('bedrooms', $bedrooms);
    }
}

QueryFilter.php

abstract class QueryFilter
{
    //
    protected $request;
    protected $builder;

    public function __construct(Request $request){
        $this->request = $request;
    }

    public function apply(Builder $builder){
        $this->builder = $builder;

        foreach($this->filters() as $name => $value){
            if(method_exists($this, $name)){
                call_user_func_array([$this, $name], array_filter([$value]));
            }
        }

        return $this->builder;
    }

    public function filters(){
        return $this->request->all();
    }
}

I guess this goes back to the question @Snapey asked previously: When a user enters different searches, are you wanting them to be AND (the reply must contain all the search terms) or OR (the reply contains any of the search terms).

So in this case the reply should only contain the the search terms.

D9705996's avatar

@Melodia, if you pass in the filter name then it will be added your query. You could use a default value in your filter to prevent the error your seeing

public function min_price($price = 0){
...
}
Snapey's avatar

you seem to be missing a check that the filter is actually set?

If the request has an empty property then you should skip that where statement

1 like
D9705996's avatar

You could also just return if the value is null

public function min_price($price = null) {

  if(!$price) {
    return $this->builder;
  }
  return $this->builder->where('price', '>=', $price);
}
Melodia's avatar

Sounds a bit odd. I attempted to initialise the price to zero, but it complained about the other methods. So intialized everything to null. But now if I just search for type, it gives me an empty array.

public function type($type){
    return $this->builder->where('type', $type);
}

public function min_price($price = 0){
    return $this->builder->where('price', '>=', $price);
}

public function max_price($price = 0){
    return $this->builder->where('price', '<=', $price);
}

public function bathrooms($bathrooms = null){
    return $this->builder->where('bathrooms', $bathrooms);
}

public function bedrooms($bedrooms = null){
    return $this->builder->where('bedrooms', $bedrooms);
}
D9705996's avatar

@melodia - use null for each filter with the conditional like my last response should fix your problem

Melodia's avatar

@D9705996 I followed your suggestion and it actually works now. Initialized all the variables and put a conditional statement in all the methods.

public function city_suburb($city = null){
    if(!$city) {
       return $this->builder;
    } 
    return $this->builder->where('city', $city)->orWhere('suburb', $city);
}

public function type($type = null){
    if(!$type) {
       return $this->builder;
    } 
    return $this->builder->where('type', $type);
}

public function min_price($price = 0){
    if(!$price) {
       return $this->builder;
    } 
    return $this->builder->where('price', '>=', $price);
}

 public function max_price($price = 0){
    if(!$price) {
       return $this->builder;
    } 
    return $this->builder->where('price', '<=', $price);
}

public function bathrooms($bathrooms = null){
    if(!$bathrooms) {
       return $this->builder;
    }       
    return $this->builder->where('bathrooms', $bathrooms);
}

public function bedrooms($bedrooms = null){
    if(!$bedrooms) {
       return $this->builder;
    }       
    return $this->builder->where('bedrooms', $bedrooms);
}
Snapey's avatar
Snapey
Best Answer
Level 122

because you are filtering to only records where the property is null

change the queryfilter

public function apply(Builder $builder){
        $this->builder = $builder;

        foreach($this->filters() as $name => $value){
            if($value && method_exists($this, $name)){
                call_user_func_array([$this, $name], array_filter([$value]));
            }
        }

        return $this->builder;
    }

will only call the function if the value is set, then you dont need to clutter the filter

1 like
D9705996's avatar

Glad you got it working. I would just be careful with your price functions

public function max_price($price = 0){
    if(!$price) {
...

If zero is a valid filter value then it won't apply the filter as if(!$price) { will be true.

This might not matter for price but you might be better with if(is_null($price)) and always setting default value to null.

Please or to participate in this conversation.