It might not be applicable for your case - but I had to do something similar recently and just decided to return a superset of results to the client and then filter it 'live' using JS. From the end users perspective it was actually faster even though I was sending them more raw data in the first instance - from then on it was pretty instant to do whatever sub-selections/filters they wanted. Anyway - it's another way of doing it which might (or might not) be of use :-)
Refactor Controller based on many get params
Hi guys. I have a URL with many filters. Some are standalone, some require another. A example of URL would be:
Standalone params:
- type=apartment
- rooms=3
- current_floor=8
For the standalone params I created a table:
Schema::create('default_characteristics', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->unsignedInteger('property_type_id'); //the rooms characteristic is available only for apartments for example
$table->timestamps();
});
and another one propriety_characterstics where I save the characterstic_id and the property_id.
Standalone boolean params:
- thermal_power_station=1
- pvc_windows=1
- insulation=1
- parquet=1
- hone=0
- faience=0
- renovated=1
- metal_door=0
For Standalone boolean params I created this table:
Schema::create('default_amenities', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->unsignedInteger('property_type_id'); //the parquet amenity is available only for apartments for example
$table->timestamps();
});
and another one propriety_amenities where I save the amenity_id and the property_id.
Depending params:
- city=new_york-1 // where 1 is the id, because I dont wana use the slug to query the database. With this I avoid duplicate city name.
- area=obcini-1 // can be only if a valid city param exists , also 1 is the id
- street=1_decembrie_1918-1 //can be only if a valid city and a optional area exists, but if area is selected must be a valid one, also 1 is the id
Here I have 3 tables for each, cities, areas, streets.
Since now I only explained how things works :) . To do this I wrote the following controller:
public function properties()
{
//url http://localhost:8000/properties?transaction=sale&type=apartment&city=suceava-1&area=obcini-1&rooms=3¤t_floor=8&thermal_power_station=1&pvc_windows=1&insulation=1&parquet=1&hone=0&faience=0&renovated=1&metal_door=0
$filters = Input::all();
$type = key_exists('type', $filters) ? $filters['type'] : null;
if($type):
$property_type = PropertyType::where('name', $type)->get()->first();
$characteristics = DefaultCharacteristic::where('property_type_id', $property_type->id)->get();
$amenities = DefaultAmenity::where('property_type_id', $property_type->id)->get();
endif;
$city_id = key_exists('city', $filters) ? explode('-', $filters['city'])[1] : null;
$area_id = key_exists('area', $filters) ? explode('-', $filters['area'])[1] : null;
$street_id = key_exists('street', $filters) ? explode('-', $filters['street'])[1] : null;
$transaction = key_exists('transaction', $filters) ? $filters['transaction'] : null;
$properties = Property::query();
if(isset($property_type)):
$properties = $properties->whereHas('property_type', function ($query) use($type) {
$query->where('name', $type);
});
foreach($characteristics as $characteristic):
if(array_key_exists ($characteristic->name, $filters)):
$values = explode(',', $filters[$characteristic->name]);
$id = $characteristic->id;
foreach($values as $value):
if(count($values) > 1):
$properties = $properties->whereHas('property_characteristics', function ($query) use($value, $id) {
$query->where('characteristic_id', $id)
->orWhere('value', $value);
});
else:
$properties = $properties->whereHas('property_characteristics', function ($query) use($value, $id) {
$query->where('characteristic_id', $id)
->where('value', $value);
});
endif;
endforeach;
endif;
endforeach;
foreach($amenities as $amenity):
if(array_key_exists ($amenity->name, $filters)):
$status = $filters[$amenity->name];
$id = $amenity->id;
$properties = $properties->whereHas('property_amenities', function ($query) use($status, $id) {
$query->where('amenity_id', $id)
->where('status', $status);
});
endif;
endforeach;
endif;
if(isset($transaction)):
$properties = $properties->whereHas('transaction_type', function ($query) use($transaction) {
$query->where('transaction_type', $transaction);
});
endif;
if(isset($city_id)):
$properties = $properties->where('city_id', $city_id);
endif;
if(isset($area_id) && isset($city_id)):
$properties = $properties->where('area_id', $area_id)
->where('city_id', $city_id);
elseif($area_id && !$city_id):
return "Select a city to get a zone!";
endif;
if(isset($city_id) && isset($street_id)):
$properties = $properties->where('street_id', $street_id)
->where('city_id', $city_id);
elseif(!isset($city_id) && isset($street_id)):
return "Select a city to get a street!";
endif;
return view('property.properties', ['properties' => $properties->paginate(2)]);
}
The main problem is the code is too long, not DRY and maybe hard to understand. The first thing I would like to do is to refactor this code. Please can you give me a hint on how to start this?
Maybe use a:
- custom class?
- middleware?
- a helper?
Is there any tip or trick from laravel to make my controller slim?
Please or to participate in this conversation.

