get(); return view('admin.students.index', compact('students')); } public function create() { $parents = ParentProfile::with('user')->get(); $studentTypes = StudentType::all(); return view('admin.students.create', compact('parents', 'studentTypes')); } public function edit(StudentProfile $student) { $student->load(['user', 'billingAccount', 'billingAccount.primaryParent']); $parents = ParentProfile::with('user')->get(); $studentTypes = StudentType::all(); return view('admin.students.edit', compact('student', 'parents', 'studentTypes')); } public function update(Request $request, StudentProfile $student) { $validated = $request->validate([ // Student 'student_first_name' => 'required|string|max:255', 'student_last_name' => 'required|string|max:255', 'student_phone' => 'nullable|string|max:255', // If you allow changing student email: // 'student_email' => ['required','email', Rule::unique('users','email')->ignore($student->user_id)], 'date_of_birth' => 'nullable|date', 'student_type_id' => 'required|exists:student_types,id', // Parent selection 'parent_option' => 'required|in:none,existing,new', 'existing_parent_id' => 'required_if:parent_option,existing|nullable|exists:parent_profiles,id', // New parent (only used if parent_option=new) 'parent_first_name' => 'required_if:parent_option,new|nullable|string|max:255', 'parent_last_name' => 'required_if:parent_option,new|nullable|string|max:255', 'parent_email' => 'required_if:parent_option,new|nullable|email|unique:users,email', 'parent_phone' => 'nullable|string|max:255', // Billing (match your edit form; keep minimal initially) 'billing_account_id' => 'sometimes|nullable|exists:billing_accounts,id', 'billing_name' => 'required|string|max:255', 'invoice_email' => 'required|email', 'billing_phone' => 'nullable|string|max:255', 'contact_name' => 'nullable|string|max:255', 'address_line1' => 'required|string|max:255', 'address_line2' => 'nullable|string|max:255', 'town' => 'required|string|max:255', 'postcode' => 'required|string|max:50', 'country' => 'required|string|max:255', ]); DB::beginTransaction(); try { // Load student user $student->load('user'); // 1) Update STUDENT USER $student->user->update([ 'first_name' => $validated['student_first_name'], 'last_name' => $validated['student_last_name'], 'phone' => $validated['student_phone'] ?? null, // 'email' => $validated['student_email'], ]); // 2) Update STUDENT PROFILE $student->update([ 'date_of_birth' => $validated['date_of_birth'] ?? null, 'student_type_id' => $validated['student_type_id'], // billing_account_id handled below ]); // 3) Resolve parent user based on parent_option $parentProfile = null; if ($validated['parent_option'] === 'existing') { $parentProfile = ParentProfile::with('user')->find($validated['existing_parent_id']); } if ($validated['parent_option'] === 'new') { $parentUser = User::create([ 'first_name' => $validated['parent_first_name'], 'last_name' => $validated['parent_last_name'], 'email' => $validated['parent_email'], 'phone' => $validated['parent_phone'], 'password' => Hash::make(str()->random(16)), ]); $parentUser->assignRole('parent'); $parentProfile = ParentProfile::firstOrCreate( ['user_id' => $parentUser->id], ['phone' => $parentUser->phone] ); // ensure $parentProfile has user loaded for later $parentProfile->load('user'); } if ($parentProfile && $parentProfile->user) { // ensure role even for the "existing" case $parentProfile->user->assignRole('parent'); } // 4) Billing account: update existing or create new (same logic as store) if (!empty($validated['billing_account_id'])) { $billingAccount = BillingAccount::findOrFail($validated['billing_account_id']); $billingAccount->update([ 'name' => $validated['billing_name'], 'contact_name' => $validated['contact_name'], 'invoice_email' => $validated['invoice_email'], 'phone' => $validated['billing_phone'], 'address_line1' => $validated['address_line1'], 'address_line2' => $validated['address_line2'], 'town' => $validated['town'], 'postcode' => $validated['postcode'], 'country' => $validated['country'], 'primary_parent_user_id' => $parentProfile?->user_id, ]); } else { $billingAccount = BillingAccount::create([ 'name' => $validated['billing_name'], 'contact_name' => $validated['contact_name'], 'invoice_email' => $validated['invoice_email'], 'phone' => $validated['billing_phone'], 'address_line1' => $validated['address_line1'], 'address_line2' => $validated['address_line2'], 'town' => $validated['town'], 'postcode' => $validated['postcode'], 'country' => $validated['country'], 'primary_parent_user_id' => $parentProfile?->user_id, ]); } if ($validated['parent_option'] === 'none') { $billingAccount->update(['primary_parent_user_id' => null]); } // Ensure student profile points at the billing account we just resolved $student->billing_account_id = $billingAccount->id; $student->save(); // 5) Parent pivot handling if ($validated['parent_option'] === 'none') { $student->parents()->detach(); } else { if ($parentProfile) { // ONE parent only: $student->parents()->sync([ $parentProfile->id => [ 'relationship' => null, 'notes' => null, ], ]); } } DB::commit(); return redirect() ->route('admin.students.edit', $student->id) ->with('success', 'Student updated successfully.'); } catch (\Exception $e) { DB::rollBack(); throw $e; } } public function store(Request $request) { // ----------------------------- // Validation // ----------------------------- $validated = $request->validate([ // Student 'student_first_name' => 'required|string|max:255', 'student_last_name' => 'required|string|max:255', 'student_email' => 'required|email|unique:users,email', 'student_phone' => 'nullable|string|max:255', 'date_of_birth' => 'nullable|date', 'student_type_id' => 'required|exists:student_types,id', // Parent selection 'parent_option' => 'required|in:none,existing,new', 'existing_parent_id' => 'required_if:parent_option,existing|nullable|exists:parent_profiles,id', // New parent 'parent_first_name' => 'nullable|string|max:255', 'parent_last_name' => 'nullable|string|max:255', 'parent_email' => 'nullable|email|unique:users,email', 'parent_phone' => 'nullable|string|max:255', // Billing 'billing_account_id' => 'sometimes|nullable|exists:billing_accounts,id', 'billing_name' => 'required|string|max:255', 'invoice_email' => 'required|email', 'billing_phone' => 'nullable|string|max:255', 'contact_name' => 'nullable|string|max:255', 'address_line1' => 'required|string|max:255', 'address_line2' => 'nullable|string|max:255', 'town' => 'required|string|max:255', 'postcode' => 'required|string|max:50', 'country' => 'required|string|max:255', ]); DB::beginTransaction(); try { // =============================================================== // 1) Create STUDENT USER // =============================================================== $studentUser = User::create([ 'first_name' => $validated['student_first_name'], 'last_name' => $validated['student_last_name'], 'email' => $validated['student_email'], 'phone' => $validated['student_phone'], 'password' => Hash::make(str()->random(16)), // Temporary password ]); $studentUser->assignRole('student'); // =============================================================== // 2) Handle PARENT selection // =============================================================== $parentProfile = null; if ($validated['parent_option'] === 'existing') { $parentProfile = ParentProfile::find($validated['existing_parent_id']); } if ($validated['parent_option'] === 'new') { create([ 'first_name' => $validated['parent_first_name'], 'last_name' => $validated['parent_last_name'], 'email' => $validated['parent_email'], 'phone' => $validated['parent_phone'], 'password' => Hash::make(str()->random(16)), ]); $parentUser->assignRole('parent'); $parentProfile = ParentProfile::firstOrCreate( ['user_id' => $parentUser->id], ['phone' => $parentUser->phone] ); } if ($parentProfile) { $parentProfile->user?->assignRole('parent'); } // Note: If parent_option == 'none', $parentUser remains null. // =============================================================== // 3) Create BILLING ACCOUNT // =============================================================== if (!empty($validated['billing_account_id'])) { // Use existing billing account $billingAccount = BillingAccount::find($validated['billing_account_id']); } else { $billingAccount = BillingAccount::create([ 'name' => $validated['billing_name'], 'contact_name' => $validated['contact_name'], 'invoice_email' => $validated['invoice_email'], 'phone' => $validated['billing_phone'], 'address_line1' => $validated['address_line1'], 'address_line2' => $validated['address_line2'], 'town' => $validated['town'], 'postcode' => $validated['postcode'], 'country' => $validated['country'], 'primary_parent_user_id' => $parentProfile?->user_id, ]); } // =============================================================== // 4) Create STUDENT PROFILE // =============================================================== $studentProfile = StudentProfile::create([ 'user_id' => $studentUser->id, 'date_of_birth' => $validated['date_of_birth'] ?? null, 'billing_account_id' => $billingAccount->id, 'student_type_id' => $validated['student_type_id'], ]); // =============================================================== // 4B) Create/link PARENT PROFILE + pivot relationship // =============================================================== if ($parentUser) { // Ensure parent has role (covers "existing parent" case) $parentUser->assignRole('parent'); // Ensure parent profile exists $parentProfile = ParentProfile::firstOrCreate( ['user_id' => $parentUser->id], ['phone' => $parentUser->phone] // optional default ); // Link parent profile to student profile via pivot $studentProfile->parents()->syncWithoutDetaching([ $parentProfile->id => [ 'relationship' => null, 'notes' => null, ], ]); } // =============================================================== // 5) Send password setup email to student user // =============================================================== Password::sendResetLink(['email' => $studentUser->email]); DB::commit(); return redirect() ->route('admin.students.index') ->with('success', 'Student created successfully.'); } catch (\Exception $e) { DB::rollBack(); throw $e; // (You can customise error messages later) } } }