From 3ec338457ec99ea9620be001b4cb330dc5d16012 Mon Sep 17 00:00:00 2001 From: Jeppe B <2jepp9350@gmail.com> Date: Wed, 11 Mar 2026 13:54:11 +0100 Subject: [PATCH] Clarify self-serve entity relationships and naming intent --- .../department_selfserve_conditions_o.php | 14 +++++-- .../department_selfserve_questions_o.php | 15 +++++-- .../objects/department_selfserve_tasks_o.php | 42 +++++++++++++++---- .../departmentSelfserveConditionsRoute.php | 10 ++--- .../departmentSelfserveQuestionsRoute.php | 4 +- .../routes/departmentSelfserveTasksRoute.php | 8 ++-- 6 files changed, 69 insertions(+), 24 deletions(-) diff --git a/services/nginx/app/objects/department_selfserve_conditions_o.php b/services/nginx/app/objects/department_selfserve_conditions_o.php index 70e1d311..7640c4dd 100644 --- a/services/nginx/app/objects/department_selfserve_conditions_o.php +++ b/services/nginx/app/objects/department_selfserve_conditions_o.php @@ -11,10 +11,18 @@ class department_selfserve_conditions_o extends db { use db_object_t; + /** + * Canonical relationship model: + * - A condition is a reusable logical node. + * - `condition_id` on this entity is a parent condition id, enabling nested condition trees. + * - Questions may reference a condition as their gate. + * - Tasks may reference a question as their gate (legacy stored in tasks.condition_id). + */ + public object_property $department; // The department id public object_property $lane; // The lane id public object_property $product; // The product id - public object_property $condition_id; // The condition id (optional) + public object_property $condition_id; // Parent condition id for nesting/grouping (nullable) public object_property $name; // The condition name public object_property $description; // The task description public object_property $created_at; @@ -34,7 +42,7 @@ class department_selfserve_conditions_o extends db * @param int $product The product id * @param string $name The condition name * @param string $description The condition description - * @param int|null $condition_id The condition id (optional) + * @param int|null $condition_id Optional parent condition id for nesting/grouping. * @return department_selfserve_conditions_o * @throws Exception If the object was not created successfully */ @@ -102,4 +110,4 @@ class department_selfserve_conditions_o extends db 'updated_at' => (string)$this->updated_at->value(), ]; } -} \ No newline at end of file +} diff --git a/services/nginx/app/objects/department_selfserve_questions_o.php b/services/nginx/app/objects/department_selfserve_questions_o.php index 06bdf1a5..7ea2214a 100644 --- a/services/nginx/app/objects/department_selfserve_questions_o.php +++ b/services/nginx/app/objects/department_selfserve_questions_o.php @@ -16,7 +16,14 @@ class department_selfserve_questions_o extends db public object_property $department; // The department id public object_property $lane; // The lane id public object_property $product; // The product id - public object_property $condition_id; // The question condition object id (if applicable) + /** + * Canonical relationship model: + * - Question `condition_id` references `department_selfserve_conditions.id` and means + * "show/ask this question only when this parent condition is met". + * - Tasks are linked to questions (not conditions); tasks use adapter methods in tasks object + * while persisting to legacy `department_selfserve_tasks.condition_id`. + */ + public object_property $condition_id; // Parent condition id that gates this question (nullable) public object_property $question; // The question text public object_property $description; // The question description public object_property $order_priority; // The order priority of the question (lower numbers are shown first) @@ -37,7 +44,7 @@ class department_selfserve_questions_o extends db * @param int $product The product id * @param string $question The question text * @param string $description The question description - * @param int|null $condition_id The question condition_id (if applicable) + * @param int|null $condition_id Optional parent condition id that gates this question. * @param int $order_priority The order priority of the question (lower numbers are shown first) * @return department_selfserve_questions_o * @throws Exception If the object was not created successfully @@ -96,6 +103,8 @@ class department_selfserve_questions_o extends db self::requireSelected(); global $db; $id = (int)$this->id; + // Deleting a question detaches dependent tasks by clearing the legacy + // `department_selfserve_tasks.condition_id` link (which semantically stores question id). $sql = "UPDATE department_selfserve_tasks SET condition_id = NULL WHERE condition_id = $id"; $db->query($sql); $this->trait_delete(); @@ -117,4 +126,4 @@ class department_selfserve_questions_o extends db 'updated_at' => (string)$this->updated_at->value(), ]; } -} \ No newline at end of file +} diff --git a/services/nginx/app/objects/department_selfserve_tasks_o.php b/services/nginx/app/objects/department_selfserve_tasks_o.php index 307f9513..0a14391b 100644 --- a/services/nginx/app/objects/department_selfserve_tasks_o.php +++ b/services/nginx/app/objects/department_selfserve_tasks_o.php @@ -13,10 +13,18 @@ class department_selfserve_tasks_o extends db { use db_object_t; + /** + * Canonical relationship model for self-serve entities: + * - Conditions can be nested via `department_selfserve_conditions.condition_id` (parent condition id). + * - Questions can optionally be gated by a condition via `department_selfserve_questions.condition_id` (parent condition id). + * - Tasks can optionally be gated by a question. For backward compatibility this is stored in + * `department_selfserve_tasks.condition_id`, but semantically this is a question reference. + */ + public object_property $department; // The department id public object_property $lane; // The lane id public object_property $product; // The product id - public object_property $condition_id; // The question id (if conditional task) + public object_property $condition_id; // Legacy column name: stores question id that gates this task (nullable) public object_property $task; // The task public object_property $description; // The task description public object_property $order_priority; // The order priority of the task (lower numbers are shown first) @@ -54,7 +62,7 @@ class department_selfserve_tasks_o extends db * @param int $department The department id * @param int $lane The lane id * @param int $product The product id - * @param int|null $condition_id The question id (if conditional task) + * @param int|null $question_id Optional question id that gates this task. Persisted in legacy `condition_id` column. * @param string $task The task text * @param string $description The task description * @param int $order_priority The order priority of the task (lower numbers are shown first) @@ -64,7 +72,7 @@ class department_selfserve_tasks_o extends db * @return department_selfserve_tasks_o * @throws Exception If the object was not created successfully */ - public function add(int $department, int $lane, int $product, int|null $condition_id, string $task, string $description, int $order_priority = 0, ?array $services = null, array|string|null $buttons = null, int|null $dynamic_images_vehicle_type = null): self + public function add(int $department, int $lane, int $product, int|null $question_id, string $task, string $description, int $order_priority = 0, ?array $services = null, array|string|null $buttons = null, int|null $dynamic_images_vehicle_type = null): self { global /** @var db $db */ $db; @@ -72,8 +80,8 @@ class department_selfserve_tasks_o extends db $department = (int)$department; $lane = (int)$lane; $product = (int)$product; - if (!is_null($condition_id)) { - $condition_id = (int)$condition_id; + if (!is_null($question_id)) { + $question_id = (int)$question_id; } $task = $db->escape_string($task); $description = $db->escape_string($description); @@ -124,7 +132,7 @@ class department_selfserve_tasks_o extends db 'department' => $department, 'lane' => $lane, 'product' => $product, - ...(!is_null($condition_id) ? ['condition_id' => $condition_id] : []), // If the question is null, it will be set to null in the database + ...(!is_null($question_id) ? ['condition_id' => $question_id] : []), // Legacy column name; contains the gating question id 'task' => $task, 'description' => $description, 'order_priority' => $order_priority, @@ -181,6 +189,26 @@ class department_selfserve_tasks_o extends db ]; } + /** + * Adapter for canonical naming. + * @return int|null Question id that gates this task. + */ + public function getQuestionId(): ?int + { + $value = $this->condition_id->value(); + return is_null($value) ? null : (int)$value; + } + + /** + * Adapter for canonical naming while persisting to legacy `condition_id` column. + * @param int|null $question_id + * @return void + */ + public function setQuestionId(?int $question_id): void + { + $this->condition_id->set(is_null($question_id) ? null : (int)$question_id); + } + /** * Normalize input for dynamic_images_vehicle_type into a nullable non-negative integer. * Accepts int, string (numeric), null, or empty string (treated as null). @@ -259,4 +287,4 @@ class department_selfserve_tasks_o extends db $ids = array_values(array_unique($ids)); return $ids; } -} \ No newline at end of file +} diff --git a/services/nginx/app/routes/departmentSelfserveConditionsRoute.php b/services/nginx/app/routes/departmentSelfserveConditionsRoute.php index 0bcb6248..f41c5317 100644 --- a/services/nginx/app/routes/departmentSelfserveConditionsRoute.php +++ b/services/nginx/app/routes/departmentSelfserveConditionsRoute.php @@ -98,13 +98,13 @@ class departmentSelfserveConditionsRoute $department = (int)$response->getRequestParameter('department'); $lane = (int)$response->getRequestParameter('lane'); $product = (int)$response->getRequestParameter('product'); - $condition_id = $response->getRequestParameter('condition_id'); + $condition_id = $response->getRequestParameter('condition_id'); // parent condition id for nested condition trees $condition_id = is_null($condition_id) || $condition_id === 'null' ? null : (int)$condition_id; $name = (string)$response->getRequestParameter('name'); $description = (string)$response->getRequestParameter('description'); if (!$department || !$lane || !$product || !$name || !$description) { - $response->error('Missing required fields', 400); + $response->error('Missing required fields: department, lane, product, name, and description', 400); } $authorized_department_ids = $user->getGroup()->getDepartments(); @@ -143,7 +143,7 @@ class departmentSelfserveConditionsRoute if ($user) { $id = (int)$response->getRequestParameter('id'); if (!$id) { - $response->error('Missing required fields', 400); + $response->error('Missing required fields: id', 400); } $condition_o = new department_selfserve_conditions_o(); @@ -171,7 +171,7 @@ class departmentSelfserveConditionsRoute $condition_o->product->update((int)$response->getRequestParameter('product')); } if ($response->isRequestParameterSet('condition_id')) { - $condition_id = $response->getRequestParameter('condition_id'); + $condition_id = $response->getRequestParameter('condition_id'); // parent condition id for nested condition trees $condition_o->condition_id->update(is_null($condition_id) || $condition_id === 'null' ? null : (int)$condition_id); } if ($response->isRequestParameterSet('name')) { @@ -200,7 +200,7 @@ class departmentSelfserveConditionsRoute if ($user) { $id = (int)$response->getRequestParameter('id'); if (!$id) { - $response->error('Missing required fields', 400); + $response->error('Missing required fields: id', 400); } $condition_o = new department_selfserve_conditions_o(); diff --git a/services/nginx/app/routes/departmentSelfserveQuestionsRoute.php b/services/nginx/app/routes/departmentSelfserveQuestionsRoute.php index fa59d80f..8a8ed076 100644 --- a/services/nginx/app/routes/departmentSelfserveQuestionsRoute.php +++ b/services/nginx/app/routes/departmentSelfserveQuestionsRoute.php @@ -100,11 +100,11 @@ class departmentSelfserveQuestionsRoute $product = (int)$response->getRequestParameter('product'); $question = (string)$response->getRequestParameter('question'); $description = (string)$response->getRequestParameter('description'); - $condition_id = $response->isRequestParameterSet('condition_id') ? (int)$response->getRequestParameter('condition_id') : null; + $condition_id = $response->isRequestParameterSet('condition_id') ? (int)$response->getRequestParameter('condition_id') : null; // parent condition id that gates this question $order_priority = (int)($response->getRequestParameter('order_priority') ?? 0); if (!$department || !$lane || !$product || !$question || !$description) { - $response->error('Missing required fields', 400); + $response->error('Missing required fields: department, lane, product, question, and description', 400); } $authorized_department_ids = $user->getGroup()->getDepartments(); diff --git a/services/nginx/app/routes/departmentSelfserveTasksRoute.php b/services/nginx/app/routes/departmentSelfserveTasksRoute.php index 6280354c..10d916ba 100644 --- a/services/nginx/app/routes/departmentSelfserveTasksRoute.php +++ b/services/nginx/app/routes/departmentSelfserveTasksRoute.php @@ -106,7 +106,7 @@ class departmentSelfserveTasksRoute $department = (int)$response->getRequestParameter('department'); $lane = (int)$response->getRequestParameter('lane'); $product = (int)$response->getRequestParameter('product'); - $condition_id = $response->isRequestParameterSet('condition_id') ? (int)$response->getRequestParameter('condition_id') : null; + $question_id = $response->isRequestParameterSet('condition_id') ? (int)$response->getRequestParameter('condition_id') : null; // legacy request field: condition_id $task = (string)$response->getRequestParameter('task'); $description = (string)$response->getRequestParameter('description'); $order_priority = (int)($response->getRequestParameter('order_priority') ?? 0); @@ -168,7 +168,7 @@ class departmentSelfserveTasksRoute } if (!$department || !$lane || !$product || !$task || !$description) { - $response->error('Missing required fields', 400); + $response->error('Missing required fields: department, lane, product, task, and description', 400); } $authorized_department_ids = $user->getGroup()->getDepartments(); @@ -181,7 +181,7 @@ class departmentSelfserveTasksRoute $department, $lane, $product, - $condition_id, + $question_id, $task, $description, $order_priority, @@ -237,7 +237,7 @@ class departmentSelfserveTasksRoute $task_o->product->set((int)self::getParameter('product')); } if (self::isParametersSet(['condition_id'])) { - $task_o->condition_id->set(self::getParameter('condition_id') === null ? null : (int)self::getParameter('condition_id')); + $task_o->setQuestionId(self::getParameter('condition_id') === null ? null : (int)self::getParameter('condition_id')); } if (self::isParametersSet(['task'])) { $task_o->task->set((string)self::getParameter('task'));