From c582bfc24268d8cc4c7480b6a32bd781195b8df8 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 25 Mar 2026 17:59:40 -0400 Subject: [PATCH] update emergency contact student controller --- .../EmergencyContactController.php | 22 +++- .../Settings/ConfigurationAdminController.php | 13 +++ .../Api/Students/StudentController.php | 1 - .../EmergencyContactParentRequest.php | 15 +++ .../EmergencyContactUpdateRequest.php | 1 + .../EmergencyContactCrudService.php | 25 ++++- .../Settings/ConfigurationService.php | 8 ++ .../Students/StudentDirectoryService.php | 1 - resources/docs/controllers.md | 2 +- routes/api.php | 2 + .../EmergencyContactControllerTest.php | 105 +++++++++++++++++- 11 files changed, 179 insertions(+), 16 deletions(-) create mode 100644 app/Http/Requests/EmergencyContacts/EmergencyContactParentRequest.php diff --git a/app/Http/Controllers/Api/Administrator/EmergencyContactController.php b/app/Http/Controllers/Api/Administrator/EmergencyContactController.php index 94d63154..8053baca 100644 --- a/app/Http/Controllers/Api/Administrator/EmergencyContactController.php +++ b/app/Http/Controllers/Api/Administrator/EmergencyContactController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api\Administrator; use App\Http\Controllers\Controller; +use App\Http\Requests\EmergencyContacts\EmergencyContactParentRequest; use App\Http\Requests\EmergencyContacts\EmergencyContactUpdateRequest; use App\Http\Resources\EmergencyContacts\EmergencyContactGroupResource; use App\Http\Resources\EmergencyContacts\EmergencyContactResource; @@ -28,9 +29,10 @@ class EmergencyContactController extends Controller ]); } - public function update(EmergencyContactUpdateRequest $request, int $contactId): JsonResponse + public function show(EmergencyContactParentRequest $request, int $contactId): JsonResponse { - $contact = $this->crudService->update($contactId, $request->validated()); + $parentId = (int) $request->validated()['parent_id']; + $contact = $this->crudService->findForParent($contactId, $parentId); return response()->json([ 'ok' => true, @@ -38,9 +40,21 @@ class EmergencyContactController extends Controller ]); } - public function destroy(int $contactId): JsonResponse + public function update(EmergencyContactUpdateRequest $request, int $contactId): JsonResponse { - $this->crudService->delete($contactId); + $payload = $request->validated(); + $contact = $this->crudService->update($contactId, (int) $payload['parent_id'], $payload); + + return response()->json([ + 'ok' => true, + 'contact' => new EmergencyContactResource($contact), + ]); + } + + public function destroy(EmergencyContactParentRequest $request, int $contactId): JsonResponse + { + $parentId = (int) $request->validated()['parent_id']; + $this->crudService->delete($contactId, $parentId); return response()->json(['ok' => true]); } diff --git a/app/Http/Controllers/Api/Settings/ConfigurationAdminController.php b/app/Http/Controllers/Api/Settings/ConfigurationAdminController.php index a0b3259d..522650a8 100644 --- a/app/Http/Controllers/Api/Settings/ConfigurationAdminController.php +++ b/app/Http/Controllers/Api/Settings/ConfigurationAdminController.php @@ -58,4 +58,17 @@ class ConfigurationAdminController extends BaseApiController return response()->json(['ok' => true]); } + + public function getByKey(string $configKey): JsonResponse + { + $config = $this->service->getByKey($configKey); + if (!$config) { + return response()->json(['ok' => false, 'message' => 'Configuration not found.'], 404); + } + + return response()->json([ + 'ok' => true, + 'config' => new ConfigurationResource($config->toArray()), + ]); + } } diff --git a/app/Http/Controllers/Api/Students/StudentController.php b/app/Http/Controllers/Api/Students/StudentController.php index 7cc0b02e..5f8ef45e 100644 --- a/app/Http/Controllers/Api/Students/StudentController.php +++ b/app/Http/Controllers/Api/Students/StudentController.php @@ -123,7 +123,6 @@ class StudentController extends BaseApiController 'parent_id', 'registration_date', 'tuition_paid', - 'rfid_tag', 'semester', 'year_of_registration', 'school_year', diff --git a/app/Http/Requests/EmergencyContacts/EmergencyContactParentRequest.php b/app/Http/Requests/EmergencyContacts/EmergencyContactParentRequest.php new file mode 100644 index 00000000..5e897d54 --- /dev/null +++ b/app/Http/Requests/EmergencyContacts/EmergencyContactParentRequest.php @@ -0,0 +1,15 @@ + ['required', 'integer', 'min:1'], + ]; + } +} diff --git a/app/Http/Requests/EmergencyContacts/EmergencyContactUpdateRequest.php b/app/Http/Requests/EmergencyContacts/EmergencyContactUpdateRequest.php index 857b2084..cdd4d50f 100644 --- a/app/Http/Requests/EmergencyContacts/EmergencyContactUpdateRequest.php +++ b/app/Http/Requests/EmergencyContacts/EmergencyContactUpdateRequest.php @@ -9,6 +9,7 @@ class EmergencyContactUpdateRequest extends ApiFormRequest public function rules(): array { return [ + 'parent_id' => ['required', 'integer', 'min:1'], 'name' => ['required', 'string', 'max:150'], 'cellphone' => ['required', 'string', 'max:30'], 'email' => ['nullable', 'email', 'max:150'], diff --git a/app/Services/EmergencyContacts/EmergencyContactCrudService.php b/app/Services/EmergencyContacts/EmergencyContactCrudService.php index 8b943acd..044749e6 100644 --- a/app/Services/EmergencyContacts/EmergencyContactCrudService.php +++ b/app/Services/EmergencyContacts/EmergencyContactCrudService.php @@ -12,9 +12,22 @@ class EmergencyContactCrudService ) { } - public function update(int $contactId, array $payload): array + public function findForParent(int $contactId, int $parentId): array { - $contact = EmergencyContact::query()->findOrFail($contactId); + $contact = EmergencyContact::query() + ->where('id', $contactId) + ->where('parent_id', $parentId) + ->firstOrFail(); + + return $contact->toArray(); + } + + public function update(int $contactId, int $parentId, array $payload): array + { + $contact = EmergencyContact::query() + ->where('id', $contactId) + ->where('parent_id', $parentId) + ->firstOrFail(); $contact->update([ 'emergency_contact_name' => $payload['name'], @@ -26,9 +39,13 @@ class EmergencyContactCrudService return $contact->toArray(); } - public function delete(int $contactId): void + public function delete(int $contactId, int $parentId): void { - $contact = EmergencyContact::query()->findOrFail($contactId); + $contact = EmergencyContact::query() + ->where('id', $contactId) + ->where('parent_id', $parentId) + ->firstOrFail(); + $contact->delete(); } } diff --git a/app/Services/Settings/ConfigurationService.php b/app/Services/Settings/ConfigurationService.php index 3a3f5630..fe1030db 100644 --- a/app/Services/Settings/ConfigurationService.php +++ b/app/Services/Settings/ConfigurationService.php @@ -46,4 +46,12 @@ class ConfigurationService return (bool) $config->delete(); } + + public function getByKey(string $key): ?Configuration + { + return Configuration::query() + ->where('config_key', $key) + ->orderByDesc('id') + ->first(); + } } diff --git a/app/Services/Students/StudentDirectoryService.php b/app/Services/Students/StudentDirectoryService.php index b57e877e..1367ba70 100644 --- a/app/Services/Students/StudentDirectoryService.php +++ b/app/Services/Students/StudentDirectoryService.php @@ -183,7 +183,6 @@ class StudentDirectoryService 'parent_id', 'registration_date', 'tuition_paid', - 'rfid_tag', 'semester', 'year_of_registration', 'school_year', diff --git a/resources/docs/controllers.md b/resources/docs/controllers.md index 07906c6e..ec2cd3f0 100755 --- a/resources/docs/controllers.md +++ b/resources/docs/controllers.md @@ -168,7 +168,7 @@ This document lists every API controller alphabetically with any inline route co - `POST /api/v1/communications/send-email` (`sendEmail`) – Body requires `student_id`, `family_id`, `template_key`, `subject`, `body`, and `recipients` (JSON array or string). Accepts optional `cc`/`bcc`, sends the HTML email through `Mail::send`, and logs the result in `communication_logs`. ## ConfigurationController -**File:** `app/Http/Controllers/Api/ConfigurationController.php` +**File:** `app/Http/Controllers/Api/Settings/ConfigurationAdminController.php` **Purpose:** Manage key/value pairs stored in the `configuration` table. - `GET /api/v1/configuration` (`index`) – Paginated list ordered by ID with optional fuzzy `key` search. diff --git a/routes/api.php b/routes/api.php index 946123d0..60311d3f 100755 --- a/routes/api.php +++ b/routes/api.php @@ -207,6 +207,7 @@ Route::prefix('v1')->group(function () { Route::get('emergency-contacts', [AdministratorEmergencyContactController::class, 'index']); Route::get('emergency-contacts/data', [AdministratorEmergencyContactController::class, 'index']); + Route::get('emergency-contacts/{contactId}', [AdministratorEmergencyContactController::class, 'show']); Route::patch('emergency-contacts/{contactId}', [AdministratorEmergencyContactController::class, 'update']); Route::delete('emergency-contacts/{contactId}', [AdministratorEmergencyContactController::class, 'destroy']); }); @@ -706,6 +707,7 @@ Route::prefix('v1')->group(function () { Route::prefix('configuration')->group(function () { Route::get('/', [ConfigurationAdminController::class, 'index']); + Route::get('key/{config_key}', [ConfigurationAdminController::class, 'getByKey']); Route::post('/', [ConfigurationAdminController::class, 'store']); Route::patch('{id}', [ConfigurationAdminController::class, 'update']); Route::delete('{id}', [ConfigurationAdminController::class, 'destroy']); diff --git a/tests/Feature/Api/V1/Administrator/EmergencyContactControllerTest.php b/tests/Feature/Api/V1/Administrator/EmergencyContactControllerTest.php index 578a74de..6da928a2 100644 --- a/tests/Feature/Api/V1/Administrator/EmergencyContactControllerTest.php +++ b/tests/Feature/Api/V1/Administrator/EmergencyContactControllerTest.php @@ -54,7 +54,7 @@ class EmergencyContactControllerTest extends TestCase 'school_year' => '2025-2026', ]); - Sanctum::actingAs($admin); + Sanctum::actingAs($admin, [], 'api'); $response = $this->getJson('/api/v1/administrator/emergency-contacts'); $response->assertOk(); @@ -79,12 +79,15 @@ class EmergencyContactControllerTest extends TestCase 'school_year' => '2025-2026', ]); - Sanctum::actingAs($admin); - $response = $this->patchJson('/api/v1/administrator/emergency-contacts/' . $contact->id, [ + Sanctum::actingAs($admin, [], 'api'); + $response = $this->patch('/api/v1/administrator/emergency-contacts/' . $contact->id, [ + 'parent_id' => $parent->id, 'name' => 'New Name', 'cellphone' => '5555555555', 'email' => 'new@example.com', 'relation' => 'Neighbor', + ], [ + 'Accept' => 'application/json', ]); $response->assertOk(); @@ -112,8 +115,12 @@ class EmergencyContactControllerTest extends TestCase 'school_year' => '2025-2026', ]); - Sanctum::actingAs($admin); - $response = $this->deleteJson('/api/v1/administrator/emergency-contacts/' . $contact->id); + Sanctum::actingAs($admin, [], 'api'); + $response = $this->delete('/api/v1/administrator/emergency-contacts/' . $contact->id, [ + 'parent_id' => $parent->id, + ], [ + 'Accept' => 'application/json', + ]); $response->assertOk(); $response->assertJson(['ok' => true]); @@ -121,4 +128,92 @@ class EmergencyContactControllerTest extends TestCase 'id' => $contact->id, ]); } + + public function test_show_returns_single_contact_for_parent(): void + { + $admin = User::factory()->create(); + $parent = User::factory()->create(); + + $contact = EmergencyContact::query()->create([ + 'parent_id' => $parent->id, + 'emergency_contact_name' => 'Single Contact', + 'cellphone' => '7777777777', + 'email' => 'single@example.com', + 'relation' => 'Friend', + 'semester' => 'Fall', + 'school_year' => '2025-2026', + ]); + + Sanctum::actingAs($admin, [], 'api'); + $response = $this->get('/api/v1/administrator/emergency-contacts/' . $contact->id . '?parent_id=' . $parent->id, [ + 'Accept' => 'application/json', + ]); + + $response->assertOk(); + $response->assertJsonPath('contact.id', $contact->id); + $response->assertJsonPath('contact.parent_id', $parent->id); + } + + public function test_update_requires_matching_parent_id(): void + { + $admin = User::factory()->create(); + $parent = User::factory()->create(); + $otherParent = User::factory()->create(); + + $contact = EmergencyContact::query()->create([ + 'parent_id' => $parent->id, + 'emergency_contact_name' => 'Old Name', + 'cellphone' => '8888888888', + 'email' => 'old@example.com', + 'relation' => 'Friend', + 'semester' => 'Fall', + 'school_year' => '2025-2026', + ]); + + Sanctum::actingAs($admin, [], 'api'); + $response = $this->patch('/api/v1/administrator/emergency-contacts/' . $contact->id, [ + 'parent_id' => $otherParent->id, + 'name' => 'New Name', + 'cellphone' => '9999999999', + 'email' => 'new@example.com', + 'relation' => 'Neighbor', + ], [ + 'Accept' => 'application/json', + ]); + + $response->assertNotFound(); + $this->assertDatabaseHas('emergency_contacts', [ + 'id' => $contact->id, + 'emergency_contact_name' => 'Old Name', + ]); + } + + public function test_destroy_requires_matching_parent_id(): void + { + $admin = User::factory()->create(); + $parent = User::factory()->create(); + $otherParent = User::factory()->create(); + + $contact = EmergencyContact::query()->create([ + 'parent_id' => $parent->id, + 'emergency_contact_name' => 'Delete Name', + 'cellphone' => '1010101010', + 'email' => 'delete@example.com', + 'relation' => 'Friend', + 'semester' => 'Fall', + 'school_year' => '2025-2026', + ]); + + Sanctum::actingAs($admin, [], 'api'); + $response = $this->delete('/api/v1/administrator/emergency-contacts/' . $contact->id, [ + 'parent_id' => $otherParent->id, + ], [ + 'Accept' => 'application/json', + ]); + + $response->assertNotFound(); + $this->assertDatabaseHas('emergency_contacts', [ + 'id' => $contact->id, + ]); + } }