Laravel avoid overlapping dates - laravel

I am new to Laravel and am trying to implement reservation dates that can not overlap on any day.
I have a model called 'Bookings' that includes the room_id, start_date and end_date.
I have validation that checks that the end date can not be before the start:
$this->validate($request, [
'start_date' => 'required|date',
'end_date' => 'required|date|after_or_equal:start_date',
]);
However I am not sure how to check that the date range does not conflict with any other date ranges of the same room_id stored in the bookings table (as one room can not be booked twice in the same range).
Any help would be appreciated,
Thanks!

In a typical overlaping, given two events A and B, you have to consider four scenarios:
Event A contains event B
Event B contains event A
End date of event A overlaps with start date of event B
End date of event B overlaps with start date of event A
So, in sql to check if a room is busy in a interval of dates, lets say 2018-11-20' and '2018-11-30' it would be:
select *
from rooms
where
start_date between '2018-11-20' and '2018-11-30' or
end_date between '2018-11-20' and '2018-11-30' or
'2018-11-20' between start_date and end_date or
'2018-11-30' between start_date and end_date;
In laravel what I do is to create a scope in the model.
public function scopeByBusy($query,$start_date,$end_date)
{
return $query->whereBetween('start_date', [$start_date, $end_date])
->orWhereBetween('end_date', [$start_date, $end_date])
->orWhereRaw('? BETWEEN start_date and end_date', [$start_date])
->orWhereRaw('? BETWEEN start_date and end_date', [$end_date]);
}
Finally, I can verify if the room with the id 95 is busy between '2018-11-20' and '2018-11-30' like this:
$room_id = 95;
$start = '2018-11-20';
$end = '2018-11-30';
$exists = Rooms::where('id', $room_id)
->byBusy($start, $end)
->first();
if($exists)
{
echo "the room is busy in the interval you selected";
}
I hope it helps you.

I had a similar usage where i needed to check if person has already scheduled something in date range
An example laravel code below :
$start = Carbon::parse($request['start_date'])->format('Y-m-d 00:00:00');
$end = Carbon::parse($request['end_date'])->format('Y-m-d 23:59:59');
$existsActive = Microsite::where(function ($query) use ($start) {
$query->where('start_date', '<=', $start);
$query->where('end_date', '>=', $start);
})->orWhere(function ($query) use ($end) {
$query->where('start_date', '<=', $end);
$query->where('end_date', '>=', $end);
})->count();
if($existsActive > 0 ){
echo "Already exists";
}

There's built-in Carbon method isPast() so you can use:
$start_date->isPast()
$end_date->isPast()
You can check If both selected were booked in past.
If you haven't used Carbon this is the link:
https://carbon.nesbot.com/docs/

if(DB::table('rooms')->whereBetween('start_date', [$request->start_date, $request->end_date])
->orwhereBetween('end_date',[$request->start_date, $request->end_date])->exists())
{
echo "the room is busy in the interval you selected";
}

You have to check that in database. So you can do this:
$result = Bookings::where('start_date', '<=' $request->start_date)->where('end_date', '>=' $request->end_date)->where('room_id',$request->room_id)->first();
if(!$result){
// Code to Book room
}

Related

How can I write Laravel eloquent query (inner query) for this plain SQL query

SELECT *
FROM (
SELECT *
FROM appointments
WHERE `date` <= Curdate()
) `t`
WHERE `t`.`end_time` <= current_time
How can I write a Laravel eloquent equivalent query for this SQL query?
If you want to query:
all past appointments (from start to yesterday, based on date field) +
all today appointments that have end_time not past (or exactly now) for today
And assuming you have a proper Model for the appointment table you could try something like :
Appointments::where('appointments.date', '<', Carbon::now()->toDateString())
->orWhere(function ($query) {
$query->where('appointments.date', Carbon::now()->toDateString())
->where('appointments.end_time', '<=', Carbon::now()->toTimeString());
});
You may need to test the Carbon format used in where clauses to be sure it matches your database storage format, and the query return expected results.
I am not sure whether this suit you, you can try it:
Appointment::whereIn('id', function($query) {
$query->select('id')
->from('appointment')
->where('date', '<=', DB::raw('CURDATE()'));
})
->where('end_time', '<=', DB::raw('CURRENT_TIME()'));
Try this i hope you got your solution .
Appointment::whereExists(function ($query){ $query->selectRaw(1) ->from((new
Appointment())->getTable()) ->whereDate("date",'<=',now()->format('Y-m-d')); })-
>whereDate("end_time",'<=',now()->format('H:i:s'))->get()

