From da9e9a840fb35359062685b060949ab11c54986c Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Mon, 24 Jun 2024 14:08:45 +0200 Subject: [PATCH] RotationConfigForm: Update only if changes exist --- application/forms/RotationConfigForm.php | 256 +++++++++++++---------- 1 file changed, 150 insertions(+), 106 deletions(-) diff --git a/application/forms/RotationConfigForm.php b/application/forms/RotationConfigForm.php index 9aa88e2e7..09cba35d4 100644 --- a/application/forms/RotationConfigForm.php +++ b/application/forms/RotationConfigForm.php @@ -75,6 +75,9 @@ class RotationConfigForm extends CompatForm /** @var ?DateTime The first handoff of a newer version for this rotation */ protected $nextHandoff; + /** @var int The rotation id */ + protected $rotationId; + /** * Set the label for the submit button * @@ -199,112 +202,8 @@ public function __construct(int $scheduleId, Connection $db) */ public function loadRotation(int $rotationId): self { - /** @var ?Rotation $rotation */ - $rotation = Rotation::on($this->db) - ->filter(Filter::all( - Filter::equal('id', $rotationId), - Filter::equal('deleted', 'n') - )) - ->first(); - if ($rotation === null) { - throw new HttpNotFoundException($this->translate('Rotation not found')); - } - - $formData = [ - 'mode' => $rotation->mode, - 'name' => $rotation->name, - 'priority' => $rotation->priority, - 'schedule' => $rotation->schedule_id, - 'options' => $rotation->options - ]; - if (! self::EXPERIMENTAL_OVERRIDES) { - $formData['first_handoff'] = $rotation->first_handoff; - } - - if (self::EXPERIMENTAL_OVERRIDES) { - $getHandoff = function (Rotation $rotation): DateTime { - switch ($rotation->mode) { - case '24-7': - $time = $rotation->options['at']; - - break; - case 'partial': - $time = $rotation->options['from']; - - break; - case 'multi': - $time = $rotation->options['from_at']; - - break; - default: - throw new LogicException('Invalid mode'); - } - - $handoff = DateTime::createFromFormat('Y-m-d H:i', $rotation->first_handoff . ' ' . $time); - if ($handoff === false) { - throw new ConfigurationError('Invalid date format'); - } - - return $handoff; - }; - - $this->previousHandoff = $getHandoff($rotation); - - /** @var ?TimeperiodEntry $previousShift */ - $previousShift = TimeperiodEntry::on($this->db) - ->columns('until_time') - ->filter(Filter::all( - Filter::equal('deleted', 'n'), - Filter::equal('timeperiod.deleted', 'n'), - Filter::equal('timeperiod.rotation.schedule_id', $rotation->schedule_id), - Filter::equal('timeperiod.rotation.priority', $rotation->priority), - Filter::unequal('timeperiod.owned_by_rotation_id', $rotation->id), - Filter::lessThanOrEqual('until_time', $this->previousHandoff), - Filter::like('until_time', '*') - )) - ->orderBy('until_time', SORT_DESC) - ->first(); - if ($previousShift !== null) { - $this->previousShift = $previousShift->until_time; - } - - /** @var ?Rotation $newerRotation */ - $newerRotation = Rotation::on($this->db) - ->columns(['first_handoff', 'options', 'mode']) - ->filter(Filter::all( - Filter::equal('deleted', 'n'), - Filter::equal('schedule_id', $rotation->schedule_id), - Filter::equal('priority', $rotation->priority), - Filter::greaterThan('first_handoff', $rotation->first_handoff) - )) - ->orderBy('first_handoff', SORT_ASC) - ->first(); - if ($newerRotation !== null) { - $this->nextHandoff = $getHandoff($newerRotation); - } - } - - $membersRes = $rotation - ->member - ->filter(Filter::equal('deleted', 'n')) - ->filter(Filter::any( - Filter::equal('contact.deleted', 'n'), - Filter::equal('contactgroup.deleted', 'n') - )) - ->orderBy('position', SORT_ASC); - - $members = []; - foreach ($membersRes as $member) { - if ($member->contact_id !== null) { - $members[] = 'contact:' . $member->contact_id; - } else { - $members[] = 'group:' . $member->contactgroup_id; - } - } - - $formData['members'] = implode(',', $members); - - $this->populate($formData); + $this->rotationId = $rotationId; + $this->populate($this->fetchDbValues()); return $this; } @@ -442,6 +341,10 @@ public function editRotation(int $rotationId): void $transactionStarted = $this->db->beginTransaction(); } + if (! $this->hasChanges()) { + return; + } + // Delay the creation, avoids intermediate constraint failures $createStmt = $this->createRotation((int) $priority); @@ -1592,4 +1495,145 @@ private function calculateRemainingHandoffs(Rule $rrule, DateInterval $shiftDura return $result; } + + /** + * Fetch the values from the database + * + * @return array + * + * @throws HttpNotFoundException + */ + private function fetchDbValues(): array + { + /** @var ?Rotation $rotation */ + $rotation = Rotation::on($this->db) + ->filter(Filter::all( + Filter::equal('id', $this->rotationId), + Filter::equal('deleted', 'n') + )) + ->first(); + if ($rotation === null) { + throw new HttpNotFoundException($this->translate('Rotation not found')); + } + + $formData = [ + 'mode' => $rotation->mode, + 'name' => $rotation->name, + 'priority' => $rotation->priority, + 'schedule' => $rotation->schedule_id, + 'options' => $rotation->options + ]; + if (! self::EXPERIMENTAL_OVERRIDES) { + $formData['first_handoff'] = $rotation->first_handoff; + } + + if (self::EXPERIMENTAL_OVERRIDES) { + $getHandoff = function (Rotation $rotation): DateTime { + switch ($rotation->mode) { + case '24-7': + $time = $rotation->options['at']; + + break; + case 'partial': + $time = $rotation->options['from']; + + break; + case 'multi': + $time = $rotation->options['from_at']; + + break; + default: + throw new LogicException('Invalid mode'); + } + + $handoff = DateTime::createFromFormat('Y-m-d H:i', $rotation->first_handoff . ' ' . $time); + if ($handoff === false) { + throw new ConfigurationError('Invalid date format'); + } + + return $handoff; + }; + + $this->previousHandoff = $getHandoff($rotation); + + /** @var ?TimeperiodEntry $previousShift */ + $previousShift = TimeperiodEntry::on($this->db) + ->columns('until_time') + ->filter(Filter::all( + Filter::equal('deleted', 'n'), + Filter::equal('timeperiod.deleted', 'n'), + Filter::equal('timeperiod.rotation.schedule_id', $rotation->schedule_id), + Filter::equal('timeperiod.rotation.priority', $rotation->priority), + Filter::unequal('timeperiod.owned_by_rotation_id', $rotation->id), + Filter::lessThanOrEqual('until_time', $this->previousHandoff), + Filter::like('until_time', '*') + )) + ->orderBy('until_time', SORT_DESC) + ->first(); + if ($previousShift !== null) { + $this->previousShift = $previousShift->until_time; + } + + /** @var ?Rotation $newerRotation */ + $newerRotation = Rotation::on($this->db) + ->columns(['first_handoff', 'options', 'mode']) + ->filter(Filter::all( + Filter::equal('deleted', 'n'), + Filter::equal('schedule_id', $rotation->schedule_id), + Filter::equal('priority', $rotation->priority), + Filter::greaterThan('first_handoff', $rotation->first_handoff) + )) + ->orderBy('first_handoff', SORT_ASC) + ->first(); + if ($newerRotation !== null) { + $this->nextHandoff = $getHandoff($newerRotation); + } + } + + $membersRes = $rotation + ->member + ->filter(Filter::equal('deleted', 'n')) + ->filter(Filter::any( + Filter::equal('contact.deleted', 'n'), + Filter::equal('contactgroup.deleted', 'n') + )) + ->orderBy('position', SORT_ASC); + + $members = []; + foreach ($membersRes as $member) { + if ($member->contact_id !== null) { + $members[] = 'contact:' . $member->contact_id; + } else { + $members[] = 'group:' . $member->contactgroup_id; + } + } + + $formData['members'] = implode(',', $members); + + return $formData; + } + + /** + * Whether the form has changes + * + * @return bool + */ + public function hasChanges(): bool + { + $values = $this->getValues(); + $values['members'] = $this->getValue('members'); + + // only keys that are present in $values + $dbValuesToCompare = array_intersect_key($this->fetchDbValues(), $values); + + $checker = static function ($a, $b) use (&$checker) { + if (! is_array($a) || ! is_array($b)) { + return $a <=> $b; + } + + return empty(array_udiff_assoc($a, $b, $checker)) ? 0 : 1; + }; + + return ! empty(array_udiff_assoc($values, $dbValuesToCompare, $checker)); + } }