# `ParentController` plan ## Scope - **Controller**: `app/Http/Controllers/Api/Parents/ParentController.php` - **Purpose**: parent portal endpoints for attendance, invoices, enrollments, registration, emergency contacts, profile, and event participation. - **Primary dependencies**: - `App\Services\Parents\ParentAttendanceService` - `App\Services\Parents\ParentInvoiceService` - `App\Services\Parents\ParentEnrollmentService` - `App\Services\Parents\ParentRegistrationService` - `App\Services\Parents\ParentEmergencyContactService` - `App\Services\Parents\ParentProfileService` - `App\Services\Parents\ParentEventParticipationService` ## Routes All routes are under `v1` + `parents` group: - Base prefix: `GET/POST/PATCH/DELETE /api/v1/parents/...` - Group middleware: `parent.access` Endpoints: - `GET /api/v1/parents/attendance` → `attendance` - `GET /api/v1/parents/invoices` → `invoices` - `GET /api/v1/parents/enrollments` → `enrollments` - `POST /api/v1/parents/enrollments` → `updateEnrollments` - `GET /api/v1/parents/registration` → `registration` - `POST /api/v1/parents/registration` → `storeRegistration` - `PATCH /api/v1/parents/students/{studentId}` → `updateStudent` - `DELETE /api/v1/parents/students/{studentId}` → `deleteStudent` - `GET /api/v1/parents/emergency-contacts` → `emergencyContacts` - `POST /api/v1/parents/emergency-contacts` → `storeEmergencyContact` - `PATCH /api/v1/parents/emergency-contacts/{contactId}` → `updateEmergencyContact` - `GET /api/v1/parents/profile` → `profile` - `PATCH /api/v1/parents/profile` → `updateProfile` - `GET /api/v1/parents/events` → `events` - `POST /api/v1/parents/events/participation` → `updateParticipation` ## Authentication & authorization - Uses `parent.access` middleware which resolves “secondary”/“authorized” users to a primary parent id and stores it in the request attribute `primary_parent_id`. - Controller helper `parentIdOrUnauthorized()`: - Prefer `request()->attributes->get('primary_parent_id')` - Fallback to `auth()->id()` - On failure returns JSON `{ ok:false, message:"Unauthorized." }` with `401` ## Requests/validation Validated via `FormRequest` classes per endpoint: - `attendance`: `ParentAttendanceRequest` (optional `school_year`) - `invoices`: `ParentInvoiceRequest` (optional `school_year`) - `enrollments`: `ParentEnrollmentRequest` (optional `school_year`) - `updateEnrollments`: `ParentEnrollmentActionRequest` (`enroll[]`, `withdraw[]`) - `storeRegistration`: `ParentRegistrationRequest` (`students[]`, `emergency_contacts[]`) - `updateStudent`: `ParentStudentUpdateRequest` - `store/update emergency contact`: `ParentEmergencyContactRequest` - `updateProfile`: `ParentProfileUpdateRequest` - `updateParticipation`: `ParentEventParticipationRequest` (`participation[]`) ## Response shapes (legacy `{ ok: ... }`) This controller largely returns a legacy parent-portal shape rather than the Base API envelope. - `attendance`: - `{ ok:true, attendance:[...], schoolYears:[...], selectedYear }` - `attendance` uses `ParentAttendanceResource::collection(...)` - `invoices`: - `{ ok:true, invoices:[...] }` (`InvoiceResource::collection(...)`) - `enrollments`: - `{ ok:true, students:[...], schoolYears:[...], selectedYear, withdrawalDeadline, lastDayOfRegistration, schoolStartDate }` - `updateEnrollments`: - `{ ok:true, enrolled:[...], withdrawn:[...] }` - `registration`: - `{ ok:true, parent, existingKids:[...], emergencies:[...], maxChilds, maxEmergency, lastDayOfRegistration, registrationAgeDeadline }` - `storeRegistration`: - `{ ok:true, created_student_ids:[...], skipped:[...] }` with status `201` if created_count>0 else `200` - `updateStudent` / `deleteStudent` / `updateParticipation`: - `{ ok:true }` - `emergencyContacts`: - `{ ok:true, contacts:[...] }` - `store/update emergency contact`: - `{ ok:true, contact:{...} }` (store returns `201`) - `profile` / `updateProfile`: - `{ ok:true, profile:{...} }` - `events`: - `{ ok:true, activeEvents, charges, yourStudents }` ## Error handling expectations - Unauthorized: `{ ok:false, message:"Unauthorized." }` with `401` (from `parentIdOrUnauthorized()`). - Service-layer exceptions are generally allowed to bubble unless handled in the service; controller does minimal try/catch. ## Test plan (feature) - Auth/parent.access: - Without auth → 401 `{ ok:false }`. - With secondary/authorized user → resolved to primary parent id and returns correct data. - Each endpoint: - Valid request returns `ok:true` and expected keys. - Validation errors are returned by FormRequest (422). - Registration: - Returns 201 when at least one student is created, 200 when all entries skipped.