/lib/Calendar.php

https://github.com/mnylen/salon-sulu-reservation-system · PHP · 434 lines · 166 code · 57 blank · 211 comment · 17 complexity · a819041b1d87e355a281e595b996d79f MD5 · raw file

  1. <?php
  2. require_once 'models/Employee.php';
  3. require_once 'models/WorkShift.php';
  4. require_once 'models/Reservation.php';
  5. class Calendar {
  6. /**
  7. * Duration of one cell in minutes. This should be something
  8. * 60 is dividable by.
  9. *
  10. * @var int
  11. */
  12. protected $cellDuration;
  13. /**
  14. * The dates covered by the calendar. Each date is represented
  15. * as Unix Epoch Time timestamp set to the midnight of the day
  16. * in question.
  17. *
  18. * @var array
  19. */
  20. protected $dates;
  21. /**
  22. * The employee this calendar is for.
  23. *
  24. * @var Employee
  25. */
  26. protected $employee;
  27. /**
  28. * The starting time of the first cell as seconds since midnight.
  29. *
  30. * @var int
  31. */
  32. protected $firstCellTime;
  33. /**
  34. * The ending time of the last cell as seconds since midnight.
  35. *
  36. * @var int
  37. */
  38. protected $lastCellTime;
  39. /**
  40. * A query iterator containing all reservations between the
  41. * first and last day of the calendar.
  42. *
  43. * @var QueryIterator
  44. */
  45. protected $reservations;
  46. /**
  47. * A query iterator containing all work shifts between the
  48. * first and last day of the calendar.
  49. *
  50. * @var QueryIterator
  51. */
  52. protected $shifts;
  53. /**
  54. * An two-dimensional array containing all cells for the calendar.
  55. * Keyed by the date.
  56. *
  57. * @var array
  58. */
  59. protected $cells = array();
  60. /**
  61. * How many seconds there is in one minute.
  62. *
  63. * @var int
  64. *
  65. */
  66. const SecondsInMinute = 60;
  67. /**
  68. * How many seconds there is in one hour.
  69. *
  70. * @var int
  71. *
  72. */
  73. const SecondsInHour = 3600;
  74. /**
  75. * How many seconds there is in one day.
  76. *
  77. * @var int
  78. */
  79. const SecondsInDay = 86400;
  80. /**
  81. * Creates a new Calendar between $startDate and $endDate for the specified
  82. * employee.
  83. *
  84. * @param Employee $employee
  85. * @param int $startDate
  86. * @param int $endDate
  87. */
  88. public function __construct(Employee $employee, $startDate, $endDate) {
  89. $this->employee = $employee;
  90. $this->dates = $this->createDates($startDate, $endDate);
  91. // Initialize and create cells
  92. $this->init();
  93. $this->createCells();
  94. }
  95. /**
  96. * Creates an array of dates that are between the given start date and
  97. * end date.
  98. *
  99. * @param int $startDate
  100. * @param int $endDate
  101. */
  102. private function createDates($startDate, $endDate) {
  103. $startDate = mktime(0, 0, 0, date('n', $startDate), date('j', $startDate), date('Y', $startDate));
  104. $endDate = mktime(0, 0, 0, date('n', $endDate), date('j', $endDate), date('Y', $endDate));
  105. $currentDate = $startDate;
  106. $dates = array();
  107. while ($currentDate < $endDate) {
  108. $dates[] = $currentDate;
  109. $currentDate += Calendar::SecondsInDay;
  110. }
  111. return $dates;
  112. }
  113. public function getDates() {
  114. return $this->dates;
  115. }
  116. /**
  117. * Gets the cells covered by the calendar as rows. Each row
  118. * in the returned array represent specific time period.
  119. *
  120. * @return array
  121. */
  122. public function getCellRows() {
  123. $cells = array();
  124. $i = 0;
  125. // Just transpose the cells array
  126. foreach ($this->dates as $date) {
  127. for ($j = 0, $jj = count($this->cells[$date]); $j < $jj; $j++)
  128. $cells[$j][$i] = $this->cells[$date][$j];
  129. $i++;
  130. }
  131. return $cells;
  132. }
  133. /**
  134. * Gets the cells for given date.
  135. *
  136. * @param int $date
  137. * @return array
  138. */
  139. public function getCells($date) {
  140. return $this->cells[$date];
  141. }
  142. /**
  143. * Initializes everything.
  144. *
  145. */
  146. private function init() {
  147. require 'config/settings.php';
  148. $this->cellDuration = $SETTINGS['time_period_duration'];
  149. $firstDay = $this->dates[0];
  150. $lastDay = $this->dates[count($this->dates)-1];
  151. // Find out the time of first and last cell
  152. $this->firstCellTime = WorkShift::findEarliestWorkComingTime($this->employee, $firstDay, $lastDay) * Calendar::SecondsInMinute;
  153. $this->lastCellTime = WorkShift::findLatestWorkingTime($this->employee, $firstDay, $lastDay) * Calendar::SecondsInMinute;
  154. // Load all reservations and working shifts for the period of the calendar
  155. $this->reservations = Reservation::findByEmployee($this->employee, time(), $lastDay);
  156. $this->shifts = WorkShift::findByEmployee($this->employee, $firstDay, $lastDay);
  157. }
  158. /**
  159. * Creates the cells.
  160. *
  161. */
  162. private function createCells() {
  163. foreach ($this->dates as $day) {
  164. $cells = array();
  165. $time = $day + $this->firstCellTime;
  166. while ($time <= ($day + $this->lastCellTime)) {
  167. $endTime = $time + ($this->cellDuration * Calendar::SecondsInMinute);
  168. $cell = new Cell($time, $endTime);
  169. // 1. Check availability of the employee
  170. $this->shifts->rewind();
  171. while ( ($shift = $this->shifts->next()) !== null ) {
  172. if ($time >= $shift->start_time && $endTime <= $shift->end_time) {
  173. $cell->setAvailable(true);
  174. $cell->setEmpty(true);
  175. break;
  176. }
  177. }
  178. if ($cell->isAvailable()) {
  179. // 2. Check whether the cell is reserved
  180. $this->reservations->rewind();
  181. while ( ($reservation = $this->reservations->next()) !== null ) {
  182. if ($time >= $reservation->start_time && $time < $reservation->end_time) {
  183. $cell->setEmpty(false);
  184. $cell->setReserved(true);
  185. $cell->setReservation($reservation);
  186. break;
  187. }
  188. }
  189. }
  190. $cells[] = $cell;
  191. $time = $endTime;
  192. }
  193. $this->cells[$day] = $cells;
  194. }
  195. }
  196. /**
  197. * Tags all reservable cells as reservable.
  198. *
  199. * A cell is considered reservable if it is empty and there's enough
  200. * cells below it to make a total duration of $reservationDuration.
  201. *
  202. * For example, if the calendar looks like this:
  203. * ----------------------------------
  204. * | Mon | Tue | Wed
  205. * |---------------------------------
  206. * 8:15 | EMPTY | EMPTY | OCCUPIED
  207. * 8:30 | EMPTY | OCCUPIED | EMPTY
  208. * 9:15 | OCCUPIED | EMPTY | EMPTY
  209. *
  210. * We want to tag all cells that can be the "starting cells" for 30 minutes
  211. * reservation. After $calendar->tagAllReservable(30) the calendar would
  212. * look like:
  213. *
  214. * ----------------------------------
  215. * | Mon | Tue | Wed
  216. * |---------------------------------
  217. * 8:15 | RESERVABLE | EMPTY | OCCUPIED
  218. * 8:30 | EMPTY | OCCUPIED | RESERVABLE
  219. * 9:15 | OCCUPIED | EMPTY | EMPTY
  220. *
  221. * Note that the empty cells not having another empty cell below
  222. * wont be tagged.
  223. *
  224. * @param int $reservationDuration Duration of the reservation in minutes
  225. */
  226. public function tagAllReservable($reservationDuration) {
  227. $now = time();
  228. foreach ($this->dates as $date) {
  229. $cells = $this->getCells($date);
  230. for ($i = 0, $ii = count($cells); $i < $ii; $i++) {
  231. $cell = $cells[$i];
  232. if (!($cell->isEmpty()) || $cell->getStartTime() < $now)
  233. continue;
  234. $duration = $this->cellDuration;
  235. for ($j = $i+1; $j < $ii; $j++) {
  236. if ($duration >= $reservationDuration)
  237. break;
  238. if (!($cells[$j]->isEmpty()))
  239. break;
  240. $duration += $this->cellDuration;
  241. }
  242. if ($duration >= $reservationDuration)
  243. $cell->setReservable(true);
  244. }
  245. }
  246. }
  247. }
  248. /**
  249. * Represents a single cell in the calendar.
  250. *
  251. */
  252. class Cell {
  253. protected $available;
  254. protected $reservable;
  255. protected $empty;
  256. protected $reserved;
  257. protected $reservation;
  258. protected $startTime;
  259. protected $endTime;
  260. /**
  261. * Creates a new cell that starts and ends at the specified times.
  262. *
  263. * @param int $startTime
  264. * @param int $endTime
  265. */
  266. public function __construct($startTime, $endTime) {
  267. $this->startTime = $startTime;
  268. $this->endTime = $endTime;
  269. }
  270. /**
  271. * Gets whether the cell is available (employee
  272. * is working).
  273. *
  274. * @return bool
  275. */
  276. public function isAvailable() {
  277. return $this->available;
  278. }
  279. /**
  280. * Sets whether the cell is available (employee
  281. * is working).
  282. *
  283. * @param bool $available
  284. */
  285. public function setAvailable($available) {
  286. $this->available = $available;
  287. }
  288. /**
  289. * Gets whether the cell is reservable.
  290. *
  291. * @return bool
  292. */
  293. public function isReservable() {
  294. return $this->reservable;
  295. }
  296. /**
  297. * Sets whether the cell is reservable.
  298. *
  299. * @param bool $reservable
  300. */
  301. public function setReservable($reservable) {
  302. $this->reservable = $reservable;
  303. }
  304. /**
  305. * Gets whether the cell is empty (not reserved)
  306. *
  307. * @return bool
  308. */
  309. public function isEmpty() {
  310. return $this->empty;
  311. }
  312. /**
  313. * Sets whether the cell is empty (not reserved)
  314. *
  315. * @param bool $empty
  316. */
  317. public function setEmpty($empty) {
  318. $this->empty = $empty;
  319. }
  320. /**
  321. * Gets whether the cell is reserved.
  322. *
  323. * @return bool
  324. */
  325. public function isReserved() {
  326. return $this->reserved;
  327. }
  328. /**
  329. * Sets whether the cell is reserved.
  330. *
  331. * @param bool $reserved
  332. */
  333. public function setReserved($reserved) {
  334. $this->reserved = $reserved;
  335. }
  336. /**
  337. * Gets the reservation associated with the cell.
  338. *
  339. * @return Reservation
  340. */
  341. public function getReservation() {
  342. return $this->reservation;
  343. }
  344. /**
  345. * Sets the reservation associated with the cell.
  346. *
  347. * @param Reservation $reservation
  348. */
  349. public function setReservation($reservation) {
  350. $this->reservation = $reservation;
  351. }
  352. /**
  353. * Gets the starting time of the cell.
  354. *
  355. * @return int
  356. */
  357. public function getStartTime() {
  358. return $this->startTime;
  359. }
  360. /**
  361. * Gets the ending time of the cell.
  362. *
  363. * @return int
  364. */
  365. public function getEndTime() {
  366. return $this->endTime;
  367. }
  368. /**
  369. * Sets the ending time of the cell.
  370. *
  371. * @param int $endTime
  372. */
  373. public function setEndTime($endTime) {
  374. $this->endTime = $endTime;
  375. }
  376. }
  377. ?>