How to get last 7 days records with 0 counts

I have an eloquent query that gets the total count records (created_at) of the last 7 days. But the problem is if one of these days have 0 records, this doesn't appear in the final data.
My query:
$data = Data::whereBetween('created_at', [Carbon::now()->subDays(6)->format('Y-m-d')." 00:00:00", Carbon::now()->format('Y-m-d')." 23:59:59"])
->groupBy('date')
->orderBy('date')
->get([
DB::raw('DATE(created_at) as date'),
DB::raw('count(*) as total')
])
->pluck('total', 'date')->toArray();
What I get:
[
"2020-04-14" => 1
"2020-04-16" => 1
"2020-04-18" => 1
"2020-04-19" => 1
]
What I expected:
[
"2020-04-14" => 1
"2020-04-15" => 0
"2020-04-16" => 1
"2020-04-17" => 0
"2020-04-18" => 1
"2020-04-19" => 1
"2020-04-20" => 0
]
Any suggestions?
SOLUTION:
-Based on Gary Houbre's proposal:
$results = Data::whereBetween('created_at', [Carbon::now()->subDays(6)->format('Y-m-d')." 00:00:00", Carbon::now()->format('Y-m-d')." 23:59:59"])
->groupBy('date')
->orderBy('date')
->get([
DB::raw('DATE_FORMAT(created_at, "%Y-%m-%d") as date'),
DB::raw('count(*) as total')
])
->keyBy('date')
->map(function ($item) {
$item->date = Carbon::parse($item->date);
return $item;
});
$period = new DatePeriod(Carbon::now()->subDays(6), CarbonInterval::day(), Carbon::now()->addDay());
$graph = array_map(function ($datePeriod) use ($results) {
$date = $datePeriod->format('Y-m-d');
return $results->has($date) ? $results->get($date)->total : 0;
}, iterator_to_array($period));
Looking directly Sql : How to include "zero" / "0" results in COUNT aggregate?
Into a same table : How to get the record if Count is zero in Laravel
You need to add an outer join into your request with Eloquent.
My idea is to create a for loop to check the days.
If there is no record on a date then print 0
Loop Iteration:
Catch the first Day (Suppose 14)
Catch the last Day
Then check in every iteration it is greater than one or many
Thus, I hope you will get normally.
We had a similar problem while trying to put back-end data into the chart. Since some of the days were missing it didn't look well. Our solution was;
Create a function like this;
public function generateDates(Date $startDate, Date $endDate, $format = 'Y/m/d'): Collection
{
$dates = collect();
$startDate = $startDate->copy();
for ($date = $startDate; $date->lte($endDate); $date->addDay()) {
$dates->put($date->format($format), 0);
}
return $dates;
}
In your case it's going to be (today and today - six days) and you will union returning collection with your query collection. What it does is; it create a date range from the keys and fill them with zero. When your query collection has some value other than zero - it is going to overwrite it.

Laravel carbon retrieve record even when it shouldn't

