diff --git a/app/Http/Controllers/StudentsController.php b/app/Http/Controllers/StudentsController.php index cc33bd36..ec11c474 100644 --- a/app/Http/Controllers/StudentsController.php +++ b/app/Http/Controllers/StudentsController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Events\StudentViewed; +use App\Http\Requests\StudentUpdateRequest; use App\Student; use App\StudentData; use App\User; @@ -50,7 +51,7 @@ public function index(): View|ViewContract /** * Create a new student research application. */ - public function create(Request $request): RedirectResponse + public function create(StudentUpdateRequest $request): RedirectResponse { $student = $request->user()->studentProfiles->first() ?? $this->store($request); @@ -127,7 +128,7 @@ public function edit(Student $student): View|ViewContract /** * Update the specified student research application in the database. */ - public function update(Request $request, Student $student): RedirectResponse + public function update(StudentUpdateRequest $request, Student $student): RedirectResponse { $updated = $student->update([ 'full_name' => $request->full_name, diff --git a/app/Http/Requests/StudentUpdateRequest.php b/app/Http/Requests/StudentUpdateRequest.php new file mode 100644 index 00000000..283769e0 --- /dev/null +++ b/app/Http/Requests/StudentUpdateRequest.php @@ -0,0 +1,152 @@ + + */ + public function rules(): array + { + $schools = Student::participatingSchools()->keys()->all(); + + return [ + 'full_name' => 'required|string', + 'faculty.*' => [ + 'integer', + Rule::exists('profiles', 'id')->where('public', 1), + ], + 'research_profile.major' => [ + 'required', + Rule::in(StudentData::majors()) + ], + 'research_profile.brief_intro' => 'sometimes|string|max:280', + 'research_profile.intro' => 'sometimes|string|max:280', + 'research_profile.interest' => 'sometimes|string|max:280', + 'research_profile.schools' => 'required|array', + 'research_profile.schools.*' => [ + 'sometimes', + 'string', + Rule::in($schools), + ], + 'research_profile.availability' => 'sometimes|array', + 'research_profile.semesters' => 'required|array', + 'research_profile.semesters.*' => [ + 'required', + 'string', + 'regex:/\b(Winter|Spring|Summer|Fall)\s\d{4}\b/', + ], + 'research_profile.languages' => 'sometimes|array', + 'research_profile.languages.*' => [ + Rule::in(array_keys(StudentData::$languages)), + ], + 'research_profile.lang_proficiency' => 'sometimes|array', + 'research_profile.lang_proficiency.*' => 'string|in:limited,basic,professional,native', + 'research_profile.*' => $this->validateCustomQuestions(), + 'research_profile.graduation_date' => 'required|date|after:today', + 'research_profile.credit' => 'required|numeric|between:-1,1', + ]; + } + + /** + * Get the error messages for the defined validation rules. + * + * @return array + */ + public function messages() + { + return [ + 'faculty.*.exists' => 'The faculty member(s) selected could not be validated.', + 'faculty.required' => 'You must selest at least one faculty member that you would like to work with.', + 'research_profile.major.required' => 'The major field is required.', + 'research_profile.semesters.required' => 'At least one semester is required.', + 'research_profile.schools.required' => 'At least one school is required.', + 'research_profile.schools.*.in' => 'Wrong value :input. Please select a valid school.', + 'research_profile.semesters.*.regex' => 'Wrong value selected for semester. The semester must follow the "Season YYYY" format.', + 'research_profile.languages.*.in' => 'Wrong value :input selected for language. Please select a valid language.', + 'research_profile.graduation_date.after' => 'The graduation date must be in the future.', + 'research_profile.credit.between' => 'The credit value is invalid.', + ]; + } + + /** + * Get custom attributes for validator errors. + * + * @return array + */ + public function attributes() + { + return [ + 'full_name' => 'display name', + 'research_profile.semesters' => 'semesters', + 'research_profile.schools' => 'school', + 'research_profile.graduation_date' => 'graduation date', + 'research_profile.credit' => 'credit', + 'research_profile.brief_intro' => 'research opportunity reasons', + 'research_profile.intro' => 'future goals', + 'research_profile.interest' => 'interest', + ]; + } + + /** + * Check if attr is a custom question and validates the value based on the question type. + * Supported question types: + * - `yes_no`: Validates that the value is either "1" or "0". + * - Other types: Ensures the value does not exceed 1000 characters if it is a string. + * + * @return \Closure A validation closure to be used in validation rules. + */ + public function validateCustomQuestions() + { + return function($attr, $value, $fail) { + + $errors = []; + + $questions = StudentData::customQuestions()->flatten(1); + $question_name = substr($attr, strpos($attr, '.') + 1); + + $question_settings = $questions->first(fn($q) => isset($q['name'], $q['type']) && $q['name'] === $question_name); + + if (is_null($question_settings)) { + return false; + } + + switch ($question_settings['type']) { + case 'yes_no': + if (!in_array($value, ["1", "0"], true)) { + $formatted_question_name = $this->formatQuestionName($question_name); + $errors[] = "The answer to {$formatted_question_name} must be Yes or No."; + } + break; + default: + if (is_string($value) && strlen($value) > 1000) { + $formatted_question_name = $this->formatQuestionName($question_name); + $errors[] = "The value for the {$formatted_question_name} question cannot exceed 1000 characters."; + } + break; + } + + foreach ($errors as $error) { + $fail($error); + } + }; + } + + /** + * Helper function to format question name + * @param string $question_name + * @return string + */ + function formatQuestionName($question_name) { + return strtolower(str_replace('_', ' ', $question_name)); + } +} diff --git a/resources/views/students/edit.blade.php b/resources/views/students/edit.blade.php index 45772032..18794325 100644 --- a/resources/views/students/edit.blade.php +++ b/resources/views/students/edit.blade.php @@ -29,6 +29,16 @@
There are some errors. Please correct them and try again.
+ @foreach ($errors->all() as $error) + {{ $error }}Complete and submit your student research application below. This application will not be public, but will be made available to faculty researchers who may be looking for students. After submitting, you can always come back later to edit or withdraw your student research application.