*/ public function rules(): array { return [ 'preferences' => ['required', 'array', 'min:1', 'max:5'], 'preferences.*.festival_section_id' => ['required', 'ulid'], 'preferences.*.priority' => ['required', 'integer', 'min:1', 'max:5'], ]; } public function withValidator($validator): void { $validator->after(function ($validator) { $preferences = $this->input('preferences', []); if (!is_array($preferences)) { return; } // Priorities must be unique $priorities = array_column($preferences, 'priority'); if (count($priorities) !== count(array_unique($priorities))) { $validator->errors()->add('preferences', 'Priorities must be unique within the request.'); } // Validate section IDs belong to the event $event = $this->route('event'); $person = $this->route('person'); if (!$event || !$person) { return; } // Valid sections: own event's sections + parent festival's cross_event sections $validSectionIds = FestivalSection::where('event_id', $event->id) ->pluck('id'); if ($event->parent_event_id) { $parentCrossEventSections = FestivalSection::where('event_id', $event->parent_event_id) ->where('type', 'cross_event') ->pluck('id'); $validSectionIds = $validSectionIds->merge($parentCrossEventSections); } foreach ($preferences as $index => $pref) { $sectionId = $pref['festival_section_id'] ?? null; if ($sectionId && !$validSectionIds->contains($sectionId)) { $validator->errors()->add( "preferences.{$index}.festival_section_id", 'Section does not belong to this event.' ); } } }); } }