SprintWorkerDayRepository.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Repositories;
  4. use App\Domain\SprintWorkerDay;
  5. use PDO;
  6. final class SprintWorkerDayRepository
  7. {
  8. public function __construct(private readonly PDO $pdo)
  9. {
  10. }
  11. /**
  12. * Build the full [sprint_worker_id][sprint_week_id] => days map for a sprint.
  13. * Cells with no DB row are simply absent from the map (read as 0 by callers).
  14. *
  15. * @return array<int, array<int, float>>
  16. */
  17. public function grid(int $sprintId): array
  18. {
  19. $stmt = $this->pdo->prepare(
  20. 'SELECT swd.sprint_worker_id, swd.sprint_week_id, swd.days
  21. FROM sprint_worker_days swd
  22. JOIN sprint_workers sw ON sw.id = swd.sprint_worker_id
  23. WHERE sw.sprint_id = ?'
  24. );
  25. $stmt->execute([$sprintId]);
  26. $out = [];
  27. foreach ($stmt as $row) {
  28. $swId = (int) $row['sprint_worker_id'];
  29. $wkId = (int) $row['sprint_week_id'];
  30. $days = (float) $row['days'];
  31. $out[$swId][$wkId] = $days;
  32. }
  33. return $out;
  34. }
  35. public function find(int $swId, int $weekId): ?SprintWorkerDay
  36. {
  37. $stmt = $this->pdo->prepare(
  38. 'SELECT * FROM sprint_worker_days WHERE sprint_worker_id = ? AND sprint_week_id = ?'
  39. );
  40. $stmt->execute([$swId, $weekId]);
  41. $row = $stmt->fetch();
  42. return is_array($row) ? self::hydrate($row) : null;
  43. }
  44. /**
  45. * All cells for a given sprint_worker (used by removeWorker to snapshot
  46. * rows before the FK cascade wipes them).
  47. *
  48. * @return list<SprintWorkerDay>
  49. */
  50. public function allForSprintWorker(int $swId): array
  51. {
  52. $stmt = $this->pdo->prepare(
  53. 'SELECT * FROM sprint_worker_days WHERE sprint_worker_id = ?'
  54. );
  55. $stmt->execute([$swId]);
  56. $out = [];
  57. foreach ($stmt as $row) {
  58. $out[] = self::hydrate($row);
  59. }
  60. return $out;
  61. }
  62. /**
  63. * All cells for a given sprint_week (used by weeks-shrink to snapshot
  64. * rows before the FK cascade wipes them).
  65. *
  66. * @return list<SprintWorkerDay>
  67. */
  68. public function allForSprintWeek(int $weekId): array
  69. {
  70. $stmt = $this->pdo->prepare(
  71. 'SELECT * FROM sprint_worker_days WHERE sprint_week_id = ?'
  72. );
  73. $stmt->execute([$weekId]);
  74. $out = [];
  75. foreach ($stmt as $row) {
  76. $out[] = self::hydrate($row);
  77. }
  78. return $out;
  79. }
  80. /**
  81. * @param array<string,mixed> $row
  82. */
  83. private static function hydrate(array $row): SprintWorkerDay
  84. {
  85. return new SprintWorkerDay(
  86. id: (int) $row['id'],
  87. sprintWorkerId: (int) $row['sprint_worker_id'],
  88. sprintWeekId: (int) $row['sprint_week_id'],
  89. days: (float) $row['days'],
  90. );
  91. }
  92. /**
  93. * Set days for a single (sprint_worker, sprint_week) cell.
  94. *
  95. * Rules:
  96. * - If the row exists and days are unchanged → no DB write, action=NOOP.
  97. * - If the row doesn't exist and days === 0 → don't insert, action=NOOP.
  98. * - If the row doesn't exist and days > 0 → INSERT, action=CREATE.
  99. * - Otherwise → UPDATE, action=UPDATE.
  100. *
  101. * @return array{action:string, before: ?SprintWorkerDay, after: ?SprintWorkerDay}
  102. */
  103. public function upsert(int $swId, int $weekId, float $days): array
  104. {
  105. $existing = $this->find($swId, $weekId);
  106. if ($existing !== null && abs($existing->days - $days) < 1e-9) {
  107. return ['action' => 'NOOP', 'before' => $existing, 'after' => $existing];
  108. }
  109. if ($existing === null) {
  110. if (abs($days) < 1e-9) {
  111. return ['action' => 'NOOP', 'before' => null, 'after' => null];
  112. }
  113. $stmt = $this->pdo->prepare(
  114. 'INSERT INTO sprint_worker_days (sprint_worker_id, sprint_week_id, days)
  115. VALUES (?, ?, ?)'
  116. );
  117. $stmt->execute([$swId, $weekId, $days]);
  118. $id = (int) $this->pdo->lastInsertId();
  119. $after = new SprintWorkerDay($id, $swId, $weekId, $days);
  120. return ['action' => 'CREATE', 'before' => null, 'after' => $after];
  121. }
  122. $stmt = $this->pdo->prepare('UPDATE sprint_worker_days SET days = ? WHERE id = ?');
  123. $stmt->execute([$days, $existing->id]);
  124. $after = new SprintWorkerDay($existing->id, $swId, $weekId, $days);
  125. return ['action' => 'UPDATE', 'before' => $existing, 'after' => $after];
  126. }
  127. }