expectsJson() || $request->isJson(), 422); $validated = $request->validate([ 'event_type' => ['required', 'string', Rule::in(AcademyAnalyticsEventType::values())], 'content_type' => ['nullable', 'string', Rule::in(AcademyAnalyticsContentType::values())], 'content_id' => ['nullable', 'integer', 'min:1'], 'metadata' => ['nullable', 'array'], 'visitor_id' => ['nullable', 'string', 'max:120'], 'session_id' => ['nullable', 'string', 'max:120'], 'url' => ['nullable', 'string', 'max:4000'], 'route_name' => ['nullable', 'string', 'max:255'], 'referrer' => ['nullable', 'string', 'max:4000'], 'utm_source' => ['nullable', 'string', 'max:255'], 'utm_medium' => ['nullable', 'string', 'max:255'], 'utm_campaign' => ['nullable', 'string', 'max:255'], ]); if (isset($validated['metadata']) && strlen((string) json_encode($validated['metadata'])) > 8192) { return response()->json([ 'message' => 'Metadata payload is too large.', ], 422); } $contentType = $validated['content_type'] ?? null; $contentId = $validated['content_id'] ?? null; if ($contentType !== null && AcademyAnalyticsContentType::requiresContentId($contentType) && $contentId === null) { return response()->json([ 'message' => 'content_id is required for this content type.', ], 422); } if ($contentType !== null && $contentId !== null && ! $this->resolver->exists($contentType, (int) $contentId)) { return response()->json([ 'message' => 'Unknown Academy analytics content target.', ], 422); } if (($validated['event_type'] ?? null) === AcademyAnalyticsEventType::SEARCH_RESULT_CLICK) { validator([ 'content_type' => $contentType, 'content_id' => $contentId, 'metadata' => $validated['metadata'] ?? [], ], [ 'content_type' => ['required', 'string', Rule::in([ AcademyAnalyticsContentType::PROMPT, AcademyAnalyticsContentType::LESSON, AcademyAnalyticsContentType::COURSE, AcademyAnalyticsContentType::PROMPT_PACK, AcademyAnalyticsContentType::CHALLENGE, ])], 'content_id' => ['required', 'integer', 'min:1'], 'metadata.query' => ['required', 'string', 'max:120'], 'metadata.normalized_query' => ['required', 'string', 'max:120'], 'metadata.results_count' => ['required', 'integer', 'min:0'], 'metadata.position' => ['nullable', 'integer', 'min:1'], 'metadata.source' => ['nullable', 'string', 'max:120'], 'metadata.filters' => ['nullable', 'array'], ])->validate(); } $this->analytics->track($validated, $request->user(), $request); return response()->json([ 'ok' => true, ]); } }