validate([ 'identity_number' => ['required', 'string', 'max:30'], 'access_code' => ['required', 'string', 'max:20'], ], [ 'identity_number.required' => 'NIS atau NISN wajib diisi.', 'access_code.required' => 'Kode akses wajib diisi.', ]); $student = Student::query() ->where('self_service_access_code', strtoupper(trim($data['access_code']))) ->where(function ($query) use ($data) { $query->where('student_number', $data['identity_number']) ->orWhere('nisn', $data['identity_number']); }) ->first(); if (! $student) { return back() ->withInput() ->withErrors([ 'identity_number' => 'Data siswa tidak ditemukan atau kode akses tidak cocok.', ]); } $request->session()->put('student_self_service.student_id', $student->id); $request->session()->put('student_self_service.verified_at', now()->toIso8601String()); $student->forceFill([ 'self_service_last_access_at' => now(), 'self_service_status' => $student->self_service_status === 'submitted' ? 'submitted' : 'draft', ])->save(); return redirect()->route('public.student-self-service.edit'); } public function edit(Request $request): View|RedirectResponse { $student = $this->resolveVerifiedStudent($request); if (! $student) { return redirect()->route('public.student-self-service.index') ->withErrors([ 'identity_number' => 'Sesi pelengkapan data tidak valid atau sudah berakhir. Silakan verifikasi ulang.', ]); } $student->load(['parents', 'academicYear', 'department', 'classRoom']); return view('public.student-self-service.edit', [ 'student' => $student, 'genders' => [ 'L' => 'Laki-laki', 'P' => 'Perempuan', ], 'religions' => [ 'Islam' => 'Islam', 'Kristen' => 'Kristen', 'Katolik' => 'Katolik', 'Hindu' => 'Hindu', 'Buddha' => 'Buddha', 'Khonghucu' => 'Khonghucu', 'Lainnya' => 'Lainnya', ], 'provinces' => Province::orderBy('name')->pluck('name', 'id'), 'parentRelationLabels' => [ 'father' => 'Ayah', 'mother' => 'Ibu', 'guardian' => 'Wali', ], 'educationOptions' => [ 'Tidak Sekolah' => 'Tidak Sekolah', 'Tidak Tamat SD' => 'Tidak Tamat SD', 'SD' => 'SD', 'SMP' => 'SMP', 'SLTA' => 'SLTA', 'D1' => 'D1', 'D2' => 'D2', 'D3' => 'D3', 'D4' => 'D4', 'S1' => 'S1', 'S2' => 'S2', 'S3' => 'S3', 'Lainnya' => 'Lainnya', ], ]); } public function update(Request $request): RedirectResponse { $student = $this->resolveVerifiedStudent($request); if (! $student) { return redirect()->route('public.student-self-service.index') ->withErrors([ 'identity_number' => 'Sesi pelengkapan data tidak valid atau sudah berakhir. Silakan verifikasi ulang.', ]); } $data = $request->validate([ 'full_name' => ['required', 'string', 'max:150'], 'gender' => ['required', Rule::in(['L', 'P'])], 'place_of_birth' => ['nullable', 'string', 'max:100'], 'date_of_birth' => ['nullable', 'date'], 'religion' => ['nullable', 'string', 'max:50'], 'nationality' => ['nullable', 'string', 'max:50'], 'family_card_number' => ['nullable', 'string', 'max:30'], 'child_order' => ['nullable', 'integer', 'min:1', 'max:50'], 'number_of_siblings' => ['nullable', 'integer', 'min:0', 'max:50'], 'phone_number' => ['nullable', 'string', 'max:30'], 'email' => ['nullable', 'email', 'max:100'], 'address' => ['nullable', 'string'], 'province_id' => ['nullable', 'exists:provinces,id'], 'regency_id' => ['nullable', 'exists:regencies,id'], 'district_id' => ['nullable', 'exists:districts,id'], 'village_id' => ['nullable', 'exists:villages,id'], 'postal_code' => ['nullable', 'string', 'max:10'], 'parents.father.name' => ['nullable', 'string', 'max:150'], 'parents.father.nik' => ['nullable', 'string', 'max:30'], 'parents.father.birth_year' => ['nullable', 'integer', 'min:1900', 'max:' . now()->year], 'parents.father.education' => ['nullable', Rule::in(['Tidak Sekolah', 'Tidak Tamat SD', 'SD', 'SMP', 'SLTA', 'D1', 'D2', 'D3', 'D4', 'S1', 'S2', 'S3', 'Lainnya'])], 'parents.father.occupation' => ['nullable', 'string', 'max:100'], 'parents.father.phone_number' => ['nullable', 'string', 'max:30'], 'parents.father.address' => ['nullable', 'string'], 'parents.father.is_alive' => ['nullable', 'boolean'], 'parents.mother.name' => ['nullable', 'string', 'max:150'], 'parents.mother.nik' => ['nullable', 'string', 'max:30'], 'parents.mother.birth_year' => ['nullable', 'integer', 'min:1900', 'max:' . now()->year], 'parents.mother.education' => ['nullable', Rule::in(['Tidak Sekolah', 'Tidak Tamat SD', 'SD', 'SMP', 'SLTA', 'D1', 'D2', 'D3', 'D4', 'S1', 'S2', 'S3', 'Lainnya'])], 'parents.mother.occupation' => ['nullable', 'string', 'max:100'], 'parents.mother.phone_number' => ['nullable', 'string', 'max:30'], 'parents.mother.address' => ['nullable', 'string'], 'parents.mother.is_alive' => ['nullable', 'boolean'], 'parents.guardian.name' => ['nullable', 'string', 'max:150'], 'parents.guardian.nik' => ['nullable', 'string', 'max:30'], 'parents.guardian.birth_year' => ['nullable', 'integer', 'min:1900', 'max:' . now()->year], 'parents.guardian.education' => ['nullable', Rule::in(['Tidak Sekolah', 'Tidak Tamat SD', 'SD', 'SMP', 'SLTA', 'D1', 'D2', 'D3', 'D4', 'S1', 'S2', 'S3', 'Lainnya'])], 'parents.guardian.occupation' => ['nullable', 'string', 'max:100'], 'parents.guardian.phone_number' => ['nullable', 'string', 'max:30'], 'parents.guardian.address' => ['nullable', 'string'], 'parents.guardian.is_alive' => ['nullable', 'boolean'], 'submit_action' => ['required', Rule::in(['draft', 'submit'])], ]); DB::transaction(function () use ($student, $data) { $student->fill([ 'full_name' => $data['full_name'], 'gender' => $data['gender'], 'place_of_birth' => $data['place_of_birth'] ?? null, 'date_of_birth' => $data['date_of_birth'] ?? null, 'religion' => $data['religion'] ?? null, 'nationality' => $data['nationality'] ?? 'Indonesia', 'family_card_number' => $data['family_card_number'] ?? null, 'child_order' => $data['child_order'] ?? null, 'number_of_siblings' => $data['number_of_siblings'] ?? null, 'phone_number' => $data['phone_number'] ?? null, 'email' => $data['email'] ?? null, 'address' => $data['address'] ?? null, 'province_id' => $data['province_id'] ?? null, 'regency_id' => $data['regency_id'] ?? null, 'district_id' => $data['district_id'] ?? null, 'village_id' => $data['village_id'] ?? null, 'postal_code' => $data['postal_code'] ?? null, 'province' => ! empty($data['province_id']) ? DB::table('provinces')->where('id', $data['province_id'])->value('name') : null, 'city' => ! empty($data['regency_id']) ? DB::table('regencies')->where('id', $data['regency_id'])->value('name') : null, 'district' => ! empty($data['district_id']) ? DB::table('districts')->where('id', $data['district_id'])->value('name') : null, 'village' => ! empty($data['village_id']) ? DB::table('villages')->where('id', $data['village_id'])->value('name') : null, 'self_service_status' => $data['submit_action'] === 'submit' ? 'submitted' : 'draft', 'self_service_last_access_at' => now(), 'self_service_submitted_at' => $data['submit_action'] === 'submit' ? now() : null, ])->save(); foreach (['father', 'mother', 'guardian'] as $relationType) { $parentData = $data['parents'][$relationType] ?? []; $hasMeaningfulValue = collect($parentData) ->except('is_alive') ->filter(fn ($value) => filled($value)) ->isNotEmpty(); $existingParent = $student->parents()->where('relation_type', $relationType)->first(); if (! $hasMeaningfulValue) { if ($existingParent) { $existingParent->delete(); } continue; } $payload = [ 'name' => $parentData['name'] ?? '-', 'nik' => $parentData['nik'] ?? null, 'birth_year' => $parentData['birth_year'] ?? null, 'education' => $parentData['education'] ?? null, 'occupation' => $parentData['occupation'] ?? null, 'phone_number' => $parentData['phone_number'] ?? null, 'address' => $parentData['address'] ?? null, 'is_alive' => (bool) ($parentData['is_alive'] ?? false), ]; if ($existingParent) { $existingParent->update($payload); } else { $student->parents()->create([ 'relation_type' => $relationType, ...$payload, ]); } } }); return redirect()->route('public.student-self-service.edit') ->with('success', $data['submit_action'] === 'submit' ? 'Data berhasil dikirim. Silakan tunggu pemeriksaan admin sekolah.' : 'Draft berhasil disimpan. Anda bisa melanjutkan lagi nanti selama sesi masih aktif.'); } protected function resolveVerifiedStudent(Request $request): ?Student { $studentId = $request->session()->get('student_self_service.student_id'); if (! $studentId) { return null; } return Student::find($studentId); } }