tracks()->orderBy('sort')->orderBy('id')->get()->map(fn (CompetitionTrack $t) => $this->toArray($t)); return response()->json([ 'data' => $tracks, ]); } public function store(StoreCompetitionTrackRequest $request, Competition $competition): JsonResponse { /** @var array $validated */ $validated = $request->validated(); $validated = $this->normalizeScoringSheetInPayload($validated); /** @var CompetitionTrack $track */ $track = $competition->tracks()->create($validated); return response()->json($this->toArray($track), 201); } public function show(Competition $competition, CompetitionTrack $track): JsonResponse { return response()->json($this->ensureTrackBelongsAndReturn($competition, $track)); } public function update(UpdateCompetitionTrackRequest $request, Competition $competition, CompetitionTrack $track): JsonResponse { $this->ensureTrackBelongs($competition, $track); /** @var array $validated */ $validated = $request->validated(); $validated = $this->normalizeScoringSheetInPayload($validated); $track->fill($validated); $track->save(); return response()->json($this->toArray($track)); } public function destroy(Competition $competition, CompetitionTrack $track): Response { $this->ensureTrackBelongs($competition, $track); $track->delete(); return response()->noContent(); } /** * @param array $validated * @return array */ private function normalizeScoringSheetInPayload(array $validated): array { if (! array_key_exists('scoring_sheet_json', $validated)) { return $validated; } $raw = $validated['scoring_sheet_json']; if ($raw === null) { $validated['scoring_sheet_json'] = null; return $validated; } $parsed = TrackScoringSheet::parseAndValidateConfig($raw); if ($parsed['ok'] !== true) { throw ValidationException::withMessages([ 'scoring_sheet_json' => [$parsed['message']], ]); } $validated['scoring_sheet_json'] = $parsed['sheet']; return $validated; } /** * @return array */ private function toArray(CompetitionTrack $track): array { $sheet = is_array($track->scoring_sheet_json) ? $track->scoring_sheet_json : null; $configured = TrackScoringSheet::isConfigured($sheet); return [ 'id' => $track->id, 'competition_id' => $track->competition_id, 'track_code' => $track->track_code, 'title' => $track->title, 'description' => $track->description, 'sort' => $track->sort, 'is_enabled' => $track->is_enabled, 'scoring_sheet_json' => $sheet, 'scoring_sheet_configured' => $configured, 'created_at' => $track->created_at?->toIso8601String(), 'updated_at' => $track->updated_at?->toIso8601String(), ]; } /** * @return array */ private function ensureTrackBelongsAndReturn(Competition $competition, CompetitionTrack $track): array { $this->ensureTrackBelongs($competition, $track); return $this->toArray($track); } private function ensureTrackBelongs(Competition $competition, CompetitionTrack $track): void { if ($track->competition_id !== $competition->id) { abort(404); } } }