middleware('auth:sanctum')->only(['store', 'update', 'destroy']); } /** * Seznam QSO – s filtrováním podle log_id, round_id, band, call_like, dx_call. */ public function index(Request $request): JsonResponse { $perPage = (int) $request->get('per_page', 100); $query = LogQso::query() ->with('log'); if ($request->filled('log_id')) { $query->where('log_id', (int) $request->get('log_id')); } if ($request->filled('round_id')) { $roundId = (int) $request->get('round_id'); $query->whereHas('log', function ($q) use ($roundId) { $q->where('round_id', $roundId); }); } if ($request->filled('band')) { $query->where('band', $request->get('band')); } if ($request->filled('call_like')) { $raw = strtoupper((string) $request->get('call_like')); $pattern = str_replace(['*', '?'], ['%', '_'], $raw); if (strpos($pattern, '%') === false && strpos($pattern, '_') === false) { $pattern = '%' . $pattern . '%'; } $query->where(function ($q) use ($pattern) { $q->whereRaw('UPPER(my_call) LIKE ?', [$pattern]) ->orWhereRaw('UPPER(dx_call) LIKE ?', [$pattern]); }); } if ($request->filled('dx_call')) { $query->where('dx_call', $request->get('dx_call')); } if ($request->filled('exclude_log_id')) { $query->where('log_id', '!=', (int) $request->get('exclude_log_id')); } if ($request->filled('exclude_log_qso_id')) { $query->where('id', '!=', (int) $request->get('exclude_log_qso_id')); } $items = $query ->orderBy('log_id') ->orderBy('qso_index') ->paginate($perPage); return response()->json($items); } /** * Vytvoření QSO řádku. * Typicky voláno parserem EDI, ne přímo z UI. */ public function store(Request $request): JsonResponse { $this->authorize('create', LogQso::class); $data = $this->validateData($request); $item = LogQso::create($data); $item->load('log'); return response()->json($item, 201); } /** * Detail jednoho QSO řádku. */ public function show(LogQso $logQso): JsonResponse { $logQso->load('log'); return response()->json($logQso); } /** * Aktualizace QSO (partial update). * Praktické pro ruční korekce / debug. */ public function update(Request $request, LogQso $logQso): JsonResponse { $this->authorize('update', $logQso); $data = $this->validateData($request, partial: true); $logQso->fill($data); $logQso->save(); $logQso->load('log'); return response()->json($logQso); } /** * Smazání QSO. */ public function destroy(LogQso $logQso): JsonResponse { $this->authorize('delete', $logQso); $logQso->delete(); return response()->json(null, 204); } /** * Validace pro store / update. */ protected function validateData(Request $request, bool $partial = false): array { $required = $partial ? 'sometimes' : 'required'; return $request->validate([ 'log_id' => [$required, 'integer', 'exists:logs,id'], 'qso_index' => ['sometimes', 'nullable', 'integer', 'min:0'], 'time_on' => ['sometimes', 'nullable', 'date'], 'band' => ['sometimes', 'nullable', 'string', 'max:10'], 'freq_khz' => ['sometimes', 'nullable', 'integer', 'min:0'], 'mode' => ['sometimes', 'nullable', 'string', 'max:5'], 'my_call' => ['sometimes', 'nullable', 'string', 'max:20'], 'my_rst' => ['sometimes', 'nullable', 'string', 'max:10'], 'my_serial' => ['sometimes', 'nullable', 'string', 'max:10'], 'my_locator' => ['sometimes', 'nullable', 'string', 'max:6'], 'dx_call' => ['sometimes', 'nullable', 'string', 'max:20'], 'dx_rst' => ['sometimes', 'nullable', 'string', 'max:10'], 'dx_serial' => ['sometimes', 'nullable', 'string', 'max:10'], 'dx_locator' => ['sometimes', 'nullable', 'string', 'max:6'], 'points' => ['sometimes', 'nullable', 'integer'], 'wwl' => ['sometimes', 'nullable', 'string', 'max:6'], 'dxcc' => ['sometimes', 'nullable', 'string', 'max:10'], 'is_duplicate'=> ['sometimes', 'boolean'], 'is_valid' => ['sometimes', 'boolean'], 'raw_line' => ['sometimes', 'nullable', 'string', 'max:500'], ]); } }