I’m having trouble how to implement a search functionality on a website I’m working on.
The search form has two fields, starting date and an end date which a user wants to rent a vehicle.
In the database I have two tables, one for the vehicle and another one for available dates.
Table: cars
- car_id (int)
- name (varchar)
- description (varchar)
Table: cars_availability
- car_id (int)
- date (datetime)
- status (int) // 1: available 2: booked
Each row in cars_availability represent a day that the associated car is available for rent or booked.
Problem:
If user searches for a car that is available from 5.feb.2013 to 8.feb.2013 I would like to do a query to select all cars that are available for all the days in the users selection.
My current unfinished solution:
I know I can iterate through all the days using something like this
$begin = new \DateTime( date('Y-m-d', strtotime($data['rent_start'])));
$end = new \DateTime( date('Y-m-d', strtotime($data['rent_end'])));
$end = $end->modify( '+1 day' );
$interval = \DateInterval::createFromDateString('1 day');
$period = new \DatePeriod($begin, $interval, $end);
$available_cars = array();
foreach ( $period as $dayTime ){
// query the cars and joining the cars_availabilty table
$item = DB::select('cars.*')
->from('cars')
->join('cars_availability')->on('cars_availability.item_id', '=', 'cars.item_id')
->where('items_availability.date', '=', $dayTime->format("Y-m-d H:i:s"))
->and_where('items_availability.status', '=', 1)
->execute()->as_array();
if(count($item) > 0){
if(isset($available_cars[$item[0]['item_id']])){
$available_cars[$item[0]['item_id']][] = $item[0];
}else{
$available_cars[$item[0]['item_id']] = array();
$available_cars[$item[0]['item_id']][] = $item[0];
}
}
}
After this I would have an array of vehicles that might have all the days available but I would need to do some extra math to find that out.
I want know if there is some more elegant solutions to this problem, using a better query maybe.
If you can give me any feedback on this I would be very grateful.
Thanks
This is actually a fairly sane setup to query. Personally, I’d recommend following @inhan’s advice, especially if it turns out cars can be booked for smaller slices of time (hours?). However, the queries get a little bit more complicated to write, because of edge-cases (if you look around here, you should find relevant examples).
Unfortunately, I can’t speak to how this fits into your framework, but the SQL can look like this:
(Have an SQL Fiddle Example)
This query assumes that there’s only one entry per day, or this breaks. Basically, it grabs every ‘available’ row greater-than/equal the beginning date, and less than the ‘end’ date (date/time/timestamps are… complicated; this article deals with SQL Server-specific problems, but the basic concepts apply), and makes sure there’s one for the count of days between them. Mostly, you were looking for the
HAVINGclause.Given the proper index on
Cars_Availability, it may not even hit the table, making this a good performer, too.