I am searching by start and end date or with a weekday, which kind of works fine however if weekday is equal to 'WE' and today's weekday is equal to 'WE' it still brings back results even when $today date is < than endate.
public function show($id)
{
$weekMap = [
0 => 'SU',
1 => 'MO',
2 => 'TU',
3 => 'WE',
4 => 'TH',
5 => 'FR',
6 => 'SA',
];
$todayWeek = Carbon::now()->dayOfWeek;
$today= Carbon::now();
$weekday = $weekMap[$todayWeek];
$event = Event::with('businesses')
->where('startdate', '<', $today->format('Y-m-d'))
->where('endate', '>', $today->format('Y-m-d'))
->orWhere('weekday', '=', $weekday)
->get();
dd($event);
return view('events.showEvent', compact('event'));
}
If I change orWhere to where, results are all good, however user not always enters a weekday so it can be empty. I want it to work like this:
output everything where startdate < today and where endate > today and if weekday is not empty, output everything where startdate < today, endate > today and weekday = $weekday. I hope that makes sense.
//edit
sample data:
data
So what I want is really search by frequency so it would look like this:
if frequency = daily
compare start and end with today's date.
if frequency = weekly
compare start and end with today's date and weekday.
bring back all results for frequency = daily+weekly
you can use if condition in query like this:
$query = Event::with('businesses')
->where('startdate', '<', $today->format('Y-m-d'))
->where('endate', '>', $today->format('Y-m-d'));
if ($weekday != '') {
$query->where('weekday', $weekday);
}
$event = $query->get();
also
condition if ($weekday != '') check $weekday isset or not you can use
if ( ! is_null($weekday )) instead

Select with advanced where clauses and timestamp using Carbon and DB

I have a code to generate a SQL query from a table.
I want to select items that exist between dates and a true value in another field.
DB use in construction and Carbon facades, following advice on how to work with Carbon in laravel 5.
But I do not get the effect I returns all rows
private function runBids() {
$dt = Carbon::parse(Config::get('constants.start_lot'));
$start = $dt->toDateTimeString(); // 2006-05-08 08:34:59
$end = $dt->addDay()->startOfDay(); // 2006-05-09 00:00:00
$lots = DB::table('lots')
->select('id')
->where('end',false)
->where('end_auction', '<=', $end)
->where('end_auction', '=>', $start) // Not work. Return 0 results
// if comment ->where('end_auction', '=>', $start) result 39 results with date
// between dates
// (start it's date of first element of table order by end_auction)
->get();
$lots_id = array();
foreach ($lots as $value){
$lots_id[] = $value->id;
}
dd($lots_id);
}
It all seems correct, except for the operator used on the $start parameter. You have
->where('end_auction', '=>', $start)
And you should have
->where('end_auction', '>=', $start)
Notice the difference between => and >=. The first throws a MySQL error. You could try to wrap that code around a try ... catch block, and check the exception message.
You can also log the executed queries using one of this answers, to check the executed query whenever you don't get the expected results.

Laravel 4.2 Eloquent get posts (events) between dates

Lets say I have event which start date is 2015-07-30 (today) and end date is 2015-08-01. Ok, I want to see tomorrow events. How show this event at 2015-07-31?
I already tried with this:
$tomorrow = Carbon::tomorrow(); // 2015-07-31 00:00:00
$dayAfterTomorrow = Carbon::tomorrow()->addDay(); // 2015-08-01 00:00:00
$events = $events->where(function ($query) use ($tomorrow, $dayAfterTomorrow) {
$query->orWhereBetween('start_date', [$tomorrow, $dayAfterTomorrow]);
$query->orWhereBetween('end_date', [$tomorrow, $dayAfterTomorrow]);
});
That query doesn't show event for tomorrow date, because tomorrow day is 2015-07-31 and event end date is 2015-08-31. Maybe I need to use if clauses? If it even possible
$events = $events->where(function ($query) use ($start, $end) {
$query->where('start_date', '<', $end)
->where('end_date', '>=', $start);
});
All events where the start_date is less than $end and the end_date is greater than or equal to $start.
If you count as $start = TOMORROW and $end = DAY_AFTER_TOMORROW than you can see that it will grab ALL events created before the $end and then it will only keep the ones where the end is greater than the $start. So events created on 2014-04-01 and end on 2015-07-31 will still be included. But events created for 2015-08-01 and end later than that will not.

Resources