ILIAS  trunk Revision v11.0_alpha-1769-g99a433fe2dc
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
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;
38  protected string $message = "";
39  protected \ILIAS\BookingManager\Reservations\ReservationDBRepository $repo;
40 
41  public function __construct(
42  ?int $a_id = null
43  ) {
44  global $DIC;
45 
46  $this->db = $DIC->database();
47  $this->id = (int) $a_id;
48 
49  $this->repo = $DIC->bookingManager()
50  ->internal()
51  ->repo()
52  ->reservation();
53  $this->read();
54  }
55 
56  public function getId(): int
57  {
58  return $this->id;
59  }
60 
61  public function setObjectId(int $a_object_id): void
62  {
63  $this->object_id = $a_object_id;
64  }
65 
66  public function getObjectId(): int
67  {
68  return $this->object_id;
69  }
70 
71  public function setUserId(int $a_user_id): void
72  {
73  $this->user_id = $a_user_id;
74  }
75 
76  public function getUserId(): int
77  {
78  return $this->user_id;
79  }
80 
81  public function setMessage(string $message): void
82  {
83  $this->message = $message;
84  }
85 
86  public function getMessage(): string
87  {
88  return $this->message;
89  }
90 
94  public function setAssignerId(int $a_assigner_id): void
95  {
96  $this->assigner_id = $a_assigner_id;
97  }
98 
99  public function getAssignerId(): int
100  {
101  return $this->assigner_id;
102  }
103 
107  public function setFrom(int $a_from): void
108  {
109  $this->from = $a_from;
110  }
111 
112  public function getFrom(): int
113  {
114  return $this->from;
115  }
116 
120  public function setTo(int $a_to): void
121  {
122  $this->to = $a_to;
123  }
124 
125  public function getTo(): int
126  {
127  return $this->to;
128  }
129 
133  public function setStatus(?int $a_status): void
134  {
135  if ($a_status === null) {
136  $this->status = 0;
137  }
138  if (self::isValidStatus((int) $a_status)) {
139  $this->status = (int) $a_status;
140  }
141  }
142 
146  public function getStatus(): ?int
147  {
148  return $this->status;
149  }
150 
151  public static function isValidStatus(int $a_status): bool
152  {
153  return in_array($a_status, array(self::STATUS_IN_USE, self::STATUS_CANCELLED));
154  }
155 
156  public function setGroupId(int $a_group_id): void
157  {
158  $this->group_id = $a_group_id;
159  }
160 
161  public function getGroupId(): int
162  {
163  return $this->group_id;
164  }
165 
169  public function setContextObjId(int $a_val): void
170  {
171  $this->context_obj_id = $a_val;
172  }
173 
177  public function getContextObjId(): int
178  {
179  return $this->context_obj_id;
180  }
181 
182  protected function read(): void
183  {
184  if ($this->id) {
185  $row = $this->repo->getForId($this->id);
186  $this->setUserId($row['user_id']);
187  $this->setAssignerId((int) $row['assigner_id']);
188  $this->setObjectId($row['object_id']);
189  $this->setFrom($row['date_from']);
190  $this->setTo($row['date_to']);
191  $this->setStatus($row['status']);
192  $this->setGroupId((int) $row['group_id']);
193  $this->setContextObjId((int) $row['context_obj_id']);
194  $this->setMessage((string) $row['message']);
195  }
196  }
197 
198  public function save(): bool
199  {
200  if ($this->id) {
201  return false;
202  }
203 
204  $this->id = $this->repo->create(
205  $this->getUserId(),
206  $this->getAssignerId(),
207  $this->getObjectId(),
208  $this->getContextObjId(),
209  $this->getFrom(),
210  $this->getTo(),
211  $this->getStatus(),
212  $this->getGroupId(),
213  $this->getMessage()
214  );
215  return ($this->id > 0);
216  }
217 
218  public function update(): bool
219  {
220  if (!$this->id) {
221  return false;
222  }
223 
224  $this->repo->update(
225  $this->id,
226  $this->getUserId(),
227  $this->getAssignerId(),
228  $this->getObjectId(),
229  $this->getContextObjId(),
230  $this->getFrom(),
231  $this->getTo(),
232  $this->getStatus(),
233  $this->getGroupId(),
234  $this->getMessage()
235  );
236  return true;
237  }
238 
239  public function delete(): void
240  {
241  $this->repo->delete($this->id);
242  }
243 
244 
248  public static function getAvailableObject(
249  array $a_ids,
250  int $a_from,
251  int $a_to,
252  bool $a_return_single = true,
253  bool $a_return_counter = false
254  ): array {
255  global $DIC;
256 
257  $nr_map = ilBookingObject::getNrOfItemsForObjects($a_ids);
258 
259  $repo = $DIC->bookingManager()->internal()->repo()->reservation();
260 
261  $blocked = $counter = array();
262  foreach ($repo->getNumberOfReservations($a_ids, $a_from, $a_to) as $row) {
263  if ($row['cnt'] >= $nr_map[$row['object_id']]) {
264  $blocked[] = $row['object_id'];
265  } elseif ($a_return_counter) {
266  $counter[$row['object_id']] = $nr_map[$row['object_id']] - (int) $row['cnt'];
267  }
268  }
269 
270  // #17868 - validate against schedule availability
271  foreach ($a_ids as $obj_id) {
272  $bobj = new ilBookingObject($obj_id);
273  if ($bobj->getScheduleId()) {
274  $schedule = new ilBookingSchedule($bobj->getScheduleId());
275 
276  $av_from = ($schedule->getAvailabilityFrom() && !$schedule->getAvailabilityFrom()->isNull())
277  ? $schedule->getAvailabilityFrom()->get(IL_CAL_UNIX)
278  : null;
279  $av_to = ($schedule->getAvailabilityTo() && !$schedule->getAvailabilityTo()->isNull())
280  ? $schedule->getAvailabilityTo()->get(IL_CAL_UNIX)
281  : null;
282 
283  if (($av_from && $a_from < $av_from) ||
284  ($av_to && $a_to > $av_to)) {
285  $blocked[] = $obj_id;
286  unset($counter[$obj_id]);
287  }
288  }
289  }
290 
291  $available = array_diff($a_ids, $blocked);
292  if (count($available)) {
293  if ($a_return_counter) {
294  foreach ($a_ids as $id) {
295  if (!isset($counter[$id])) {
296  $counter[$id] = $nr_map[$id];
297  }
298  }
299  return $counter;
300  }
301  if ($a_return_single) {
302  return array_shift($available);
303  }
304  return $available;
305  }
306  return [];
307  }
308 
309  public static function isObjectAvailableInPeriod(
310  int $a_obj_id,
311  ilBookingSchedule $a_schedule,
312  ?int $a_from,
313  ?int $a_to
314  ): bool {
315  global $DIC;
316 
317  if (!$a_from) {
318  $a_from = time();
319  }
320  if (!$a_to) {
321  $a_to = strtotime("+1year", $a_from);
322  }
323 
324  if ($a_from > $a_to) {
325  return false;
326  }
327 
328  // all nr of reservations in period that are not over yet (to >= now)
329  $repo = $DIC->bookingManager()->internal()->repo()->reservation();
330  $res = $repo->getNumberOfReservations([$a_obj_id], $a_from, $a_to, true);
331  $booked_in_period = (int) ($res[$a_obj_id]["cnt"] ?? 0);
332 
333  $per_slot = ilBookingObject::getNrOfItemsForObjects(array($a_obj_id));
334  $per_slot = $per_slot[$a_obj_id];
335 
336  // max available nr of items per (week)day
337  $schedule_slots = array();
338  $definition = $a_schedule->getDefinition();
339  $map = array_flip(array("su", "mo", "tu", "we", "th", "fr", "sa"));
340  foreach ($definition as $day => $day_slots) {
341  $schedule_slots[$map[$day]] = $day_slots;
342  }
343 
344  $av_from = ($a_schedule->getAvailabilityFrom() && !$a_schedule->getAvailabilityFrom()->isNull())
345  ? $a_schedule->getAvailabilityFrom()->get(IL_CAL_UNIX)
346  : null;
347  $av_to = ($a_schedule->getAvailabilityTo() && !$a_schedule->getAvailabilityTo()->isNull())
348  ? strtotime($a_schedule->getAvailabilityTo()->get(IL_CAL_DATE) . " 23:59:59")
349  : null;
350 
351  // sum up max available (to >= now) items in period per (week)day
352  $available_in_period = 0;
353  $loop = 0;
354  while ($a_from < $a_to &&
355  ++$loop < 1000) {
356  // any slots for current weekday?
357  $day_slots = $schedule_slots[date("w", $a_from)] ?? false;
358  if ($day_slots) {
359  foreach ($day_slots as $slot) {
360  // convert slot to current datetime
361  $slot = explode("-", $slot);
362  $slot_from = strtotime(date("Y-m-d", $a_from) . " " . $slot[0]);
363  $slot_to = strtotime(date("Y-m-d", $a_from) . " " . $slot[1]);
364  // slot has to be in the future and part of schedule availability
365  if ($slot_from >= $av_from &&
366  ($slot_to <= $av_to || is_null($av_to)) &&
367  $slot_to > time()) {
368  $available_in_period += $per_slot;
369  }
370  }
371  }
372 
373  $a_from += (60 * 60 * 24);
374  }
375  return $available_in_period - $booked_in_period > 0;
376  }
377 
378  //check if the user reached the limit of bookings in this booking pool.
379  public static function isBookingPoolLimitReachedByUser(
380  int $a_user_id,
381  int $a_pool_id
382  ): int {
383  global $DIC;
384  $ilDB = $DIC->database();
385  $object_manager = $DIC->bookingManager()->internal()->domain()
386  ->objects($a_pool_id);
387 
388  $booking_pool_objects = $object_manager->getObjectIds();
389 
390  $query = "SELECT count(user_id) total" .
391  " FROM booking_reservation" .
392  " WHERE " . $ilDB->in('object_id', $booking_pool_objects, false, 'integer') .
393  " AND user_id = " . $a_user_id .
394  " AND (status IS NULL OR status <> " . self::STATUS_CANCELLED . ')';
395  $res = $ilDB->query($query);
396  $row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC);
397 
398  return (int) $row['total'];
399  }
400 
404  public static function getMembersWithoutReservation(
405  int $a_object_id
406  ): array {
407  global $DIC;
408  $ilDB = $DIC->database();
409 
410  $pool_id = ilBookingObject::lookupPoolId($a_object_id);
411 
412  $res = array();
413  $query = 'SELECT DISTINCT bm.user_id user_id' .
414  ' FROM booking_member bm' .
415  ' WHERE bm.booking_pool_id = ' . $ilDB->quote($pool_id, 'integer') .
416  ' AND bm.user_id NOT IN (' .
417  'SELECT user_id' .
418  ' FROM booking_reservation' .
419  ' WHERE object_id = ' . $ilDB->quote($a_object_id, 'integer') .
420  ' AND (status IS NULL OR status <> ' . self::STATUS_CANCELLED . '))';
421 
422  $set = $ilDB->query($query);
423 
424  while ($row = $ilDB->fetchAssoc($set)) {
425  $res[] = (int) $row['user_id'];
426  }
427 
428  return $res;
429  }
430 
431  public static function isObjectAvailableNoSchedule(int $a_obj_id): bool
432  {
433  $available = self::getNumAvailablesNoSchedule($a_obj_id);
434  return (bool) $available; // #11864
435  }
436 
437  public static function numAvailableFromObjectNoSchedule(int $a_obj_id): int
438  {
439  return self::getNumAvailablesNoSchedule($a_obj_id);
440  }
441 
442  public static function getNumAvailablesNoSchedule(int $a_obj_id): int
443  {
444  global $DIC;
445 
446  $ilDB = $DIC->database();
447 
448  $all = ilBookingObject::getNrOfItemsForObjects(array($a_obj_id));
449  $all = $all[$a_obj_id];
450 
451  $set = $ilDB->query('SELECT COUNT(*) cnt' .
452  ' FROM booking_reservation r' .
453  ' JOIN booking_object o ON (o.booking_object_id = r.object_id)' .
454  ' WHERE (status IS NULL OR status <> ' . $ilDB->quote(self::STATUS_CANCELLED, 'integer') . ')' .
455  ' AND r.object_id = ' . $ilDB->quote($a_obj_id, 'integer'));
456  $cnt = $ilDB->fetchAssoc($set);
457  $cnt = (int) $cnt['cnt'];
458 
459  return $all - $cnt; // #11864
460  }
461 
465  public static function getCurrentOrUpcomingReservation(
466  int $a_object_id
467  ): array {
468  global $DIC;
469 
470  $ilDB = $DIC->database();
471 
472  $now = $ilDB->quote(time(), 'integer');
473 
474  $ilDB->setLimit(1);
475  $set = $ilDB->query('SELECT user_id, status, date_from, date_to' .
476  ' FROM booking_reservation' .
477  ' WHERE ((date_from <= ' . $now . ' AND date_to >= ' . $now . ')' .
478  ' OR date_from > ' . $now . ')' .
479  ' AND (status <> ' . $ilDB->quote(self::STATUS_CANCELLED, 'integer') .
480  ' OR STATUS IS NULL) AND object_id = ' . $ilDB->quote($a_object_id, 'integer') .
481  ' ORDER BY date_from');
482  return $ilDB->fetchAssoc($set);
483  }
484 
488  public static function getObjectReservationForUser(
489  int $a_object_id,
490  int $a_user_id
491  ): ?array {
492  global $DIC;
493 
494  $ilDB = $DIC->database();
495 
496  $set = $ilDB->query('SELECT booking_reservation_id FROM booking_reservation' .
497  ' WHERE user_id = ' . $ilDB->quote($a_user_id, 'integer') .
498  ' AND object_id = ' . $ilDB->quote($a_object_id, 'integer') .
499  ' AND (status <> ' . $ilDB->quote(self::STATUS_CANCELLED, 'integer') .
500  ' OR STATUS IS NULL)');
501  $res = array();
502  while ($row = $ilDB->fetchAssoc($set)) {
503  $res[] = (int) $row['booking_reservation_id'];
504  }
505  return $res;
506  }
507 
512  public static function getList(
513  array $a_object_ids,
514  int $a_limit = 10,
515  int $a_offset = 0,
516  array $filter = []
517  ): array {
518  global $DIC;
519 
520  $ilDB = $DIC->database();
521 
522  $sql = 'SELECT r.*,o.title' .
523  ' FROM booking_reservation r' .
524  ' JOIN booking_object o ON (o.booking_object_id = r.object_id)';
525 
526  $count_sql = 'SELECT COUNT(*) AS counter' .
527  ' FROM booking_reservation r' .
528  ' JOIN booking_object o ON (o.booking_object_id = r.object_id)';
529 
530  $where = array($ilDB->in('r.object_id', $a_object_ids, '', 'integer'));
531  if (isset($filter['status'])) {
532  if ($filter['status'] > 0) {
533  $where[] = 'status = ' . $ilDB->quote($filter['status'], 'integer');
534  } else {
535  $where[] = '(status != ' . $ilDB->quote(-$filter['status'], 'integer') .
536  ' OR status IS NULL)';
537  }
538  }
539  if (isset($filter['from'])) {
540  $where[] = 'date_from >= ' . $ilDB->quote($filter['from'], 'integer');
541  }
542  if (isset($filter['to'])) {
543  $where[] = 'date_to <= ' . $ilDB->quote($filter['to'], 'integer');
544  }
545  if (count($where)) {
546  $sql .= ' WHERE ' . implode(' AND ', $where);
547  $count_sql .= ' WHERE ' . implode(' AND ', $where);
548  }
549 
550  $set = $ilDB->query($count_sql);
551  $row = $ilDB->fetchAssoc($set);
552  $counter = $row['counter'];
553 
554  $sql .= ' ORDER BY date_from DESC, booking_reservation_id DESC';
555 
556  $ilDB->setLimit($a_limit, $a_offset);
557  $set = $ilDB->query($sql);
558  $res = array();
559  while ($row = $ilDB->fetchAssoc($set)) {
560  $res[] = $row;
561  }
562 
563  return array('data' => $res, 'counter' => $counter);
564  }
565 
566 
573  public static function getUserFilter(
574  array $a_object_ids
575  ): array {
576  global $DIC;
577 
578  $ilDB = $DIC->database();
579 
580  $res = array();
581 
582  $sql = "SELECT ud.usr_id,ud.lastname,ud.firstname,ud.login" .
583  " FROM usr_data ud " .
584  " LEFT JOIN booking_reservation r ON (r.user_id = ud.usr_id)" .
585  " WHERE ud.usr_id <> " . $ilDB->quote(ANONYMOUS_USER_ID, "integer") .
586  " AND " . $ilDB->in("r.object_id", $a_object_ids, "", "integer") .
587  " ORDER BY ud.lastname,ud.firstname";
588  $set = $ilDB->query($sql);
589  while ($row = $ilDB->fetchAssoc($set)) {
590  $res[$row["usr_id"]] = $row["lastname"] . ", " . $row["firstname"] .
591  " (" . $row["login"] . ")";
592  }
593 
594  return $res;
595  }
596 
597 
604  public static function changeStatus(
605  array $a_ids,
606  int $a_status
607  ): int {
608  global $DIC;
609 
610  $ilDB = $DIC->database();
611 
612  if (self::isValidStatus($a_status)) {
613  return $ilDB->manipulate('UPDATE booking_reservation' .
614  ' SET status = ' . $ilDB->quote($a_status, 'integer') .
615  ' WHERE ' . $ilDB->in('booking_reservation_id', $a_ids, '', 'integer'));
616  }
617  return 0;
618  }
619 
620  // get calendar id of reservation
621  public function getCalendarEntry(): int
622  {
623  $ilDB = $this->db;
624 
625  $set = $ilDB->query("SELECT ce.cal_id FROM cal_entries ce" .
626  " JOIN cal_cat_assignments cca ON ce.cal_id = cca.cal_id" .
627  " JOIN cal_categories cc ON cca.cat_id = cc.cat_id" .
628  " JOIN booking_reservation br ON ce.context_id = br.booking_reservation_id" .
629  " WHERE cc.obj_id = " . $ilDB->quote($this->getUserId(), 'integer') .
630  " AND br.user_id = " . $ilDB->quote($this->getUserId(), 'integer') .
631  " AND cc.type = " . $ilDB->quote(ilCalendarCategory::TYPE_BOOK, 'integer') .
632  " AND ce.context_id = " . $ilDB->quote($this->getId(), 'integer'));
633  $row = $ilDB->fetchAssoc($set);
634  return (int) ($row["cal_id"] ?? 0);
635  }
636 
641  public static function getCancelDetails(
642  int $a_obj_id,
643  int $a_user_id,
644  int $a_from,
645  int $a_to
646  ): array {
647  global $DIC;
648 
649  $ilDB = $DIC->database();
650 
651  $res = array();
652 
653  $sql = "SELECT booking_reservation_id" .
654  " FROM booking_reservation" .
655  " WHERE object_id = " . $ilDB->quote($a_obj_id, "integer") .
656  " AND user_id = " . $ilDB->quote($a_user_id, "integer") .
657  " AND date_from = " . $ilDB->quote($a_from, "integer") .
658  " AND date_to = " . $ilDB->quote($a_to, "integer") .
659  " AND (status IS NULL" .
660  " OR status <> " . $ilDB->quote(self::STATUS_CANCELLED, "integer") . ")";
661  $set = $ilDB->query($sql);
662  while ($row = $ilDB->fetchAssoc($set)) {
663  $res[] = (int) $row["booking_reservation_id"];
664  }
665 
666  return $res;
667  }
668 }
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:66
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.
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.
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
static getNrOfItemsForObjects(array $a_obj_ids)
Get nr of available items for a set of object ids.
static getMembersWithoutReservation(int $a_object_id)
global $DIC
Definition: shib_login.php:22
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.
static isBookingPoolLimitReachedByUser(int $a_user_id, int $a_pool_id)
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)
to(\GdImage $image, ?int $quality=null)
Currently this is the only way to make a FileStream from a GD image resource.
static isValidStatus(int $a_status)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
ILIAS BookingManager Reservations ReservationDBRepository $repo