I got an idea to change the way I currently schedule appointments. Currently, I have an officehours table with 1 record per appointment time per day, as such weekday, appt_time, type_id, provider_id
I was thinking it might be better to have the officehours table instead have 1 record per day instead, with opening_time, closing_time, weekday, provider_id
I also have a closed table, which has open(time), close(time), closed (boolean), provider_id.
Then I have the appts table which saves appt_date, appt_time, client_id, appt_type_id, provider_id, etc
What I did in the past was create arrays of upcoming dates, booked appointments, officehours, and closures. I'd loop through the dates, and filter the arrays to match and run my logic. Anything that wasn't booked or closed would be pushed into an available array and I'd display that. Seems complicated, and it was.
I was wondering if it would be better to use Carbon dates, starting with the first day of the current week, and adding a day until 2 weeks of appointments is done. It's a lot of nested loops, though.
for each day, I have each provider (loop) and for each provider I'd use Carbon to start with the opening time and add the appointment if available and skip 10 minutes or the length of the appointment that is already booked before moving on to the next time.
This way I was using Eqoquent Collections instead of standard arrays, and I'm feeling as though they are slower and more difficult to work with but I'll stick it out if someone who knows better can help me understand.
Anyone have a great idea of how to make this happen in a great way (dare I say best)? I have a hard time simplifying things.
My old code:
public function index(Request $request)
{
$type = str_contains($request->path(), 'calendar') ? 1 : 2;
$doc = str_contains($request->path(), 'drdave') ? 2 : 1;
// getDates($displayWeeks, $daysInWeek) default 2, 4
$settings = Setting::where('type','public')->first();
$days = CalendarController::getDates($settings->weeks_visible ?: 2, $settings->days_in_week ?: 6);
$display = CalendarController::getApptCalendarData($days, $type, $doc);
return view('calendar', ['display'=>$display, 'daysInWeek'=>$settings->days_in_week, 'type'=>$type]);
}
// returns a list of days to be displayed on the calendar
public function getDates($displayWeeks = null, $daysInWeek = null) {
$today = date('Y-m-d',strtotime("today")); // today’s date
$thisday = date('w',strtotime("today")); // today’s weekday
$lastmon = date('m/d/Y',strtotime("last monday")); // last monday
$nextmon = date('m/d/Y',strtotime("next monday")); // next monday
$now = date("h:i:s"); // current time
$days = [];
// DETERMINE STARTING MONDAY
if ($thisday < 1 || $thisday > 6) // today is Sun, Fri, Sat
{
$day0 = date('m/d/Y',strtotime('-1 day', strtotime($nextmon)));
} elseif ($thisday > 1) // today is Tue, Wed, Thu, Fri
{
$day0 = date('m/d/Y',strtotime('-1 day', strtotime($lastmon)));
} else {
$day0 = date('m/d/Y',strtotime('-1 day', strtotime($today)));
}
// GENERATE LIST OF DATES
$displayWeeks = $displayWeeks ? $displayWeeks : 2; // # of weeks shown
// 4 = M-TH, 5 = M-F, 6 = M-Sat, 7 = full week
$daysInWeek = $daysInWeek ? $daysInWeek : 4;
$showSunday = $daysInWeek === 7 ? true : false;
// create array of dates to display
for ($w = 1; $w <= $displayWeeks; $w++) {
for ( $i = 0; $i < $daysInWeek; $i++ ) {
$x = $showSunday === false ? $i + 1 : $i;
$day = strftime("%Y-%m-%d", strtotime( '+' . $x .' day', strtotime($day0)));
array_push($days, $day);
}
// change to the next week
$day0 = strftime("%Y-%m-%d", strtotime( '+7 days', strtotime($day0)));;
}
return $days;
}
// returns list of appointments for doc (1 for Mel, 2 for Dave)
public function getApptCalendarData($days, $type = 0, $doc = 1) {
$start = $days[0];
$end = $days[count($days)-1];
// $closed = Closed::select('*')->where(['closed_date' >='$start', 'closed_date' <= '$end'])->orderBy('closed_date','ASC');
$closed = DB::select("SELECT AM_PM_DAY, closed_date, reason, open, close, date_format(open,'%l:%i %p') as opentime, date_format(close,'%l:%i %p') as closetime from closed WHERE closed_date >='$start' AND closed_date <= '$end' ORDER BY closed_date ASC");
$appointments = DB::select("SELECT appt_date, appt_time, appt_note, appt_reminder, reminder_cell, date_format(appt_time,'%l:%i %p') as time, patient_id, patient.id, patient.nickname, month(appt_date), year(appt_date), appt_type_id, appt_type.id, appt_type.appt_abbr, appt_type.appt_type_name, appt.id as appt_id, appt_status_id FROM appt, patient, appt_type WHERE appt.patient_id = patient.id and appt_date >= '$start' AND appt_date <= '$end' AND appt.appt_type_id = appt_type.id AND appt_status_id NOT IN ( '" . implode( "', '" , $this->RECALL ) . "' ) ORDER BY appt_date, appt_time ASC");
$available = $type > 0 ? DB::select("SELECT appt_time, dayslot, doctor_id, type, date_format(appt_time,'%l:%i %p') as time from officehour WHERE type = '$type' ORDER BY dayslot asc, appt_time asc") : DB::select("SELECT appt_time, dayslot, doctor_id, type, date_format(appt_time,'%l:%i %p') as time from officehour ORDER BY dayslot asc, appt_time asc");
$display = [];
// create an array of objects to display of date, closedInfo, appointments, and availability for each day
foreach($days as $day) {
$item = new class{};
$item->day = $day;
$item->isAdmin = false;
$item->today = date('Y-m-d',strtotime("today")) === date('Y-m-d', strtotime($day)); // today’s date
$item->show = date('Y-m-d', strtotime($day)) >= date('Y-m-d', strtotime('today'));
$item->displayDay = date("l F jS", strtotime($day));
$dayofweek = date('N',strtotime($day)); // for dayslot of office hours
$c = $closed;
$closedToday = array_filter(
$c,
function ($e) use (&$day) {
return $e->closed_date == $day;
}
);
$item->isclosed = count($closedToday) === 0 ? false : true;
if($item->isclosed === true) {
$item->closedInfo = head($closedToday);
$item->closedInfo->message = $item->closedInfo->AM_PM_DAY === 'DAY' ? "Closed Today" : ($item->closedInfo->AM_PM_DAY === 'AM' ? 'Morning Closed' : 'Afternoon/Evening Closed');
}
// filter booked appointments for this day
$existing = array_filter(
$appointments,
function ($e) use (&$day) {
return $e->appt_date == $day;
}
);
// LIST TIMES BOOKED FOR COMPArISON
$existing_times = array_column($existing, 'appt_time');
// office hours for this day of the week
$item->hours = array_filter(
$available,
function ($e) use (&$dayofweek) {
return $e->dayslot == $dayofweek;
}
);
// DISPLAY ARRAYS
$item->availability = [];
$item->adminAppts = [];
$appts = [];
// CHECK FOR AVAILABLE OR BOOKED, AND FILL LISTS
foreach($item->hours as $hour) {
// IS THE TIME BOOKED?
$freetime = (in_Array($hour->appt_time, $existing_times) === true) ? false : true;
// ADD the existing appt to the list
if($freetime === false && count($existing) > 0) {
// add this item to the admin appts list
$pushed = array_shift($existing);
array_push($appts, $pushed);
}
// CHECK IF TIME IS OPEN
$opentime = $item->isclosed === false ? true : false;
if($item->isclosed === true) {
$openAMPMDAY = (($item->closedInfo->AM_PM_DAY !== substr($hour->time, -2)) && ($item->closedInfo->AM_PM_DAY !== 'DAY'));
$openHOURS = (($hour->appt_time >= $item->closedInfo->open && $hour->appt_time <= $item->closedInfo->close));
$opentime = ($openHOURS === true && $openAMPMDAY === true);
}
// ADD THE OPEN TIME TO THE LIST
if($freetime === true && $opentime === true ) {
array_push($appts, $hour);
}
}
// ADD ANY REMAINING APPOINTMENTS TO THE LIST
foreach($existing as $remaining) {
array_push($appts, $remaining);
}
// sort the appointments by time
usort($appts, fn($a, $b) => strcmp($a->appt_time, $b->appt_time));
// split the appointments into 2 lists for column display
$count = ceil(count($appts) /2);
array_push($item->adminAppts, array_splice($appts, 0, $count, true));
array_push($item->adminAppts, array_diff_key($appts, $item->adminAppts));
// array_push($item->adminAppts, $session);
array_push($display, $item);
}
return $display;
}
To give you an idea of how it looks, https://adjust-me.com/calendar for existing, or https://adjust-me.com/newbies for new people. What it's NOT doing is displaying the closure info for each doctor with their appointments, the labeling sucks, and I need separate schedules depending on the type of appointment.
I was hoping to make things simpler for clients and allow them to book any time in the range, but newbies are 60 minutes so I'd need to switch from 1 record per time slot to make that work. That said I don't want it to lag.
I need to update month_no fields of the records resulting from a select query using some variables passed from controller but every field is written with the same value.
These are the variables:
$id_calc = 27
$from_y = "2013-05-01"
$to_y = "2015-11-31"
The table result:
id id_paid year_ref month_no
1 26 2012 12
2 26 2013 12
4 27 2013 12 (should be 8)
5 27 2014 12
6 27 2015 12 (should be 11)
This is model:
public function CorrectTable($id_calc, $from_y, $to_y)
{
$connection = Yii::$app->db;
$query = "SELECT * FROM my_table where id_paid='$id_calc'";
$data = $connection->createCommand($query)->queryAll();
// calculate no. of month first and last year
$month_no_begin = (13 - date("m",strtotime($from_y)));
$month_no_end = (date("m",strtotime($to_y)));
//
foreach($data as $row)
{
$id = $row['id'];
// calculate number of month per year
if ($row['year_ref'] = date("Y",strtotime($from_y)))
{
$month_no = $month_no_begin;
} elseif ($row['year_ref'] = date("Y",strtotime($to_y)))
{
$month_no = $month_no_end;
} else
{
$month_no = 12;
}
Yii::$app->db->createCommand()
->update('my_table', ['month_no' => $month_no], ['id' => $id])
->execute();
}
I tried to put the above code in controller with the same result.
A careless mistake once I had made before. You should use == or === in the if statement to test equal condition, instead of =
public function CorrectTable($id_calc, $from_y, $to_y)
{
$connection = Yii::$app->db;
$query = "SELECT * FROM my_table where id_paid='$id_calc'";
$data = $connection->createCommand($query)->queryAll();
// calculate no. of month first and last year
$month_no_begin = (13 - date("m",strtotime($from_y)));
$month_no_end = (date("m",strtotime($to_y)));
//
foreach($data as $row)
{
$id = $row['id'];
// use `==` instead of `=`
if ($row['year_ref'] == date("Y",strtotime($from_y)))
{
$month_no = $month_no_begin;
}
// use `==` instead of `=`
elseif ($row['year_ref'] == date("Y",strtotime($to_y)))
{
$month_no = $month_no_end;
}
else
{
$month_no = 12;
}
Yii::$app->db->createCommand()
->update('my_table', ['month_no' => $month_no], ['id' => $id])
->execute();
}
}
our if ... esleif ... else sequence seems not logig
as you done the else if is never executed
so you should change the condition this way the e
foreach($data as $row)
{
$id = $row['id'];
// assigne default value
$month_no = 12;
// check for month_begin
if ($row['year_ref'] == date("Y",strtotime($from_y)))
{
$month_no = $month_no_begin;
}
//check for month_end
if ($row['year_ref'] == date("Y",strtotime($to_y)))
{
$month_no = $month_no_end;
}
Yii::$app->db->createCommand()
->update('my_table', ['month_no' => $month_no], ['id' => $id])
->execute();
}
I have a Reservation model which has date_from and date_to fields.
How can reservations be grouped by one day interval, so it is possible to know how many of them are active on a particular date?
Example
Reservation::groupBy('...')->get()
Output:
reservations
==================
qty | date
------------------
3 | 2000-01-01
5 | 2000-01-02
2 | 2000-01-03
...
First add "date_from" and "date_to" to $dates array on model
protected $dates = ['date_from', 'date_to'];
Then
$min_date = Reservation::select(DB::raw('MIN(date_from) as min'))->first();
$max_date = Reservation::select(DB::raw('MIN(date_to) as max'))->first();
$data = [];
if (!is_null($min_date) && !is_null($max_date)) {
$daterange = new DatePeriod(DateTime::createFromFormat('Y-m-d H:i:s', $min_date->min), new DateInterval('P1D') ,DateTime::createFromFormat('Y-m-d H:i:s', $max_date->max));
$reservations = Reservation::all();
foreach ($daterange as $date) {
$data[$date->format('Y-m-d')] = $reservations->filter(function($reservation) use($date) {
$carbon_date = Carbon::instance($date)->endOfDay();
return ($reservation->date_from->startOfDay()->lte($carbon_date) && $reservation->date_to->endOfDay()->gte($carbon_date));
})
->count();
}
}
I am developing a hotel room reservation system, There having only 5 rooms. I want to get the date periods if 5 or more bookings are already done.
Example:
+-------------------------------------------------------------+
|Booking_from_date | Booking_to_date | Number_of_booking_rooms|
+------------------+-----------------+------------------------+
| 2013-01-01 | 2013-01-10 | 3 |
+------------------+-----------------+------------------------+
| 2013-01-06 | 2013-01-15 | 2 |
---------------------------------------------------------------
(now there are total 5 room booked between 2013-01-06 to 2013-01-10, so i want to get this date period).
I tried using MySql, but not achieved yet. Is it possible to create a query like this?
There may a different simpler way, but what i can think of is to split the days and group it yo get sum.
select date_add(a.booking_from_date, interval col1 day),
sum(Number_of_booking_rooms) from
(select * from table1)a,
(select 0 col1 union all
select 1 col1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7 union all
select 8 union all
select 9) b
where date_add(a.booking_from_date, interval col1 day) <= a.booking_to_date
group by date_add(a.booking_from_date, interval col1 day)
having sum(Number_of_booking_rooms) >= 5
http://sqlfiddle.com/#!2/87813/15
This is the result i got.
DATE SUM
January, 06 2013 00:00:00+0000 5
January, 07 2013 00:00:00+0000 5
January, 08 2013 00:00:00+0000 5
January, 09 2013 00:00:00+0000 5
January, 10 2013 00:00:00+0000 5
I guess this is not possible from just MySql query.But I have a solution by using loop. Please check this code, you will get the output which you want.
/* all active booking from mysql table. */
$this -> data['booked_dates'] = $this -> admin_model -> get_booked_dates_from();//(just a function to get all dates from table)
$array_index = 0;
$not_available_date_range = array();
//declaration
$not_available_date = array();
foreach ($this -> data['booked_dates'] as $key => $booked_date)// loop for each booking dates(each table row).
{
//flag to know package is to be blocked or not...
$block_status = 0;
$check_in_min_date = 0;
$check_out_max_date = 0;
$strDateFrom = $booked_date -> check_in_date;
$strDateTo = $booked_date -> check_out_date;
// Takes two dates, formatted in YYYY-MM-DD.
// Inclusive array of the dates between the from and to dates.
$aryRange = array();
$iDateFrom = strtotime($strDateFrom);
$iDateTo = strtotime($strDateTo);
if ($iDateTo >= $iDateFrom)//just a validation
{
array_push($aryRange, date('Y-m-d', $iDateFrom));
// first entry
$date_gap = 0;
while ($iDateFrom <= $iDateTo)//select each date...between check-in date and check-out date. one by one
{
$date_gap++;
$booking_count = 0;
for ($i = $key; $i <= (count($this -> data['booked_dates']) - 1); $i++)// loop for each booking. // loop have to reduce size. by using good logic..:)
{
$strDateFrom2 = strtotime($this -> data['booked_dates'][$i] -> check_in_date);
$strDateTo2 = strtotime($this -> data['booked_dates'][$i] -> check_out_date);
if (($iDateFrom >= $strDateFrom2) && ($iDateFrom <= $strDateTo2))
{
$booking_count = $booking_count + $this -> data['booked_dates'][$i] -> rooms;
}
}
if ($booking_count >= 5)// compare with maximum available rooms.
{
$block_status = 1;
//room should be blocked.
if ($check_in_min_date == 0)
{
$check_in_min_date = $iDateFrom;
$check_out_max_date = $iDateFrom;
//For if only one day is going to block.
}
else
{
$check_out_max_date = $iDateFrom;
//datefrom is incremented till the maximum booked date.
}
array_push($not_available_date, date('Y-m-d', $iDateFrom));
}
$iDateFrom += 86400;
// add 24 hours
}
}
else
{
echo 'Wrong input!';
}
if ($block_status)//if the room is blocked..
{
$not_available_date_range[$array_index]['check_in_date'] = date('Y-m-d', $check_in_min_date);
$not_available_date_range[$array_index]['check_out_date'] = date('Y-m-d', $check_out_max_date);
$array_index++;
}
}
$this -> data['blocked_dates'] = $not_available_date_range; // Now we have booking period where more than 5 bookings are done.
I'm using Codeigniter V2.1.3 and I have a calendar template that have a scheduling stuff and I want to count the events in a particular date. Here is my database:
----Table: schedule -----
id | materialID | borrowerID | date_reserve |
------------------------------------------------
1 | 7 | 7 | 2013-08-16 |
2 | 10 | 10 | 2013-08-16 |
1 | 12 | 13 | 2013-08-18 |
What I want is in my calendar template the total event for the date=2013-08-16 will be 2 events. Here is my code which is not working coz it keeps on sending me only 1 event maybe you could figure out where is my mistake in here:
$query = $this->db->select('*')->from('schedule')->like('date_reserve', "$year-$month")->get();
$cal_data = array();
foreach ($query->result() as $row) {
$index = ltrim(substr($row->date_reserve,8,2), '0');
$cal_data[$index] = count($row->borrowerID). 'event(s)';
}
return $cal_data;
Any help?
If you want number of events for exact date: YYYY-MM-DD you can replace like with where:
$r = $this->db->select('id, materialID, borrowerID, date_reserve')
->where('date_reverse', $year."-".$month."-".$day)
->get('schedule')
->result();
Also you can print_r the result so you will see what the result is. Also you can echo last query: echo $this->db->last_query();
Edit:
We had to run query per each date to fetch the number of events.
$cal_data = array();
for($i=1;$i<=31;$i++)
{
$this->db->select('COUNT(id) as count,date_reserve,borrowerID');
$this->db->from('schedule');
$this->db->where('date_reserve',"$year-$month-$i");
//$this->db->group_by('date_reserve');
$query = $this->db->get();
foreach ($query->result() as $row) {
$cal_data[$index] = $row->count. 'event(s)';
}
try using group_by
public function get_results($date) {
$this->db->select('COUNT(id),date_reserve');
$this->db->from('schedule');
$this->db->like('date_reserve',$date);
$this->group_by('date_reserve');
$result = $this->db->get();
return $result->result_array();
}