ILIAS  release_8 Revision v8.23
class.ilBookingReservation.php
Go to the documentation of this file.
1 <?php
2 
24 {
25  public const STATUS_IN_USE = 2;
26  public const STATUS_CANCELLED = 5;
27 
28  protected ilDBInterface $db;
29  protected int $id = 0;
30  protected int $object_id = 0;
31  protected int $user_id = 0;
32  protected int $from = 0;
33  protected int $to = 0;
34  protected int $status = 0;
35  protected int $group_id = 0;
36  protected int $assigner_id = 0;
37  protected int $context_obj_id = 0;
39 
40  public function __construct(
41  int $a_id = null
42  ) {
43  global $DIC;
44 
45  $this->db = $DIC->database();
46  $this->id = (int) $a_id;
47 
49  $this->repo = $f->getRepo();
50 
51  $this->read();
52  }
53 
54  public function getId(): int
55  {
56  return $this->id;
57  }
58 
59  public function setObjectId(int $a_object_id): void
60  {
61  $this->object_id = $a_object_id;
62  }
63 
64  public function getObjectId(): int
65  {
66  return $this->object_id;
67  }
68 
69  public function setUserId(int $a_user_id): void
70  {
71  $this->user_id = $a_user_id;
72  }
73 
74  public function getUserId(): int
75  {
76  return $this->user_id;
77  }
78 
82  public function setAssignerId(int $a_assigner_id): void
83  {
84  $this->assigner_id = $a_assigner_id;
85  }
86 
87  public function getAssignerId(): int
88  {
89  return $this->assigner_id;
90  }
91 
95  public function setFrom(int $a_from): void
96  {
97  $this->from = $a_from;
98  }
99 
100  public function getFrom(): int
101  {
102  return $this->from;
103  }
104 
108  public function setTo(int $a_to): void
109  {
110  $this->to = $a_to;
111  }
112 
113  public function getTo(): int
114  {
115  return $this->to;
116  }
117 
121  public function setStatus(?int $a_status): void
122  {
123  if ($a_status === null) {
124  $this->status = 0;
125  }
126  if (self::isValidStatus((int) $a_status)) {
127  $this->status = (int) $a_status;
128  }
129  }
130 
134  public function getStatus(): ?int
135  {
136  return $this->status;
137  }
138 
139  public static function isValidStatus(int $a_status): bool
140  {
141  return in_array($a_status, array(self::STATUS_IN_USE, self::STATUS_CANCELLED));
142  }
143 
144  public function setGroupId(int $a_group_id): void
145  {
146  $this->group_id = $a_group_id;
147  }
148 
149  public function getGroupId(): int
150  {
151  return $this->group_id;
152  }
153 
157  public function setContextObjId(int $a_val): void
158  {
159  $this->context_obj_id = $a_val;
160  }
161 
165  public function getContextObjId(): int
166  {
167  return $this->context_obj_id;
168  }
169 
170  protected function read(): void
171  {
172  if ($this->id) {
173  $row = $this->repo->getForId($this->id);
174  $this->setUserId($row['user_id']);
175  $this->setAssignerId((int) $row['assigner_id']);
176  $this->setObjectId($row['object_id']);
177  $this->setFrom($row['date_from']);
178  $this->setTo($row['date_to']);
179  $this->setStatus($row['status']);
180  $this->setGroupId((int) $row['group_id']);
181  $this->setContextObjId((int) $row['context_obj_id']);
182  }
183  }
184 
185  public function save(): bool
186  {
187  if ($this->id) {
188  return false;
189  }
190 
191  $this->id = $this->repo->create(
192  $this->getUserId(),
193  $this->getAssignerId(),
194  $this->getObjectId(),
195  $this->getContextObjId(),
196  $this->getFrom(),
197  $this->getTo(),
198  $this->getStatus(),
199  $this->getGroupId()
200  );
201  return ($this->id > 0);
202  }
203 
204  public function update(): bool
205  {
206  if (!$this->id) {
207  return false;
208  }
209 
210  $this->repo->update(
211  $this->id,
212  $this->getUserId(),
213  $this->getAssignerId(),
214  $this->getObjectId(),
215  $this->getContextObjId(),
216  $this->getFrom(),
217  $this->getTo(),
218  $this->getStatus(),
219  $this->getGroupId()
220  );
221  return true;
222  }
223 
224  public function delete(): void
225  {
226  $this->repo->delete($this->id);
227  }
228 
229 
233  public static function getAvailableObject(
234  array $a_ids,
235  int $a_from,
236  int $a_to,
237  bool $a_return_single = true,
238  bool $a_return_counter = false
239  ): array {
240  $nr_map = ilBookingObject::getNrOfItemsForObjects($a_ids);
241 
243  $repo = $f->getRepo();
244 
245  $blocked = $counter = array();
246  foreach ($repo->getNumberOfReservations($a_ids, $a_from, $a_to) as $row) {
247  if ($row['cnt'] >= $nr_map[$row['object_id']]) {
248  $blocked[] = $row['object_id'];
249  } elseif ($a_return_counter) {
250  $counter[$row['object_id']] = $nr_map[$row['object_id']] - (int) $row['cnt'];
251  }
252  }
253 
254  // #17868 - validate against schedule availability
255  foreach ($a_ids as $obj_id) {
256  $bobj = new ilBookingObject($obj_id);
257  if ($bobj->getScheduleId()) {
258  $schedule = new ilBookingSchedule($bobj->getScheduleId());
259 
260  $av_from = ($schedule->getAvailabilityFrom() && !$schedule->getAvailabilityFrom()->isNull())
261  ? $schedule->getAvailabilityFrom()->get(IL_CAL_UNIX)
262  : null;
263  $av_to = ($schedule->getAvailabilityTo() && !$schedule->getAvailabilityTo()->isNull())
264  ? $schedule->getAvailabilityTo()->get(IL_CAL_UNIX)
265  : null;
266 
267  if (($av_from && $a_from < $av_from) ||
268  ($av_to && $a_to > $av_to)) {
269  $blocked[] = $obj_id;
270  unset($counter[$obj_id]);
271  }
272  }
273  }
274 
275  $available = array_diff($a_ids, $blocked);
276  if (count($available)) {
277  if ($a_return_counter) {
278  foreach ($a_ids as $id) {
279  if (!isset($counter[$id])) {
280  $counter[$id] = $nr_map[$id];
281  }
282  }
283  return $counter;
284  }
285  if ($a_return_single) {
286  return array_shift($available);
287  }
288  return $available;
289  }
290  return [];
291  }
292 
293  public static function isObjectAvailableInPeriod(
294  int $a_obj_id,
295  ilBookingSchedule $a_schedule,
296  ?int $a_from,
297  ?int $a_to
298  ): bool {
299  if (!$a_from) {
300  $a_from = time();
301  }
302  if (!$a_to) {
303  $a_to = strtotime("+1year", $a_from);
304  }
305 
306  if ($a_from > $a_to) {
307  return false;
308  }
309 
310  // all nr of reservations in period that are not over yet (to >= now)
312  $repo = $f->getRepo();
313  $res = $repo->getNumberOfReservations([$a_obj_id], $a_from, $a_to, true);
314  $booked_in_period = (int) ($res[$a_obj_id]["cnt"] ?? 0);
315 
316  $per_slot = ilBookingObject::getNrOfItemsForObjects(array($a_obj_id));
317  $per_slot = $per_slot[$a_obj_id];
318 
319  // max available nr of items per (week)day
320  $schedule_slots = array();
321  $definition = $a_schedule->getDefinition();
322  $map = array_flip(array("su", "mo", "tu", "we", "th", "fr", "sa"));
323  foreach ($definition as $day => $day_slots) {
324  $schedule_slots[$map[$day]] = $day_slots;
325  }
326 
327  $av_from = ($a_schedule->getAvailabilityFrom() && !$a_schedule->getAvailabilityFrom()->isNull())
328  ? $a_schedule->getAvailabilityFrom()->get(IL_CAL_UNIX)
329  : null;
330  $av_to = ($a_schedule->getAvailabilityTo() && !$a_schedule->getAvailabilityTo()->isNull())
331  ? strtotime($a_schedule->getAvailabilityTo()->get(IL_CAL_DATE) . " 23:59:59")
332  : null;
333 
334  // sum up max available (to >= now) items in period per (week)day
335  $available_in_period = 0;
336  $loop = 0;
337  while ($a_from < $a_to &&
338  ++$loop < 1000) {
339  // any slots for current weekday?
340  $day_slots = $schedule_slots[date("w", $a_from)] ?? false;
341  if ($day_slots) {
342  foreach ($day_slots as $slot) {
343  // convert slot to current datetime
344  $slot = explode("-", $slot);
345  $slot_from = strtotime(date("Y-m-d", $a_from) . " " . $slot[0]);
346  $slot_to = strtotime(date("Y-m-d", $a_from) . " " . $slot[1]);
347  // slot has to be in the future and part of schedule availability
348  if ($slot_from >= $av_from &&
349  ($slot_to <= $av_to || is_null($av_to)) &&
350  $slot_to > time()) {
351  $available_in_period += $per_slot;
352  }
353  }
354  }
355 
356  $a_from += (60 * 60 * 24);
357  }
358  return $available_in_period - $booked_in_period > 0;
359  }
360 
361  //check if the user reached the limit of bookings in this booking pool.
362  public static function isBookingPoolLimitReachedByUser(
363  int $a_user_id,
364  int $a_pool_id
365  ): int {
366  global $DIC;
367  $ilDB = $DIC->database();
368 
369  $booking_pool_objects = ilBookingObject::getObjectsForPool($a_pool_id);
370 
371  $query = "SELECT count(user_id) total" .
372  " FROM booking_reservation" .
373  " WHERE " . $ilDB->in('object_id', $booking_pool_objects, false, 'integer') .
374  " AND user_id = " . $a_user_id .
375  " AND (status IS NULL OR status <> " . self::STATUS_CANCELLED . ')';
376  $res = $ilDB->query($query);
377  $row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC);
378 
379  return (int) $row['total'];
380  }
381 
385  public static function getMembersWithoutReservation(
386  int $a_object_id
387  ): array {
388  global $DIC;
389  $ilDB = $DIC->database();
390 
391  $pool_id = ilBookingObject::lookupPoolId($a_object_id);
392 
393  $res = array();
394  $query = 'SELECT DISTINCT bm.user_id user_id' .
395  ' FROM booking_member bm' .
396  ' WHERE bm.booking_pool_id = ' . $ilDB->quote($pool_id, 'integer') .
397  ' AND bm.user_id NOT IN (' .
398  'SELECT user_id' .
399  ' FROM booking_reservation' .
400  ' WHERE object_id = ' . $ilDB->quote($a_object_id, 'integer') .
401  ' AND (status IS NULL OR status <> ' . self::STATUS_CANCELLED . '))';
402 
403  $set = $ilDB->query($query);
404 
405  while ($row = $ilDB->fetchAssoc($set)) {
406  $res[] = (int) $row['user_id'];
407  }
408 
409  return $res;
410  }
411 
412  public static function isObjectAvailableNoSchedule(int $a_obj_id): bool
413  {
414  $available = self::getNumAvailablesNoSchedule($a_obj_id);
415  return (bool) $available; // #11864
416  }
417 
418  public static function numAvailableFromObjectNoSchedule(int $a_obj_id): int
419  {
420  return self::getNumAvailablesNoSchedule($a_obj_id);
421  }
422 
423  public static function getNumAvailablesNoSchedule(int $a_obj_id): int
424  {
425  global $DIC;
426 
427  $ilDB = $DIC->database();
428 
429  $all = ilBookingObject::getNrOfItemsForObjects(array($a_obj_id));
430  $all = $all[$a_obj_id];
431 
432  $set = $ilDB->query('SELECT COUNT(*) cnt' .
433  ' FROM booking_reservation r' .
434  ' JOIN booking_object o ON (o.booking_object_id = r.object_id)' .
435  ' WHERE (status IS NULL OR status <> ' . $ilDB->quote(self::STATUS_CANCELLED, 'integer') . ')' .
436  ' AND r.object_id = ' . $ilDB->quote($a_obj_id, 'integer'));
437  $cnt = $ilDB->fetchAssoc($set);
438  $cnt = (int) $cnt['cnt'];
439 
440  return $all - $cnt; // #11864
441  }
442 
446  public static function getCurrentOrUpcomingReservation(
447  int $a_object_id
448  ): array {
449  global $DIC;
450 
451  $ilDB = $DIC->database();
452 
453  $now = $ilDB->quote(time(), 'integer');
454 
455  $ilDB->setLimit(1);
456  $set = $ilDB->query('SELECT user_id, status, date_from, date_to' .
457  ' FROM booking_reservation' .
458  ' WHERE ((date_from <= ' . $now . ' AND date_to >= ' . $now . ')' .
459  ' OR date_from > ' . $now . ')' .
460  ' AND (status <> ' . $ilDB->quote(self::STATUS_CANCELLED, 'integer') .
461  ' OR STATUS IS NULL) AND object_id = ' . $ilDB->quote($a_object_id, 'integer') .
462  ' ORDER BY date_from');
463  return $ilDB->fetchAssoc($set);
464  }
465 
469  public static function getObjectReservationForUser(
470  int $a_object_id,
471  int $a_user_id
472  ): ?array {
473  global $DIC;
474 
475  $ilDB = $DIC->database();
476 
477  $set = $ilDB->query('SELECT booking_reservation_id FROM booking_reservation' .
478  ' WHERE user_id = ' . $ilDB->quote($a_user_id, 'integer') .
479  ' AND object_id = ' . $ilDB->quote($a_object_id, 'integer') .
480  ' AND (status <> ' . $ilDB->quote(self::STATUS_CANCELLED, 'integer') .
481  ' OR STATUS IS NULL)');
482  $res = array();
483  while ($row = $ilDB->fetchAssoc($set)) {
484  $res[] = (int) $row['booking_reservation_id'];
485  }
486  return $res;
487  }
488 
493  public static function getList(
494  array $a_object_ids,
495  int $a_limit = 10,
496  int $a_offset = 0,
497  array $filter = []
498  ): array {
499  global $DIC;
500 
501  $ilDB = $DIC->database();
502 
503  $sql = 'SELECT r.*,o.title' .
504  ' FROM booking_reservation r' .
505  ' JOIN booking_object o ON (o.booking_object_id = r.object_id)';
506 
507  $count_sql = 'SELECT COUNT(*) AS counter' .
508  ' FROM booking_reservation r' .
509  ' JOIN booking_object o ON (o.booking_object_id = r.object_id)';
510 
511  $where = array($ilDB->in('r.object_id', $a_object_ids, '', 'integer'));
512  if (isset($filter['status'])) {
513  if ($filter['status'] > 0) {
514  $where[] = 'status = ' . $ilDB->quote($filter['status'], 'integer');
515  } else {
516  $where[] = '(status != ' . $ilDB->quote(-$filter['status'], 'integer') .
517  ' OR status IS NULL)';
518  }
519  }
520  if (isset($filter['from'])) {
521  $where[] = 'date_from >= ' . $ilDB->quote($filter['from'], 'integer');
522  }
523  if (isset($filter['to'])) {
524  $where[] = 'date_to <= ' . $ilDB->quote($filter['to'], 'integer');
525  }
526  if (count($where)) {
527  $sql .= ' WHERE ' . implode(' AND ', $where);
528  $count_sql .= ' WHERE ' . implode(' AND ', $where);
529  }
530 
531  $set = $ilDB->query($count_sql);
532  $row = $ilDB->fetchAssoc($set);
533  $counter = $row['counter'];
534 
535  $sql .= ' ORDER BY date_from DESC, booking_reservation_id DESC';
536 
537  $ilDB->setLimit($a_limit, $a_offset);
538  $set = $ilDB->query($sql);
539  $res = array();
540  while ($row = $ilDB->fetchAssoc($set)) {
541  $res[] = $row;
542  }
543 
544  return array('data' => $res, 'counter' => $counter);
545  }
546 
547 
554  public static function getUserFilter(
555  array $a_object_ids
556  ): array {
557  global $DIC;
558 
559  $ilDB = $DIC->database();
560 
561  $res = array();
562 
563  $sql = "SELECT ud.usr_id,ud.lastname,ud.firstname,ud.login" .
564  " FROM usr_data ud " .
565  " LEFT JOIN booking_reservation r ON (r.user_id = ud.usr_id)" .
566  " WHERE ud.usr_id <> " . $ilDB->quote(ANONYMOUS_USER_ID, "integer") .
567  " AND " . $ilDB->in("r.object_id", $a_object_ids, "", "integer") .
568  " ORDER BY ud.lastname,ud.firstname";
569  $set = $ilDB->query($sql);
570  while ($row = $ilDB->fetchAssoc($set)) {
571  $res[$row["usr_id"]] = $row["lastname"] . ", " . $row["firstname"] .
572  " (" . $row["login"] . ")";
573  }
574 
575  return $res;
576  }
577 
578 
585  public static function changeStatus(
586  array $a_ids,
587  int $a_status
588  ): int {
589  global $DIC;
590 
591  $ilDB = $DIC->database();
592 
593  if (self::isValidStatus($a_status)) {
594  return $ilDB->manipulate('UPDATE booking_reservation' .
595  ' SET status = ' . $ilDB->quote($a_status, 'integer') .
596  ' WHERE ' . $ilDB->in('booking_reservation_id', $a_ids, '', 'integer'));
597  }
598  return 0;
599  }
600 
601  // get calendar id of reservation
602  public function getCalendarEntry(): int
603  {
604  $ilDB = $this->db;
605 
606  $set = $ilDB->query("SELECT ce.cal_id FROM cal_entries ce" .
607  " JOIN cal_cat_assignments cca ON ce.cal_id = cca.cal_id" .
608  " JOIN cal_categories cc ON cca.cat_id = cc.cat_id" .
609  " JOIN booking_reservation br ON ce.context_id = br.booking_reservation_id" .
610  " WHERE cc.obj_id = " . $ilDB->quote($this->getUserId(), 'integer') .
611  " AND br.user_id = " . $ilDB->quote($this->getUserId(), 'integer') .
612  " AND cc.type = " . $ilDB->quote(ilCalendarCategory::TYPE_BOOK, 'integer') .
613  " AND ce.context_id = " . $ilDB->quote($this->getId(), 'integer'));
614  $row = $ilDB->fetchAssoc($set);
615  return (int) ($row["cal_id"] ?? 0);
616  }
617 
622  public static function getCancelDetails(
623  int $a_obj_id,
624  int $a_user_id,
625  int $a_from,
626  int $a_to
627  ): array {
628  global $DIC;
629 
630  $ilDB = $DIC->database();
631 
632  $res = array();
633 
634  $sql = "SELECT booking_reservation_id" .
635  " FROM booking_reservation" .
636  " WHERE object_id = " . $ilDB->quote($a_obj_id, "integer") .
637  " AND user_id = " . $ilDB->quote($a_user_id, "integer") .
638  " AND date_from = " . $ilDB->quote($a_from, "integer") .
639  " AND date_to = " . $ilDB->quote($a_to, "integer") .
640  " AND (status IS NULL" .
641  " OR status <> " . $ilDB->quote(self::STATUS_CANCELLED, "integer") . ")";
642  $set = $ilDB->query($sql);
643  while ($row = $ilDB->fetchAssoc($set)) {
644  $res[] = (int) $row["booking_reservation_id"];
645  }
646 
647  return $res;
648  }
649 }
static getCurrentOrUpcomingReservation(int $a_object_id)
Get details about object reservation.
setFrom(int $a_from)
Set booking from date.
setTo(int $a_to)
Set booking to date.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$res
Definition: ltiservices.php:69
static getNumAvailablesNoSchedule(int $a_obj_id)
const ANONYMOUS_USER_ID
Definition: constants.php:27
static changeStatus(array $a_ids, int $a_status)
Batch update reservation status.
static getUserFilter(array $a_object_ids)
Get all users who have reservations for object(s)
static getObjectReservationForUser(int $a_object_id, int $a_user_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getAvailableObject(array $a_ids, int $a_from, int $a_to, bool $a_return_single=true, bool $a_return_counter=false)
Check if any of given objects are bookable.
getNumberOfReservations(array $ids, int $from, int $to, bool $only_not_over_yet=false)
Get number of uncancelled reservations in time frame.
getStatus()
Get booking status.
const IL_CAL_UNIX
static getList(array $a_object_ids, int $a_limit=10, int $a_offset=0, array $filter=[])
List all reservations.
setAssignerId(int $a_assigner_id)
Set assigner user id.
static getObjectsForPool(int $a_pool_id)
Get all booking pool object ids from an specific booking pool.
global $DIC
Definition: feed.php:28
static getNrOfItemsForObjects(array $a_obj_ids)
Get nr of available items for a set of object ids.
ilBookingReservationDBRepository $repo
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getMembersWithoutReservation(int $a_object_id)
static getCancelDetails(int $a_obj_id, int $a_user_id, int $a_from, int $a_to)
Get reservation ids from aggregated id for cancellation.
setStatus(?int $a_status)
Set booking status.
$query
static isBookingPoolLimitReachedByUser(int $a_user_id, int $a_pool_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const IL_CAL_DATE
static numAvailableFromObjectNoSchedule(int $a_obj_id)
static isObjectAvailableNoSchedule(int $a_obj_id)
static lookupPoolId(int $object_id)
static isObjectAvailableInPeriod(int $a_obj_id, ilBookingSchedule $a_schedule, ?int $a_from, ?int $a_to)
static isValidStatus(int $a_status)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...