| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- <?php
- declare(strict_types=1);
- namespace App\Repositories;
- use App\Domain\SprintWorkerDay;
- use PDO;
- final class SprintWorkerDayRepository
- {
- public function __construct(private readonly PDO $pdo)
- {
- }
- /**
- * Build the full [sprint_worker_id][sprint_week_id] => days map for a sprint.
- * Cells with no DB row are simply absent from the map (read as 0 by callers).
- *
- * @return array<int, array<int, float>>
- */
- public function grid(int $sprintId): array
- {
- $stmt = $this->pdo->prepare(
- 'SELECT swd.sprint_worker_id, swd.sprint_week_id, swd.days
- FROM sprint_worker_days swd
- JOIN sprint_workers sw ON sw.id = swd.sprint_worker_id
- WHERE sw.sprint_id = ?'
- );
- $stmt->execute([$sprintId]);
- $out = [];
- foreach ($stmt as $row) {
- $swId = (int) $row['sprint_worker_id'];
- $wkId = (int) $row['sprint_week_id'];
- $days = (float) $row['days'];
- $out[$swId][$wkId] = $days;
- }
- return $out;
- }
- public function find(int $swId, int $weekId): ?SprintWorkerDay
- {
- $stmt = $this->pdo->prepare(
- 'SELECT * FROM sprint_worker_days WHERE sprint_worker_id = ? AND sprint_week_id = ?'
- );
- $stmt->execute([$swId, $weekId]);
- $row = $stmt->fetch();
- return is_array($row) ? self::hydrate($row) : null;
- }
- /**
- * All cells for a given sprint_worker (used by removeWorker to snapshot
- * rows before the FK cascade wipes them).
- *
- * @return list<SprintWorkerDay>
- */
- public function allForSprintWorker(int $swId): array
- {
- $stmt = $this->pdo->prepare(
- 'SELECT * FROM sprint_worker_days WHERE sprint_worker_id = ?'
- );
- $stmt->execute([$swId]);
- $out = [];
- foreach ($stmt as $row) {
- $out[] = self::hydrate($row);
- }
- return $out;
- }
- /**
- * All cells for a given sprint_week (used by weeks-shrink to snapshot
- * rows before the FK cascade wipes them).
- *
- * @return list<SprintWorkerDay>
- */
- public function allForSprintWeek(int $weekId): array
- {
- $stmt = $this->pdo->prepare(
- 'SELECT * FROM sprint_worker_days WHERE sprint_week_id = ?'
- );
- $stmt->execute([$weekId]);
- $out = [];
- foreach ($stmt as $row) {
- $out[] = self::hydrate($row);
- }
- return $out;
- }
- /**
- * @param array<string,mixed> $row
- */
- private static function hydrate(array $row): SprintWorkerDay
- {
- return new SprintWorkerDay(
- id: (int) $row['id'],
- sprintWorkerId: (int) $row['sprint_worker_id'],
- sprintWeekId: (int) $row['sprint_week_id'],
- days: (float) $row['days'],
- );
- }
- /**
- * Set days for a single (sprint_worker, sprint_week) cell.
- *
- * Rules:
- * - If the row exists and days are unchanged → no DB write, action=NOOP.
- * - If the row doesn't exist and days === 0 → don't insert, action=NOOP.
- * - If the row doesn't exist and days > 0 → INSERT, action=CREATE.
- * - Otherwise → UPDATE, action=UPDATE.
- *
- * @return array{action:string, before: ?SprintWorkerDay, after: ?SprintWorkerDay}
- */
- public function upsert(int $swId, int $weekId, float $days): array
- {
- $existing = $this->find($swId, $weekId);
- if ($existing !== null && abs($existing->days - $days) < 1e-9) {
- return ['action' => 'NOOP', 'before' => $existing, 'after' => $existing];
- }
- if ($existing === null) {
- if (abs($days) < 1e-9) {
- return ['action' => 'NOOP', 'before' => null, 'after' => null];
- }
- $stmt = $this->pdo->prepare(
- 'INSERT INTO sprint_worker_days (sprint_worker_id, sprint_week_id, days)
- VALUES (?, ?, ?)'
- );
- $stmt->execute([$swId, $weekId, $days]);
- $id = (int) $this->pdo->lastInsertId();
- $after = new SprintWorkerDay($id, $swId, $weekId, $days);
- return ['action' => 'CREATE', 'before' => null, 'after' => $after];
- }
- $stmt = $this->pdo->prepare('UPDATE sprint_worker_days SET days = ? WHERE id = ?');
- $stmt->execute([$days, $existing->id]);
- $after = new SprintWorkerDay($existing->id, $swId, $weekId, $days);
- return ['action' => 'UPDATE', 'before' => $existing, 'after' => $after];
- }
- }
|