mcadio's avatar
Level 2

Help simplifying office hours db structure

I’m re-structuring my app while upgrading and would like to use cron and model events to handle available appointment slots.

Here are the relevant tables I do have: officehour (open, close, dayofweek, is_closed) There is one record for every day of the week, but I most often am only open on weekdays.

modified-hours (date, officehour id, open, close, is_closed, nps, and reason)

session_hours (date, office hour is, modified-hour-id, open, close, is_closed, reason, nps and header)

day (id and date)

There can be many sessions for one day, however if there are modified hours, those will override the office hours.

If the modified hours are removed and there are no sessions listed for that date, it falls back to the office hour record.

What is the best and simplest way to do this? I feel like this could be simplified, but I’m really struggling with the how.

Appointments would simply be booked from the available status appointments, and can be put back into the pool on rescheduling or canceling.

0 likes
4 replies
LaryAI's avatar
Level 58

To simplify your office hours database structure while ensuring it meets your requirements, you can streamline the tables and relationships. Here's a proposed structure and approach:

Proposed Database Structure

  1. office_hours: This table will store the default office hours for each day of the week.
  2. modified_hours: This table will store any modifications to the default office hours for specific dates.
  3. sessions: This table will store the session details for each date.
  4. days: This table will store the dates.

Table Definitions

office_hours

CREATE TABLE office_hours (
    id INT AUTO_INCREMENT PRIMARY KEY,
    day_of_week TINYINT NOT NULL, -- 0 = Sunday, 1 = Monday, ..., 6 = Saturday
    open TIME NOT NULL,
    close TIME NOT NULL,
    is_closed BOOLEAN DEFAULT FALSE
);

modified_hours

CREATE TABLE modified_hours (
    id INT AUTO_INCREMENT PRIMARY KEY,
    date DATE NOT NULL,
    open TIME,
    close TIME,
    is_closed BOOLEAN DEFAULT FALSE,
    nps INT,
    reason VARCHAR(255)
);

sessions

CREATE TABLE sessions (
    id INT AUTO_INCREMENT PRIMARY KEY,
    date DATE NOT NULL,
    open TIME NOT NULL,
    close TIME NOT NULL,
    is_closed BOOLEAN DEFAULT FALSE,
    reason VARCHAR(255),
    nps INT,
    header VARCHAR(255)
);

days

CREATE TABLE days (
    id INT AUTO_INCREMENT PRIMARY KEY,
    date DATE NOT NULL UNIQUE
);

Logic for Handling Office Hours

  1. Default Office Hours: Use the office_hours table to get the default hours for each day of the week.
  2. Modified Hours: Check the modified_hours table for any modifications on a specific date. If a record exists, it overrides the default office hours.
  3. Sessions: Use the sessions table to store session details. If there are no sessions for a date and no modified hours, fall back to the default office hours.

Example Query to Get Office Hours for a Specific Date

SELECT 
    COALESCE(mh.open, oh.open) AS open,
    COALESCE(mh.close, oh.close) AS close,
    COALESCE(mh.is_closed, oh.is_closed) AS is_closed
FROM 
    days d
LEFT JOIN 
    modified_hours mh ON d.date = mh.date
LEFT JOIN 
    office_hours oh ON DAYOFWEEK(d.date) - 1 = oh.day_of_week
WHERE 
    d.date = '2023-10-10';

Example Query to Get Sessions for a Specific Date

SELECT 
    s.open,
    s.close,
    s.is_closed,
    s.reason,
    s.nps,
    s.header
FROM 
    sessions s
WHERE 
    s.date = '2023-10-10';

Using Cron and Model Events

  1. Cron Job: Set up a cron job to run daily and check for any modifications or sessions for the upcoming days. This job can update the days table with the relevant office hours or session details.
  2. Model Events: Use model events (e.g., created, updated, deleted) to handle changes in the modified_hours and sessions tables. When a record is added, updated, or deleted, update the corresponding entry in the days table.

Example Laravel Model Event

class ModifiedHour extends Model
{
    protected static function booted()
    {
        static::saved(function ($modifiedHour) {
            // Update the corresponding entry in the days table
            Day::updateOrCreate(
                ['date' => $modifiedHour->date],
                [
                    'open' => $modifiedHour->open,
                    'close' => $modifiedHour->close,
                    'is_closed' => $modifiedHour->is_closed
                ]
            );
        });

        static::deleted(function ($modifiedHour) {
            // Handle deletion logic
            $day = Day::where('date', $modifiedHour->date)->first();
            if ($day) {
                $day->delete();
            }
        });
    }
}

This structure and approach should simplify your database schema and make it easier to manage office hours, modified hours, and sessions.

jlrdw's avatar

however if there are modified hours, those will override the office hours.

I don't quite get that statement, do you mean if an appointment runs longer?

I wouldn't worry so much about "after the fact" slots as long as everyone was seen and billed correctly. Each new day is new slots.

mcadio's avatar
Level 2

@jlrdw I currently have a working booking calendar at 'https://innerhealer.org/calendar'. The problem is how complicated it is. I am going through and checking officehours and modified hours and then only showing times for the unbooked appointments (they aren't in the database, just a list of times from an array). It's slow, and I didn't know any other way to do it back then.

I currently use cron jobs and model events to calculate and adjust billing, since I don't have a front desk person. I was thinking it might be simpler to create unbooked appointment records, and then just grab them for the calendar. No massive calculations, looping, etc. just to see what's what.

I was wondering if I could simplify things, by using less tables or even by just adding an active (boolean) field. Then, I could inactivate the regular office hours if there is a different (modified) schedule that day. If I later change my mind and decide to go back to normal office hours, I can just delete the modified hours and activate the normal office hours.

The officehour table is what my normal everyday hours are. The modified hours table are for specific dates, where I may want to add an extra session, change the start or end times of a session, or even close it.

My brain leans towards complicated, so I try to ask and learn how to simplify so things get easier.

jlrdw's avatar

@mcadio

just a list of times from an array

Have you tried to use a table instead indexed and use it instead of an array? But I can't see why looping an array would be slow. Perhaps use a collection instead.

On the active (boolean) field, do you mean once booked a value of 1 instead of 0? 0 meaning appointment is available for the slot.

Edit:

How are you handling race conditions (more than one person trying to book at same time).

Like @tray2 always states, let the database do the work with tables. You could possibly make use of a database view here for the daily sessions availability times.

I agree it can be tricky especially with more than one doctor.

Edit 2

I noticed on Dr. Dave when you click the link July comes up, but not available til August. When clicking Dr. Dave, maybe add a quick DB lookup to determine what month to start with.

Please or to participate in this conversation.