The appointment is valid iff the following conditions are met:
the appointment is within the user's work hours; and
it does not clash with an existing appointment, which can happen in three possible ways:
the clashing appointment starts during the new appointment; and/or
the clashing appointment ends during the new appointment; or
the clashing appointment starts before and ends after the new appointment.
You can query for this in pure SQL with something along the following lines:
SELECT EXISTS (
SELECT *
FROM appointments a JOIN workhours h USING (user_id)
WHERE user_id = 1
AND h.start <= '08:00:00' AND h.end >= '09:00:00'
AND (
a.start BETWEEN '08:00:00' AND '09:00:00'
OR a.end BETWEEN '08:00:00' AND '09:00:00'
OR (a.start < '08:00:00' AND a.end > '09:00:00')
)
LIMIT 1
)
Beware to perform this SELECT
using a locking-read within a transaction in order to prevent concurrency issues.