From 96f703bf22b7b6448d1eefa5eba36ed8f281cf09 Mon Sep 17 00:00:00 2001 From: Igor Scheller Date: Sun, 22 Jan 2023 18:43:09 +0100 Subject: [PATCH] Migrate NeededAngelType model --- .../Shifts/NeededAngelTypeFactory.php | 27 +++++ ...000000_create_needed_angel_types_table.php | 108 ++++++++++++++++++ includes/controller/shifts_controller.php | 23 ++-- includes/model/NeededAngelTypes_model.php | 85 ++------------ includes/model/Shifts_model.php | 48 ++++---- includes/model/Stats.php | 42 +++---- includes/pages/admin_rooms.php | 40 ++++--- includes/pages/admin_shifts.php | 39 ++----- src/Models/AngelType.php | 35 +++--- src/Models/Room.php | 23 ++-- src/Models/Shifts/NeededAngelType.php | 63 ++++++++++ src/Models/Shifts/Shift.php | 44 ++++--- tests/Unit/Models/AngelTypeTest.php | 17 +++ tests/Unit/Models/RoomTest.php | 24 ++++ .../Models/Shifts/NeededAngelTypeTest.php | 40 +++++++ tests/Unit/Models/Shifts/ShiftTest.php | 23 ++++ 16 files changed, 459 insertions(+), 222 deletions(-) create mode 100644 db/factories/Shifts/NeededAngelTypeFactory.php create mode 100644 db/migrations/2023_01_17_000000_create_needed_angel_types_table.php create mode 100644 src/Models/Shifts/NeededAngelType.php create mode 100644 tests/Unit/Models/Shifts/NeededAngelTypeTest.php diff --git a/db/factories/Shifts/NeededAngelTypeFactory.php b/db/factories/Shifts/NeededAngelTypeFactory.php new file mode 100644 index 00000000..74731f46 --- /dev/null +++ b/db/factories/Shifts/NeededAngelTypeFactory.php @@ -0,0 +1,27 @@ +faker->boolean(); + + return [ + 'room_id' => $forRoom ? Room::factory() : null, + 'shift_id' => $forRoom ? null : Shift::factory(), + 'angel_type_id' => AngelType::factory(), + 'count' => $this->faker->numberBetween(1, 5), + ]; + } +} diff --git a/db/migrations/2023_01_17_000000_create_needed_angel_types_table.php b/db/migrations/2023_01_17_000000_create_needed_angel_types_table.php new file mode 100644 index 00000000..cbc05d5c --- /dev/null +++ b/db/migrations/2023_01_17_000000_create_needed_angel_types_table.php @@ -0,0 +1,108 @@ +schema->getConnection(); + $previous = $this->schema->hasTable('NeededAngelTypes'); + + $this->schema->create('needed_angel_types', function (Blueprint $table): void { + $table->increments('id'); + $this->references($table, 'rooms')->nullable(); + $this->references($table, 'shifts')->nullable(); + $this->references($table, 'angel_types'); + $table->integer('count')->index(); + + $table->index(['room_id', 'angel_type_id']); + }); + + if (!$previous) { + return; + } + + // Delete old entries which don't need angels + $connection + ->table('NeededAngelTypes') + ->where('count', 0) + ->delete(); + + /** @var stdClass[] $records */ + $records = $connection + ->table('NeededAngelTypes') + ->get(); + foreach ($records as $record) { + $connection->table('needed_angel_types')->insert([ + 'id' => $record->id, + 'room_id' => $record->room_id, + 'shift_id' => $record->shift_id, + 'angel_type_id' => $record->angel_type_id, + 'count' => $record->count, + ]); + } + + $this->changeReferences( + 'NeededAngelTypes', + 'id', + 'needed_angel_types', + 'id' + ); + + $this->schema->drop('NeededAngelTypes'); + } + + /** + * Recreates the previous table, copies the data and drops the new one + */ + public function down(): void + { + $connection = $this->schema->getConnection(); + + $this->schema->create('NeededAngelTypes', function (Blueprint $table): void { + $table->increments('id'); + $this->references($table, 'rooms')->nullable(); + $this->references($table, 'shifts')->nullable(); + $this->references($table, 'angel_types'); + $table->integer('count')->index(); + + $table->index(['room_id', 'angel_type_id']); + }); + + /** @var stdClass[] $records */ + $records = $connection + ->table('needed_angel_types') + ->get(); + foreach ($records as $record) { + $connection->table('NeededAngelTypes')->insert([ + 'id' => $record->id, + 'room_id' => $record->room_id, + 'shift_id' => $record->shift_id, + 'angel_type_id' => $record->angel_type_id, + 'count' => $record->count, + ]); + } + + $this->changeReferences( + 'needed_angel_types', + 'id', + 'NeededAngelTypes', + 'id' + ); + + $this->schema->drop('needed_angel_types'); + } +} diff --git a/includes/controller/shifts_controller.php b/includes/controller/shifts_controller.php index bbaf2d23..bfae83c1 100644 --- a/includes/controller/shifts_controller.php +++ b/includes/controller/shifts_controller.php @@ -3,6 +3,7 @@ use Carbon\CarbonTimeZone; use Engelsystem\Http\Exceptions\HttpForbidden; use Engelsystem\Models\AngelType; +use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\Shifts\ScheduleShift; use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\ShiftType; @@ -60,7 +61,7 @@ function shift_edit_controller() } $shift_id = $request->input('edit_shift'); - $shift = Shift(Shift::findOrFail($shift_id)); + $shift = Shift::findOrFail($shift_id); if (ScheduleShift::whereShiftId($shift->id)->first()) { warning(__( 'This shift was imported from a schedule so some changes will be overwritten with the next import.' @@ -149,6 +150,8 @@ function shift_edit_controller() } if ($valid) { + $oldShift = Shift::find($shift->id); + $shift->shift_type_id = $shifttype_id; $shift->title = $title; $shift->description = $description; @@ -156,19 +159,21 @@ function shift_edit_controller() $shift->start = $start; $shift->end = $end; $shift->updatedBy()->associate(auth()->user()); - - // Remove merged data as it is not really part of the model and thus can't be saved - unset($shift->neededAngels); - $shift->save(); - mail_shift_change(Shift($shift->id), $shift); + mail_shift_change($oldShift, $shift); - NeededAngelTypes_delete_by_shift($shift_id); + NeededAngelType::whereShiftId($shift_id)->delete(); $needed_angel_types_info = []; foreach ($needed_angel_types as $type_id => $count) { - NeededAngelType_add($shift_id, $type_id, null, $count); - if ($count > 0) { + $angeltype = AngelType::find($type_id); + if (!empty($angeltype) && $count > 0) { + $neededAngelType = new NeededAngelType(); + $neededAngelType->shift()->associate($shift); + $neededAngelType->angel_type_id = $type_id; + $neededAngelType->count = $count; + $neededAngelType->save(); + $needed_angel_types_info[] = $angeltypes[$type_id] . ': ' . $count; } } diff --git a/includes/model/NeededAngelTypes_model.php b/includes/model/NeededAngelTypes_model.php index c2d0c9b6..9922b7eb 100644 --- a/includes/model/NeededAngelTypes_model.php +++ b/includes/model/NeededAngelTypes_model.php @@ -4,75 +4,6 @@ use Engelsystem\Database\Db; use Engelsystem\Models\Shifts\ShiftEntry; use Illuminate\Database\Eloquent\Collection; -/** - * Entity needed angeltypes describes how many angels of given type are needed for a shift or in a room. - */ - -/** - * Insert a new needed angel type. - * - * @param int|null $shift_id The shift. Can be null, but then a room_id must be given. - * @param int $angeltype_id The angeltype - * @param int|null $room_id The room. Can be null, but then a shift_id must be given. - * @param int $count How many angels are needed? - * - * @return int|false - */ -function NeededAngelType_add($shift_id, $angeltype_id, $room_id, $count) -{ - Db::insert( - ' - INSERT INTO `NeededAngelTypes` ( `shift_id`, `angel_type_id`, `room_id`, `count`) - VALUES (?, ?, ?, ?) - ', - [ - $shift_id, - $angeltype_id, - $room_id, - $count, - ] - ); - - return Db::getPdo()->lastInsertId(); -} - -/** - * Deletes all needed angel types from given shift. - * - * @param int $shift_id id of the shift - */ -function NeededAngelTypes_delete_by_shift($shift_id) -{ - Db::delete('DELETE FROM `NeededAngelTypes` WHERE `shift_id` = ?', [$shift_id]); -} - -/** - * Deletes all needed angel types from given room. - * - * @param int $room_id id of the room - */ -function NeededAngelTypes_delete_by_room($room_id) -{ - Db::delete( - 'DELETE FROM `NeededAngelTypes` WHERE `room_id` = ?', - [$room_id] - ); -} - -/** - * Returns all needed angeltypes by room. - * - * @param int $room_id - * @return array - */ -function NeededAngelTypes_by_room($room_id) -{ - return Db::select( - 'SELECT `angel_type_id`, `count` FROM `NeededAngelTypes` WHERE `room_id`=?', - [$room_id] - ); -} - /** * Returns all needed angeltypes and already taken needs. * @@ -84,15 +15,14 @@ function NeededAngelTypes_by_shift($shiftId) $needed_angeltypes_source = Db::select( ' SELECT - `NeededAngelTypes`.*, + `needed_angel_types`.*, `angel_types`.`id`, `angel_types`.`name`, `angel_types`.`restricted`, `angel_types`.`no_self_signup` - FROM `NeededAngelTypes` - JOIN `angel_types` ON `angel_types`.`id` = `NeededAngelTypes`.`angel_type_id` + FROM `needed_angel_types` + JOIN `angel_types` ON `angel_types`.`id` = `needed_angel_types`.`angel_type_id` WHERE `shift_id` = ? - AND `count` > 0 ORDER BY `room_id` DESC', [$shiftId] ); @@ -100,12 +30,11 @@ function NeededAngelTypes_by_shift($shiftId) // Use settings from room if (count($needed_angeltypes_source) == 0) { $needed_angeltypes_source = Db::select(' - SELECT `NeededAngelTypes`.*, `angel_types`.`name`, `angel_types`.`restricted` - FROM `NeededAngelTypes` - JOIN `angel_types` ON `angel_types`.`id` = `NeededAngelTypes`.`angel_type_id` - JOIN `shifts` ON `shifts`.`room_id` = `NeededAngelTypes`.`room_id` + SELECT `needed_angel_types`.*, `angel_types`.`name`, `angel_types`.`restricted` + FROM `needed_angel_types` + JOIN `angel_types` ON `angel_types`.`id` = `needed_angel_types`.`angel_type_id` + JOIN `shifts` ON `shifts`.`room_id` = `needed_angel_types`.`room_id` WHERE `shifts`.`id` = ? - AND `count` > 0 ORDER BY `room_id` DESC ', [$shiftId]); } diff --git a/includes/model/Shifts_model.php b/includes/model/Shifts_model.php index 5bdcd136..397cd184 100644 --- a/includes/model/Shifts_model.php +++ b/includes/model/Shifts_model.php @@ -20,19 +20,17 @@ function Shifts_by_angeltype(AngelType $angeltype) { return Db::select(' SELECT DISTINCT `shifts`.* FROM `shifts` - JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `shifts`.`id` + JOIN `needed_angel_types` ON `needed_angel_types`.`shift_id` = `shifts`.`id` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id - WHERE `NeededAngelTypes`.`angel_type_id` = ? - AND `NeededAngelTypes`.`count` > 0 + WHERE `needed_angel_types`.`angel_type_id` = ? AND s.shift_id IS NULL UNION SELECT DISTINCT `shifts`.* FROM `shifts` - JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id` = `shifts`.`room_id` + JOIN `needed_angel_types` ON `needed_angel_types`.`room_id` = `shifts`.`room_id` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id - WHERE `NeededAngelTypes`.`angel_type_id` = ? - AND `NeededAngelTypes`.`count` > 0 + WHERE `needed_angel_types`.`angel_type_id` = ? AND NOT s.shift_id IS NULL ', [$angeltype->id, $angeltype->id]); } @@ -58,7 +56,7 @@ function Shifts_free($start, $end, ShiftsFilter $filter = null) FROM `shifts` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id WHERE (`end` > ? AND `start` < ?) - AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`shifts`.`id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') + AND (SELECT SUM(`count`) FROM `needed_angel_types` WHERE `needed_angel_types`.`shift_id`=`shifts`.`id`' . ($filter ? ' AND needed_angel_types.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') > (SELECT COUNT(*) FROM `shift_entries` WHERE `shift_entries`.`shift_id`=`shifts`.`id` AND shift_entries.`freeloaded`=0' . ($filter ? ' AND shift_entries.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') AND s.shift_id IS NULL ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' @@ -69,7 +67,7 @@ function Shifts_free($start, $end, ShiftsFilter $filter = null) FROM `shifts` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id WHERE (`end` > ? AND `start` < ?) - AND (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`shifts`.`room_id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') + AND (SELECT SUM(`count`) FROM `needed_angel_types` WHERE `needed_angel_types`.`room_id`=`shifts`.`room_id`' . ($filter ? ' AND needed_angel_types.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') > (SELECT COUNT(*) FROM `shift_entries` WHERE `shift_entries`.`shift_id`=`shifts`.`id` AND `freeloaded`=0' . ($filter ? ' AND shift_entries.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') AND NOT s.shift_id IS NULL ' . ($filter ? 'AND shifts.room_id IN (' . implode(',', $filter->getRooms()) . ')' : '') . ' @@ -101,12 +99,11 @@ function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter) FROM `shifts` JOIN `rooms` ON `shifts`.`room_id` = `rooms`.`id` JOIN `shift_types` ON `shift_types`.`id` = `shifts`.`shift_type_id` - JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id` = `shifts`.`id` + JOIN `needed_angel_types` ON `needed_angel_types`.`shift_id` = `shifts`.`id` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ') AND `start` BETWEEN ? AND ? - AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ') - AND `NeededAngelTypes`.`count` > 0 + AND `needed_angel_types`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ') AND s.shift_id IS NULL UNION @@ -115,12 +112,11 @@ function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter) FROM `shifts` JOIN `rooms` ON `shifts`.`room_id` = `rooms`.`id` JOIN `shift_types` ON `shift_types`.`id` = `shifts`.`shift_type_id` - JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`shifts`.`room_id` + JOIN `needed_angel_types` ON `needed_angel_types`.`room_id`=`shifts`.`room_id` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ') AND `start` BETWEEN ? AND ? - AND `NeededAngelTypes`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ') - AND `NeededAngelTypes`.`count` > 0 + AND `needed_angel_types`.`angel_type_id` IN (' . implode(',', $shiftsFilter->getTypes()) . ') AND NOT s.shift_id IS NULL ) AS tmp_shifts @@ -153,15 +149,15 @@ function NeededAngeltypes_by_ShiftsFilter(ShiftsFilter $shiftsFilter) { $sql = ' SELECT - `NeededAngelTypes`.*, + `needed_angel_types`.*, `shifts`.`id` AS shift_id, `angel_types`.`id`, `angel_types`.`name`, `angel_types`.`restricted`, `angel_types`.`no_self_signup` FROM `shifts` - JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`shifts`.`id` - JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id` + JOIN `needed_angel_types` ON `needed_angel_types`.`shift_id`=`shifts`.`id` + JOIN `angel_types` ON `angel_types`.`id`= `needed_angel_types`.`angel_type_id` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ') AND shifts.`start` BETWEEN ? AND ? @@ -170,15 +166,15 @@ function NeededAngeltypes_by_ShiftsFilter(ShiftsFilter $shiftsFilter) UNION SELECT - `NeededAngelTypes`.*, + `needed_angel_types`.*, `shifts`.`id` AS shift_id, `angel_types`.`id`, `angel_types`.`name`, `angel_types`.`restricted`, `angel_types`.`no_self_signup` FROM `shifts` - JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`shifts`.`room_id` - JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id` + JOIN `needed_angel_types` ON `needed_angel_types`.`room_id`=`shifts`.`room_id` + JOIN `angel_types` ON `angel_types`.`id`= `needed_angel_types`.`angel_type_id` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id WHERE `shifts`.`room_id` IN (' . implode(',', $shiftsFilter->getRooms()) . ') AND shifts.`start` BETWEEN ? AND ? @@ -206,15 +202,15 @@ function NeededAngeltype_by_Shift_and_Angeltype(Shift $shift, AngelType $angelty return Db::selectOne( ' SELECT - `NeededAngelTypes`.*, + `needed_angel_types`.*, `shifts`.`id` AS shift_id, `angel_types`.`id`, `angel_types`.`name`, `angel_types`.`restricted`, `angel_types`.`no_self_signup` FROM `shifts` - JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`shift_id`=`shifts`.`id` - JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id` + JOIN `needed_angel_types` ON `needed_angel_types`.`shift_id`=`shifts`.`id` + JOIN `angel_types` ON `angel_types`.`id`= `needed_angel_types`.`angel_type_id` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id WHERE `shifts`.`id`=? AND `angel_types`.`id`=? @@ -223,15 +219,15 @@ function NeededAngeltype_by_Shift_and_Angeltype(Shift $shift, AngelType $angelty UNION SELECT - `NeededAngelTypes`.*, + `needed_angel_types`.*, `shifts`.`id` AS shift_id, `angel_types`.`id`, `angel_types`.`name`, `angel_types`.`restricted`, `angel_types`.`no_self_signup` FROM `shifts` - JOIN `NeededAngelTypes` ON `NeededAngelTypes`.`room_id`=`shifts`.`room_id` - JOIN `angel_types` ON `angel_types`.`id`= `NeededAngelTypes`.`angel_type_id` + JOIN `needed_angel_types` ON `needed_angel_types`.`room_id`=`shifts`.`room_id` + JOIN `angel_types` ON `angel_types`.`id`= `needed_angel_types`.`angel_type_id` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id WHERE `shifts`.`id`=? AND `angel_types`.`id`=? diff --git a/includes/model/Stats.php b/includes/model/Stats.php index 04168c64..744f21d0 100644 --- a/includes/model/Stats.php +++ b/includes/model/Stats.php @@ -42,7 +42,7 @@ function stats_hours_to_work(ShiftsFilter $filter = null) ' SELECT ROUND(SUM(`count`)) AS `count` FROM ( SELECT - (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`shift_id`=`shifts`.`id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') + (SELECT SUM(`count`) FROM `needed_angel_types` WHERE `needed_angel_types`.`shift_id`=`shifts`.`id`' . ($filter ? ' AND needed_angel_types.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') * TIMESTAMPDIFF(MINUTE, `shifts`.`start`, `shifts`.`end`) / 60 AS `count` FROM `shifts` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id @@ -53,7 +53,7 @@ function stats_hours_to_work(ShiftsFilter $filter = null) UNION ALL SELECT - (SELECT SUM(`count`) FROM `NeededAngelTypes` WHERE `NeededAngelTypes`.`room_id`=`shifts`.`room_id`' . ($filter ? ' AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') + (SELECT SUM(`count`) FROM `needed_angel_types` WHERE `needed_angel_types`.`room_id`=`shifts`.`room_id`' . ($filter ? ' AND needed_angel_types.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ') * TIMESTAMPDIFF(MINUTE, `shifts`.`start`, `shifts`.`end`) / 60 AS `count` FROM `shifts` LEFT JOIN schedule_shift AS s on shifts.id = s.shift_id @@ -82,12 +82,12 @@ function stats_angels_needed_three_hours(ShiftsFilter $filter = null) SELECT GREATEST(0, ( - SELECT SUM(`count`) - FROM `NeededAngelTypes` - JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id` + SELECT SUM(needed_angel_types.`count`) + FROM `needed_angel_types` + JOIN `angel_types` ON `angel_types`.`id`=`needed_angel_types`.`angel_type_id` WHERE `angel_types`.`show_on_dashboard`=TRUE - AND `NeededAngelTypes`.`shift_id`=`shifts`.`id` - ' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' + AND `needed_angel_types`.`shift_id`=`shifts`.`id` + ' . ($filter ? 'AND needed_angel_types.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ) - ( SELECT COUNT(*) FROM `shift_entries` JOIN `angel_types` ON `angel_types`.`id`=`shift_entries`.`angel_type_id` @@ -109,12 +109,12 @@ function stats_angels_needed_three_hours(ShiftsFilter $filter = null) SELECT GREATEST(0, ( - SELECT SUM(`count`) - FROM `NeededAngelTypes` - JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id` + SELECT SUM(needed_angel_types.`count`) + FROM `needed_angel_types` + JOIN `angel_types` ON `angel_types`.`id`=`needed_angel_types`.`angel_type_id` WHERE `angel_types`.`show_on_dashboard`=TRUE - AND `NeededAngelTypes`.`room_id`=`shifts`.`room_id` - ' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' + AND `needed_angel_types`.`room_id`=`shifts`.`room_id` + ' . ($filter ? 'AND needed_angel_types.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ) - ( SELECT COUNT(*) FROM `shift_entries` @@ -162,12 +162,12 @@ function stats_angels_needed_for_nightshifts(ShiftsFilter $filter = null) SELECT GREATEST(0, ( - SELECT SUM(`count`) - FROM `NeededAngelTypes` - JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id` + SELECT SUM(needed_angel_types.`count`) + FROM `needed_angel_types` + JOIN `angel_types` ON `angel_types`.`id`=`needed_angel_types`.`angel_type_id` WHERE `angel_types`.`show_on_dashboard`=TRUE - AND `NeededAngelTypes`.`shift_id`=`shifts`.`id` - ' . ($filter ? 'AND NeededAngelTypes.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' + AND `needed_angel_types`.`shift_id`=`shifts`.`id` + ' . ($filter ? 'AND needed_angel_types.angel_type_id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ) - ( SELECT COUNT(*) FROM `shift_entries` JOIN `angel_types` ON `angel_types`.`id`=`shift_entries`.`angel_type_id` @@ -189,11 +189,11 @@ function stats_angels_needed_for_nightshifts(ShiftsFilter $filter = null) SELECT GREATEST(0, ( - SELECT SUM(`count`) - FROM `NeededAngelTypes` - JOIN `angel_types` ON `angel_types`.`id`=`NeededAngelTypes`.`angel_type_id` + SELECT SUM(needed_angel_types.`count`) + FROM `needed_angel_types` + JOIN `angel_types` ON `angel_types`.`id`=`needed_angel_types`.`angel_type_id` WHERE `angel_types`.`show_on_dashboard`=TRUE - AND `NeededAngelTypes`.`room_id`=`shifts`.`room_id` + AND `needed_angel_types`.`room_id`=`shifts`.`room_id` ' . ($filter ? 'AND angel_types.id IN (' . implode(',', $filter->getTypes()) . ')' : '') . ' ) - ( SELECT COUNT(*) FROM `shift_entries` diff --git a/includes/pages/admin_rooms.php b/includes/pages/admin_rooms.php index e52fb915..95241791 100644 --- a/includes/pages/admin_rooms.php +++ b/includes/pages/admin_rooms.php @@ -2,6 +2,7 @@ use Engelsystem\Models\AngelType; use Engelsystem\Models\Room; +use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\User\User; /** @@ -23,10 +24,10 @@ function admin_rooms() foreach ($rooms_source as $room) { $rooms[] = [ - 'name' => Room_name_render($room), - 'dect' => icon_bool($room->dect), - 'map_url' => icon_bool($room->map_url), - 'actions' => table_buttons([ + 'name' => Room_name_render($room), + 'dect' => icon_bool($room->dect), + 'map_url' => icon_bool($room->map_url), + 'actions' => table_buttons([ button( page_link_to('admin_rooms', ['show' => 'edit', 'id' => $room->id]), icon('pencil') . __('edit'), @@ -70,12 +71,10 @@ function admin_rooms() $description = $room->description; $dect = $room->dect; - $needed_angeltypes = NeededAngelTypes_by_room($room_id); - foreach ($needed_angeltypes as $needed_angeltype) { - $angeltypes_count[$needed_angeltype['angel_type_id']] = $needed_angeltype['count']; - } + $angeltypes_count = NeededAngelType::whereRoomId($room_id) + ->pluck('count', 'angel_type_id') + ->toArray() + $angeltypes_count; } - if ($request->input('show') == 'edit') { if ($request->hasPostData('submit')) { $valid = true; @@ -129,15 +128,18 @@ function admin_rooms() Room_update($room_id, $name, $map_url, $description, $dect); } - NeededAngelTypes_delete_by_room($room_id); + NeededAngelType::whereRoomId($room_id)->delete(); $needed_angeltype_info = []; foreach ($angeltypes_count as $angeltype_id => $angeltype_count) { $angeltype = AngelType::find($angeltype_id); - if (!empty($angeltype)) { - NeededAngelType_add(null, $angeltype_id, $room_id, $angeltype_count); - if ($angeltype_count > 0) { - $needed_angeltype_info[] = $angeltype->name . ': ' . $angeltype_count; - } + if (!empty($angeltype) && $angeltype_count > 0) { + $neededAngelType = new NeededAngelType(); + $neededAngelType->room_id = $room_id; + $neededAngelType->angelType()->associate($angeltype); + $neededAngelType->count = $angeltype_count; + $neededAngelType->save(); + + $needed_angeltype_info[] = $angeltype->name . ': ' . $angeltype_count; } } @@ -227,10 +229,10 @@ function admin_rooms() ]), msg(), table([ - 'name' => __('Name'), - 'dect' => __('DECT'), - 'map_url' => __('Map'), - 'actions' => '' + 'name' => __('Name'), + 'dect' => __('DECT'), + 'map_url' => __('Map'), + 'actions' => '' ], $rooms) ], true); } diff --git a/includes/pages/admin_shifts.php b/includes/pages/admin_shifts.php index 7cfd3000..1143506e 100644 --- a/includes/pages/admin_shifts.php +++ b/includes/pages/admin_shifts.php @@ -5,6 +5,7 @@ use Engelsystem\Helpers\Carbon; use Engelsystem\Http\Exceptions\HttpForbidden; use Engelsystem\Models\AngelType; use Engelsystem\Models\Room; +use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\Shifts\Schedule; use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\ShiftType; @@ -184,18 +185,9 @@ function admin_shifts() // Alle Eingaben in Ordnung if ($valid) { if ($angelmode == 'location') { - $needed_angel_types = []; - $needed_angel_types_location = Db::select( - ' - SELECT `angel_type_id`, `count` - FROM `NeededAngelTypes` - WHERE `room_id`=? - ', - [$rid] - ); - foreach ($needed_angel_types_location as $type) { - $needed_angel_types[$type['angel_type_id']] = $type['count']; - } + $needed_angel_types = NeededAngelType::whereRoomId($rid) + ->pluck('count', 'angel_type_id') + ->toArray() + $needed_angel_types; } $shifts = []; @@ -377,7 +369,6 @@ function admin_shifts() $shift->transaction_id = $transactionId; $shift->createdBy()->associate(auth()->user()); $shift->save(); - $shift_id = $shift->id; engelsystem_log( 'Shift created: ' . $shifttypes[$shift->shift_type_id] @@ -391,22 +382,14 @@ function admin_shifts() $needed_angel_types_info = []; foreach ($session->get('admin_shifts_types', []) as $type_id => $count) { $angel_type_source = AngelType::find($type_id); - if (!empty($angel_type_source)) { - Db::insert( - ' - INSERT INTO `NeededAngelTypes` (`shift_id`, `angel_type_id`, `count`) - VALUES (?, ?, ?) - ', - [ - $shift_id, - $type_id, - $count - ] - ); + if (!empty($angel_type_source) && $count > 0) { + $neededAngelType = new NeededAngelType(); + $neededAngelType->shift()->associate($shift); + $neededAngelType->angelType()->associate($angel_type_source); + $neededAngelType->count = $count; + $neededAngelType->save(); - if ($count > 0) { - $needed_angel_types_info[] = $angel_type_source->name . ': ' . $count; - } + $needed_angel_types_info[] = $angel_type_source->name . ': ' . $count; } } engelsystem_log('Shift needs following angel types: ' . join(', ', $needed_angel_types_info)); diff --git a/src/Models/AngelType.php b/src/Models/AngelType.php index a4543768..4438106f 100644 --- a/src/Models/AngelType.php +++ b/src/Models/AngelType.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Engelsystem\Models; +use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\Shifts\ShiftEntry; use Illuminate\Database\Eloquent\Builder; use Engelsystem\Models\User\User; @@ -14,21 +15,22 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Query\Builder as QueryBuilder; /** - * @property int $id - * @property string $name - * @property string $description - * @property string $contact_name - * @property string $contact_dect - * @property string $contact_email - * @property boolean $restricted # If users need an introduction - * @property boolean $requires_driver_license # If users must have a driver license - * @property boolean $no_self_signup # Users can sign up for shifts - * @property boolean $show_on_dashboard # Show on public dashboard - * @property boolean $hide_register # Hide from registration page + * @property int $id + * @property string $name + * @property string $description + * @property string $contact_name + * @property string $contact_dect + * @property string $contact_email + * @property boolean $restricted # If users need an introduction + * @property boolean $requires_driver_license # If users must have a driver license + * @property boolean $no_self_signup # Users can sign up for shifts + * @property boolean $show_on_dashboard # Show on public dashboard + * @property boolean $hide_register # Hide from registration page * - * @property-read UserAngelType $pivot - * @property-read Collection|ShiftEntry[] $shiftEntries - * @property-read Collection|User[] $userAngelTypes + * @property-read Collection|NeededAngelType[] $neededBy + * @property-read UserAngelType $pivot + * @property-read Collection|ShiftEntry[] $shiftEntries + * @property-read Collection|User[] $userAngelTypes * * @method static QueryBuilder|AngelType[] whereId($value) * @method static QueryBuilder|AngelType[] whereName($value) @@ -71,6 +73,11 @@ class AngelType extends BaseModel 'hide_register' => 'boolean', ]; + public function neededBy(): HasMany + { + return $this->hasMany(NeededAngelType::class); + } + public function shiftEntries(): HasMany { return $this->hasMany(ShiftEntry::class); diff --git a/src/Models/Room.php b/src/Models/Room.php index 082cee23..42c6d9a9 100644 --- a/src/Models/Room.php +++ b/src/Models/Room.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Engelsystem\Models; use Carbon\Carbon; +use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\Shifts\Shift; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -12,15 +13,16 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Query\Builder as QueryBuilder; /** - * @property int $id - * @property string $name - * @property string $map_url - * @property string $description - * @property string $dect - * @property Carbon|null $created_at - * @property Carbon|null $updated_at + * @property int $id + * @property string $name + * @property string $map_url + * @property string $description + * @property string $dect + * @property Carbon|null $created_at + * @property Carbon|null $updated_at * - * @property-read Collection|Shift[] $shifts + * @property-read Collection|NeededAngelType[] $neededAngelTypes + * @property-read Collection|Shift[] $shifts * * @method static QueryBuilder|Room[] whereId($value) * @method static QueryBuilder|Room[] whereName($value) @@ -45,6 +47,11 @@ class Room extends BaseModel 'description', ]; + public function neededAngelTypes(): HasMany + { + return $this->hasMany(NeededAngelType::class); + } + public function shifts(): HasMany { return $this->hasMany(Shift::class); diff --git a/src/Models/Shifts/NeededAngelType.php b/src/Models/Shifts/NeededAngelType.php new file mode 100644 index 00000000..c3223b2e --- /dev/null +++ b/src/Models/Shifts/NeededAngelType.php @@ -0,0 +1,63 @@ + */ + protected $fillable = [ // phpcs:ignore + 'room_id', + 'shift_id', + 'angel_type_id', + 'count', + ]; + + /** @var array default attributes */ + protected $attributes = [ // phpcs:ignore + 'room_id' => null, + 'shift_id' => null, + ]; + + public function room(): BelongsTo + { + return $this->belongsTo(Room::class); + } + + public function shift(): BelongsTo + { + return $this->belongsTo(Shift::class); + } + + public function angelType(): BelongsTo + { + return $this->belongsTo(AngelType::class); + } +} diff --git a/src/Models/Shifts/Shift.php b/src/Models/Shifts/Shift.php index 21cdb0ac..ceabee40 100644 --- a/src/Models/Shifts/Shift.php +++ b/src/Models/Shifts/Shift.php @@ -16,26 +16,27 @@ use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Query\Builder as QueryBuilder; /** - * @property int $id - * @property string $title - * @property string $description - * @property string $url - * @property Carbon $start - * @property Carbon $end - * @property int $shift_type_id - * @property int $room_id - * @property string $transaction_id - * @property int $created_by - * @property int|null $updated_by - * @property Carbon|null $created_at - * @property Carbon|null $updated_at + * @property int $id + * @property string $title + * @property string $description + * @property string $url + * @property Carbon $start + * @property Carbon $end + * @property int $shift_type_id + * @property int $room_id + * @property string $transaction_id + * @property int $created_by + * @property int|null $updated_by + * @property Carbon|null $created_at + * @property Carbon|null $updated_at * - * @property-read QueryBuilder|Schedule $schedule - * @property-read QueryBuilder|Collection|ShiftEntry[] $shiftEntries - * @property-read QueryBuilder|ShiftType $shiftType - * @property-read QueryBuilder|Room $room - * @property-read QueryBuilder|User $createdBy - * @property-read QueryBuilder|User|null $updatedBy + * @property-read Collection|NeededAngelType[] $neededAngelTypes + * @property-read Schedule $schedule + * @property-read Collection|ShiftEntry[] $shiftEntries + * @property-read ShiftType $shiftType + * @property-read Room $room + * @property-read User $createdBy + * @property-read User|null $updatedBy * * @method static QueryBuilder|Shift[] whereId($value) * @method static QueryBuilder|Shift[] whereTitle($value) @@ -86,6 +87,11 @@ class Shift extends BaseModel 'end', ]; + public function neededAngelTypes(): HasMany + { + return $this->hasMany(NeededAngelType::class); + } + public function schedule(): HasOneThrough { return $this->hasOneThrough(Schedule::class, ScheduleShift::class, null, 'id', null, 'schedule_id'); diff --git a/tests/Unit/Models/AngelTypeTest.php b/tests/Unit/Models/AngelTypeTest.php index d21ffcff..18ef18e5 100644 --- a/tests/Unit/Models/AngelTypeTest.php +++ b/tests/Unit/Models/AngelTypeTest.php @@ -3,6 +3,7 @@ namespace Engelsystem\Test\Unit\Models; use Engelsystem\Models\AngelType; +use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\Shifts\ShiftEntry; use Engelsystem\Models\User\User; use Engelsystem\Models\UserAngelType; @@ -70,9 +71,25 @@ class AngelTypeTest extends ModelTest ShiftEntry::factory(3)->create(['angel_type_id' => $angelType->id]); + $angelType = AngelType::find(1); $this->assertCount(3, $angelType->shiftEntries); } + /** + * @covers \Engelsystem\Models\AngelType::neededBy + */ + public function testNeededBy(): void + { + $angelType = AngelType::create(['name' => 'test type']); + + $this->assertCount(0, $angelType->neededBy); + + NeededAngelType::factory(4)->create(['angel_type_id' => $angelType->id]); + + $angelType = AngelType::find(1); + $this->assertCount(4, $angelType->neededBy); + } + /** * @covers \Engelsystem\Models\AngelType::boot */ diff --git a/tests/Unit/Models/RoomTest.php b/tests/Unit/Models/RoomTest.php index ef9c916d..19eef000 100644 --- a/tests/Unit/Models/RoomTest.php +++ b/tests/Unit/Models/RoomTest.php @@ -5,7 +5,9 @@ declare(strict_types=1); namespace Engelsystem\Test\Unit\Models; use Engelsystem\Models\Room; +use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\Shifts\Shift; +use Illuminate\Database\Eloquent\Collection; class RoomTest extends ModelTest { @@ -23,4 +25,26 @@ class RoomTest extends ModelTest $room = Room::find(1); $this->assertCount(1, $room->shifts); } + + /** + * @covers \Engelsystem\Models\Room::neededAngelTypes + */ + public function testNeededAngelTypes(): void + { + /** @var Collection|Room[] $shifts */ + $shifts = Room::factory(3)->create(); + + $this->assertCount(0, Room::find(1)->neededAngelTypes); + + (NeededAngelType::factory()->make(['room_id' => $shifts[0]->id, 'shift_id' => null]))->save(); + (NeededAngelType::factory()->make(['room_id' => $shifts[0]->id, 'shift_id' => null]))->save(); + (NeededAngelType::factory()->make(['room_id' => $shifts[1]->id, 'shift_id' => null]))->save(); + (NeededAngelType::factory()->make(['room_id' => $shifts[2]->id, 'shift_id' => null]))->save(); + + $this->assertCount(2, Room::find(1)->neededAngelTypes); + $this->assertEquals(1, Room::find(1)->neededAngelTypes[0]->id); + $this->assertEquals(2, Room::find(1)->neededAngelTypes[1]->id); + $this->assertEquals(3, Room::find(2)->neededAngelTypes->first()->id); + $this->assertEquals(4, Room::find(3)->neededAngelTypes->first()->id); + } } diff --git a/tests/Unit/Models/Shifts/NeededAngelTypeTest.php b/tests/Unit/Models/Shifts/NeededAngelTypeTest.php new file mode 100644 index 00000000..f0e89880 --- /dev/null +++ b/tests/Unit/Models/Shifts/NeededAngelTypeTest.php @@ -0,0 +1,40 @@ +create(); + /** @var Shift $shift */ + $shift = Shift::factory()->create(); + /** @var AngelType $angelType */ + $angelType = AngelType::factory()->create(); + + $model = new NeededAngelType(); + $model->room()->associate($room); + $model->shift()->associate($shift); + $model->angelType()->associate($angelType); + $model->count = 3; + $model->save(); + + $model = NeededAngelType::find(1); + $this->assertEquals($room->id, $model->room->id); + $this->assertEquals($shift->id, $model->shift->id); + $this->assertEquals($angelType->id, $model->angelType->id); + $this->assertEquals(3, $model->count); + } +} diff --git a/tests/Unit/Models/Shifts/ShiftTest.php b/tests/Unit/Models/Shifts/ShiftTest.php index fae30feb..1abe7637 100644 --- a/tests/Unit/Models/Shifts/ShiftTest.php +++ b/tests/Unit/Models/Shifts/ShiftTest.php @@ -6,6 +6,7 @@ namespace Engelsystem\Test\Unit\Models\Shifts; use Engelsystem\Helpers\Carbon; use Engelsystem\Models\Room; +use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\Shifts\Schedule; use Engelsystem\Models\Shifts\ScheduleShift; use Engelsystem\Models\Shifts\Shift; @@ -56,6 +57,28 @@ class ShiftTest extends ModelTest $this->assertEquals($user2->id, $model->updatedBy->id); } + /** + * @covers \Engelsystem\Models\Shifts\Shift::neededAngelTypes + */ + public function testNeededAngelTypes(): void + { + /** @var Collection|Shift[] $shifts */ + $shifts = Shift::factory(3)->create(); + + $this->assertCount(0, Shift::find(1)->neededAngelTypes); + + (NeededAngelType::factory()->make(['shift_id' => $shifts[0]->id, 'room_id' => null]))->save(); + (NeededAngelType::factory()->make(['shift_id' => $shifts[0]->id, 'room_id' => null]))->save(); + (NeededAngelType::factory()->make(['shift_id' => $shifts[1]->id, 'room_id' => null]))->save(); + (NeededAngelType::factory()->make(['shift_id' => $shifts[2]->id, 'room_id' => null]))->save(); + + $this->assertCount(2, Shift::find(1)->neededAngelTypes); + $this->assertEquals(1, Shift::find(1)->neededAngelTypes[0]->id); + $this->assertEquals(2, Shift::find(1)->neededAngelTypes[1]->id); + $this->assertEquals(3, Shift::find(2)->neededAngelTypes->first()->id); + $this->assertEquals(4, Shift::find(3)->neededAngelTypes->first()->id); + } + /** * @covers \Engelsystem\Models\Shifts\Shift::schedule */