db = Database::getConnection(); } catch (\Throwable $e) { error_log("Failed to get DB connection in ShiftDefinitionController: " . $e->getMessage()); http_response_code(500); echo json_encode(['error' => 'Internal Server Error - DB Connection']); exit; } } /** * Mostra un elenco di definizioni di turno. * GET /api/shift-definitions */ public function index(): void { try { $queryBuilder = $this->db->createQueryBuilder(); $shifts = $queryBuilder ->select('*') ->from('shift_definitions') ->orderBy('start_time', 'ASC') // Ordina per orario inizio ->fetchAllAssociative(); echo json_encode($shifts); } catch (\Throwable $e) { http_response_code(500); error_log("Error fetching shift definitions: " . $e->getMessage()); echo json_encode(['error' => 'Failed to fetch shift definitions']); } } /** * Mostra una definizione di turno specifica. * GET /api/shift-definitions/{id} */ public function show(array $vars): void { $id = $vars['id'] ?? null; if ($id === null) { http_response_code(400); echo json_encode(['error' => 'Missing shift definition ID']); return; } try { $shift = $this->db->fetchAssociative('SELECT * FROM shift_definitions WHERE id = ?', [$id]); if ($shift === false) { http_response_code(404); echo json_encode(['error' => "Shift definition with ID {$id} not found"]); } else { echo json_encode($shift); } } catch (\Throwable $e) { http_response_code(500); error_log("Error fetching shift definition {$id}: " . $e->getMessage()); echo json_encode(['error' => 'Failed to fetch shift definition']); } } /** * Crea una nuova definizione di turno. * POST /api/shift-definitions */ public function store(): void { $input = json_decode(file_get_contents('php://input'), true); if (json_last_error() !== JSON_ERROR_NONE || !is_array($input)) { http_response_code(400); echo json_encode(['error' => 'Invalid JSON input']); return; } // Validazione campi obbligatori if (empty($input['name']) || empty($input['start_time']) || empty($input['end_time'])) { http_response_code(400); echo json_encode(['error' => 'Missing required fields: name, start_time, end_time']); return; } // Validazione formato orario (HH:MM o HH:MM:SS) $timeRegex = '/^([01]\d|2[0-3]):([0-5]\d)(:([0-5]\d))?$/'; if (!preg_match($timeRegex, $input['start_time']) || !preg_match($timeRegex, $input['end_time'])) { http_response_code(400); echo json_encode(['error' => 'Invalid time format. Use HH:MM or HH:MM:SS.']); return; } // Validazione logica orari (opzionale, ma utile) if (strtotime($input['end_time']) <= strtotime($input['start_time'])) { http_response_code(400); echo json_encode(['error' => 'End time must be after start time.']); return; } $dataToInsert = [ 'name' => $input['name'], 'start_time' => $input['start_time'], 'end_time' => $input['end_time'], 'notes' => $input['notes'] ?? null, ]; try { $result = $this->db->insert('shift_definitions', $dataToInsert); if ($result === false || $result === 0) { throw new \Exception("Failed to insert shift definition."); } $newId = $this->db->lastInsertId(); http_response_code(201); $this->show(['id' => $newId]); } catch (UniqueConstraintViolationException $e) { http_response_code(409); error_log("Error creating shift definition (duplicate name?): " . $e->getMessage()); echo json_encode(['error' => 'Failed to create shift definition. Name might already exist.']); } catch (\Throwable $e) { http_response_code(500); error_log("Error creating shift definition: " . $e->getMessage()); echo json_encode(['error' => 'Failed to create shift definition']); } } /** * Aggiorna una definizione di turno esistente. * PUT /api/shift-definitions/{id} */ public function update(array $vars): void { $id = $vars['id'] ?? null; if ($id === null) { http_response_code(400); echo json_encode(['error' => 'Missing shift definition ID']); return; } $input = json_decode(file_get_contents('php://input'), true); if (json_last_error() !== JSON_ERROR_NONE || !is_array($input)) { http_response_code(400); echo json_encode(['error' => 'Invalid JSON input']); return; } if (empty($input)) { http_response_code(400); echo json_encode(['error' => 'Missing update data']); return; } // Validazione formato orario se presenti $timeRegex = '/^([01]\d|2[0-3]):([0-5]\d)(:([0-5]\d))?$/'; if (isset($input['start_time']) && !preg_match($timeRegex, $input['start_time'])) { http_response_code(400); echo json_encode(['error' => 'Invalid start_time format.']); return; } if (isset($input['end_time']) && !preg_match($timeRegex, $input['end_time'])) { http_response_code(400); echo json_encode(['error' => 'Invalid end_time format.']); return; } // Validazione logica orari se entrambi presenti $startTime = $input['start_time'] ?? null; $endTime = $input['end_time'] ?? null; if ($startTime && $endTime && strtotime($endTime) <= strtotime($startTime)) { http_response_code(400); echo json_encode(['error' => 'End time must be after start time.']); return; } // Validazione con orari esistenti se solo uno viene fornito (più complessa, omessa per brevità) $dataToUpdate = []; $allowedFields = ['name', 'start_time', 'end_time', 'notes']; foreach ($allowedFields as $field) { if (array_key_exists($field, $input)) { $dataToUpdate[$field] = ($input[$field] === '') ? null : $input[$field]; } } if (empty($dataToUpdate)) { http_response_code(400); echo json_encode(['error' => 'No valid fields provided for update']); return; } try { $existing = $this->db->fetchOne('SELECT 1 FROM shift_definitions WHERE id = ?', [$id]); if ($existing === false) { http_response_code(404); echo json_encode(['error' => "Shift definition with ID {$id} not found"]); return; } $this->db->update('shift_definitions', $dataToUpdate, ['id' => $id]); $this->show(['id' => $id]); } catch (UniqueConstraintViolationException $e) { http_response_code(409); error_log("Error updating shift definition {$id} (duplicate name?): " . $e->getMessage()); echo json_encode(['error' => 'Failed to update shift definition. Name might already exist.']); } catch (\Throwable $e) { http_response_code(500); error_log("Error updating shift definition {$id}: " . $e->getMessage()); echo json_encode(['error' => 'Failed to update shift definition']); } } /** * Elimina una definizione di turno. * DELETE /api/shift-definitions/{id} */ public function delete(array $vars): void { $id = $vars['id'] ?? null; if ($id === null) { http_response_code(400); echo json_encode(['error' => 'Missing shift definition ID']); return; } try { // Aggiungere qui controlli se la definizione è usata in turni effettivi? $existing = $this->db->fetchOne('SELECT 1 FROM shift_definitions WHERE id = ?', [$id]); if ($existing === false) { http_response_code(404); echo json_encode(['error' => "Shift definition with ID {$id} not found"]); return; } $deletedRows = $this->db->delete('shift_definitions', ['id' => $id]); if ($deletedRows > 0) { http_response_code(204); // No Content } else { http_response_code(500); error_log("Failed to delete shift definition {$id}, delete returned 0 rows affected."); echo json_encode(['error' => 'Failed to delete shift definition']); } } catch (\Throwable $e) { // Gestire ForeignKeyConstraintViolationException se la definizione è usata altrove http_response_code(500); error_log("Error deleting shift definition {$id}: " . $e->getMessage()); echo json_encode(['error' => 'Failed to delete shift definition. It might be in use.']); } } }