['sometimes', 'nullable', 'string'], 'height' => ['sometimes', 'nullable', 'integer'], 'locale' => ['sometimes', 'string', Rule::in(config('app.supported_locales', ['fr', 'en']))], 'avatar' => ['sometimes', 'nullable', 'image', 'max:2048'], 'nutritionGoals' => ['sometimes', 'array'], 'nutritionGoals.calories' => ['required_with:nutritionGoals', 'integer', 'min:0', 'max:100000'], 'nutritionGoals.proteins' => ['required_with:nutritionGoals', 'numeric', 'min:0', 'max:10000'], 'nutritionGoals.carbs' => ['required_with:nutritionGoals', 'numeric', 'min:0', 'max:10000'], 'nutritionGoals.fats' => ['required_with:nutritionGoals', 'numeric', 'min:0', 'max:10000'], 'physicalActivityLevel' => ['sometimes', Rule::enum(PhysicalActivityLevel::class)], 'weightGoal' => ['sometimes', Rule::enum(WeightGoal::class)], 'pacePreference' => ['sometimes', Rule::enum(PacePreference::class)], 'sex' => ['sometimes', Rule::enum(UserSex::class)], 'dateOfBirth' => ['sometimes', 'nullable', 'date_format:Y-m-d', 'before:today', 'after:1900-01-01'], ]; } public function messages(): array { return [ 'avatar.max' => __('api.validation.image_max_2mb'), 'nutritionGoals.calories.integer' => __('api.validation.calories_integer'), 'nutritionGoals.*.required_with' => __('api.validation.nutrition_goals_required'), 'nutritionGoals.*.min' => __('api.validation.nutrition_goals_positive'), 'nutritionGoals.*.max' => __('api.validation.nutrition_goals_max'), ]; } protected function prepareForValidation(): void { if ($this->has('locale')) { $this->merge([ 'locale' => $this->normalizeLocale((string) $this->input('locale')), ]); } } private function normalizeLocale(string $locale): string { $locale = strtolower(str_replace('_', '-', $locale)); return explode('-', $locale)[0]; } }