835 lines
29 KiB
PHP
835 lines
29 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\Students;
|
|
|
|
use App\Http\Controllers\Api\Core\BaseApiController;
|
|
use App\Http\Requests\Students\StudentAssignClassRequest;
|
|
use App\Http\Requests\Students\StudentAssignmentListRequest;
|
|
use App\Http\Requests\Students\StudentAutoDistributeRequest;
|
|
use App\Http\Requests\Students\StudentPromotionTotalsRequest;
|
|
use App\Http\Requests\Students\StudentRemoveClassRequest;
|
|
use App\Http\Requests\Students\StudentSetActiveRequest;
|
|
use App\Http\Requests\Students\StudentUpdateRequest;
|
|
use App\Http\Resources\Students\StudentAssignmentResource;
|
|
use App\Http\Resources\Students\StudentClassSectionResource;
|
|
use App\Http\Resources\Students\StudentRemovedResource;
|
|
use App\Http\Resources\Students\StudentScoreCardRowResource;
|
|
use App\Models\AttendanceRecord;
|
|
use App\Models\EmergencyContact;
|
|
use App\Models\Incident;
|
|
use App\Models\SemesterScore;
|
|
use App\Models\Student;
|
|
use App\Models\StudentClass;
|
|
use App\Models\User;
|
|
use App\Services\Students\StudentAssignmentService;
|
|
use App\Services\Students\StudentAutoDistributionService;
|
|
use App\Services\Students\StudentDirectoryService;
|
|
use App\Services\Students\StudentProfileService;
|
|
use App\Services\Students\StudentScoreCardService;
|
|
use App\Services\Students\StudentStatusService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Facades\Validator;
|
|
|
|
class StudentController extends BaseApiController
|
|
{
|
|
public function __construct(
|
|
private StudentAssignmentService $assignmentService,
|
|
private StudentDirectoryService $directoryService,
|
|
private StudentStatusService $statusService,
|
|
private StudentAutoDistributionService $autoDistributionService,
|
|
private StudentProfileService $profileService,
|
|
private StudentScoreCardService $scoreCardService
|
|
) {
|
|
parent::__construct();
|
|
}
|
|
|
|
public function assignments(StudentAssignmentListRequest $request): JsonResponse
|
|
{
|
|
$data = $this->directoryService->assignmentData($request->validated()['school_year'] ?? null);
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'students' => StudentAssignmentResource::collection($data['students']),
|
|
'classes' => StudentClassSectionResource::collection($data['classes']),
|
|
'schoolYears' => $data['schoolYears'],
|
|
'selectedYear' => $data['selectedYear'],
|
|
'currentYear' => $data['currentYear'],
|
|
'isCurrentYear' => $data['isCurrentYear'],
|
|
]);
|
|
}
|
|
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
|
|
$validator = Validator::make($data, [
|
|
'school_year' => ['nullable', 'string', 'max:50'],
|
|
'parent_id' => ['nullable', 'integer', 'min:1'],
|
|
'parent_ids' => ['nullable', 'array'],
|
|
'parent_ids.*' => ['integer', 'min:1'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$payload = $validator->validated();
|
|
$parentIds = [];
|
|
if (! empty($payload['parent_ids'])) {
|
|
$parentIds = array_values(array_unique(array_map('intval', $payload['parent_ids'])));
|
|
}
|
|
if (! empty($payload['parent_id'])) {
|
|
$parentIds[] = (int) $payload['parent_id'];
|
|
}
|
|
$parentIds = array_values(array_unique(array_filter($parentIds)));
|
|
|
|
$data = $this->directoryService->listStudents($payload['school_year'] ?? null, $parentIds ?: null);
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'students' => $data['students'],
|
|
'school_year' => $data['schoolYear'],
|
|
'current_year' => $data['currentYear'],
|
|
]);
|
|
}
|
|
|
|
public function scoreCardSelectable(Request $request): JsonResponse
|
|
{
|
|
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
|
|
$validator = Validator::make($data, [
|
|
'q' => ['nullable', 'string', 'max:255'],
|
|
'limit' => ['nullable', 'integer', 'min:1', 'max:1000'],
|
|
'class_section_id' => ['nullable', 'integer', 'min:1'],
|
|
'school_year' => ['nullable', 'string', 'max:32'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$payload = $validator->validated();
|
|
$query = trim((string) ($payload['q'] ?? ''));
|
|
$limit = (int) ($payload['limit'] ?? ($query !== '' ? 50 : 1000));
|
|
$classSectionId = isset($payload['class_section_id']) ? (int) $payload['class_section_id'] : null;
|
|
$schoolYear = isset($payload['school_year']) ? trim((string) $payload['school_year']) : null;
|
|
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
return response()->json([
|
|
'ok' => false,
|
|
'message' => 'Unauthorized.',
|
|
], 401);
|
|
}
|
|
|
|
$students = $this->scoreCardService->scoreCardSelectableStudents(
|
|
$user,
|
|
$query,
|
|
$limit,
|
|
$classSectionId > 0 ? $classSectionId : null,
|
|
$schoolYear !== '' ? $schoolYear : null,
|
|
);
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'students' => $students,
|
|
'selectable' => $students,
|
|
]);
|
|
}
|
|
|
|
public function show(int $studentId): JsonResponse
|
|
{
|
|
$student = Student::query()->find($studentId);
|
|
if (! $student) {
|
|
return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
|
|
}
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'student' => $student->only([
|
|
'id',
|
|
'school_id',
|
|
'firstname',
|
|
'lastname',
|
|
'dob',
|
|
'age',
|
|
'gender',
|
|
'is_active',
|
|
'registration_grade',
|
|
'is_new',
|
|
'photo_consent',
|
|
'parent_id',
|
|
'registration_date',
|
|
'tuition_paid',
|
|
'semester',
|
|
'year_of_registration',
|
|
'school_year',
|
|
]),
|
|
]);
|
|
}
|
|
|
|
public function store(Request $request): JsonResponse
|
|
{
|
|
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
|
|
$parentId = (int) ($data['parent_id'] ?? 0);
|
|
$isFirstForParent = $parentId > 0
|
|
&& Student::query()->where('parent_id', $parentId)->count() === 0;
|
|
if (empty($data['school_id'])) {
|
|
$nextId = (int) Student::query()->max('id') + 1;
|
|
$data['school_id'] = $this->generateSchoolId($nextId);
|
|
}
|
|
|
|
$rules = [
|
|
'school_id' => ['nullable', 'string', 'max:50'],
|
|
'firstname' => ['required', 'string', 'max:150'],
|
|
'lastname' => ['required', 'string', 'max:150'],
|
|
'dob' => ['nullable', 'date'],
|
|
'age' => ['required', 'integer', 'min:0'],
|
|
'gender' => ['required', 'string', 'max:20'],
|
|
'is_active' => ['nullable', 'boolean'],
|
|
'registration_grade' => ['nullable', 'string', 'max:50'],
|
|
'is_new' => ['nullable', 'boolean'],
|
|
'photo_consent' => ['required', 'boolean'],
|
|
'parent_id' => ['required', 'integer', 'min:1'],
|
|
'registration_date' => ['nullable', 'date'],
|
|
'tuition_paid' => ['nullable', 'boolean'],
|
|
'semester' => ['nullable', 'string', 'max:50'],
|
|
'year_of_registration' => ['required', 'integer', 'min:1900'],
|
|
'school_year' => ['required', 'string', 'max:50'],
|
|
'medical_conditions' => ['nullable', 'string'],
|
|
'allergies' => ['nullable', 'string'],
|
|
];
|
|
|
|
if ($isFirstForParent) {
|
|
$rules = array_merge($rules, [
|
|
'name' => ['required', 'string', 'max:150'],
|
|
'cellphone' => ['required', 'string', 'max:30'],
|
|
'email' => ['required', 'email', 'max:150'],
|
|
'relation' => ['nullable', 'string', 'max:80'],
|
|
'semester' => ['nullable', 'string', 'max:50'],
|
|
'school_year' => ['nullable', 'string', 'max:50'],
|
|
]);
|
|
}
|
|
|
|
$validator = Validator::make($data, $rules);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$payload = $validator->validated();
|
|
$studentPayload = collect($payload)->only([
|
|
'school_id',
|
|
'firstname',
|
|
'lastname',
|
|
'dob',
|
|
'age',
|
|
'gender',
|
|
'is_active',
|
|
'registration_grade',
|
|
'is_new',
|
|
'photo_consent',
|
|
'parent_id',
|
|
'registration_date',
|
|
'tuition_paid',
|
|
'semester',
|
|
'year_of_registration',
|
|
'school_year',
|
|
])->all();
|
|
|
|
$student = Student::query()->create($studentPayload);
|
|
|
|
if (empty($student->school_id)) {
|
|
$student->school_id = $this->generateSchoolId($student->id);
|
|
$student->save();
|
|
}
|
|
|
|
$healthPayload = [];
|
|
if (array_key_exists('medical_conditions', $payload)) {
|
|
$healthPayload['medical_conditions'] = $payload['medical_conditions'];
|
|
}
|
|
if (array_key_exists('allergies', $payload)) {
|
|
$healthPayload['allergies'] = $payload['allergies'];
|
|
}
|
|
if (! empty($healthPayload)) {
|
|
$this->profileService->updateStudent($student->id, $healthPayload);
|
|
}
|
|
|
|
if ($isFirstForParent) {
|
|
$contactPayload = [
|
|
'parent_id' => $parentId,
|
|
'emergency_contact_name' => $payload['name'],
|
|
'cellphone' => $payload['cellphone'],
|
|
'email' => $payload['email'],
|
|
'relation' => $payload['relation'] ?? null,
|
|
'semester' => $payload['semester'] ?? null,
|
|
'school_year' => $payload['school_year'] ?? null,
|
|
];
|
|
if (Schema::hasColumn('emergency_contacts', 'student_id')) {
|
|
$contactPayload['student_id'] = $student->id;
|
|
}
|
|
|
|
EmergencyContact::query()->create($contactPayload);
|
|
}
|
|
|
|
return response()->json(['ok' => true, 'student' => $student], 201);
|
|
}
|
|
|
|
public function assignClass(StudentAssignClassRequest $request): JsonResponse
|
|
{
|
|
$payload = $request->validated();
|
|
$result = $this->assignmentService->assignClasses(
|
|
(int) $payload['student_id'],
|
|
$payload['class_section_id'],
|
|
(bool) ($payload['is_event_only'] ?? false),
|
|
(int) (Auth::id() ?? 0)
|
|
);
|
|
|
|
$status = $result['ok'] ? 200 : 422;
|
|
|
|
return response()->json($result + ['ok' => (bool) $result['ok']], $status);
|
|
}
|
|
|
|
public function removeClass(StudentRemoveClassRequest $request): JsonResponse
|
|
{
|
|
$payload = $request->validated();
|
|
$result = $this->assignmentService->removeClass(
|
|
(int) $payload['student_id'],
|
|
(int) $payload['class_section_id'],
|
|
(int) (Auth::id() ?? 0)
|
|
);
|
|
|
|
$status = $result['ok'] ? 200 : 422;
|
|
|
|
return response()->json($result + ['ok' => (bool) $result['ok']], $status);
|
|
}
|
|
|
|
public function removed(StudentAssignmentListRequest $request): JsonResponse
|
|
{
|
|
$data = $this->directoryService->removedStudents($request->validated()['school_year'] ?? null);
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'active_students' => StudentRemovedResource::collection($data['active_students']),
|
|
'removed_students' => StudentRemovedResource::collection($data['removed_students']),
|
|
'school_year' => $data['school_year'],
|
|
]);
|
|
}
|
|
|
|
public function setActive(StudentSetActiveRequest $request): JsonResponse
|
|
{
|
|
$payload = $request->validated();
|
|
$result = $this->statusService->setActive(
|
|
(int) $payload['student_id'],
|
|
(bool) $payload['is_active'],
|
|
(int) (Auth::id() ?? 0)
|
|
);
|
|
|
|
$status = $result['ok'] ? 200 : 422;
|
|
|
|
return response()->json($result + ['ok' => (bool) $result['ok']], $status);
|
|
}
|
|
|
|
public function autoDistribute(StudentAutoDistributeRequest $request): JsonResponse
|
|
{
|
|
$payload = $request->validated();
|
|
$result = $this->autoDistributionService->autoDistribute(
|
|
(int) ($payload['class_id'] ?? 0),
|
|
(int) $payload['students_per_section'],
|
|
$payload['school_year'] ?? null,
|
|
(int) ($payload['class_section_id'] ?? 0),
|
|
(int) (Auth::id() ?? 0)
|
|
);
|
|
|
|
$status = $result['ok'] ? 200 : 422;
|
|
|
|
return response()->json($result + ['ok' => (bool) $result['ok']], $status);
|
|
}
|
|
|
|
public function promotionTotals(StudentPromotionTotalsRequest $request): JsonResponse
|
|
{
|
|
$data = $this->autoDistributionService->promotionTotals($request->validated()['school_year'] ?? null);
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'year' => $data['year'],
|
|
'rows' => $data['rows'],
|
|
]);
|
|
}
|
|
|
|
public function update(StudentUpdateRequest $request, int $studentId): JsonResponse
|
|
{
|
|
$result = $this->profileService->updateStudent($studentId, $request->validated());
|
|
$status = $result['ok'] ? 200 : 422;
|
|
|
|
return response()->json($result + ['ok' => (bool) $result['ok']], $status);
|
|
}
|
|
|
|
public function destroy(int $studentId): JsonResponse
|
|
{
|
|
$student = Student::query()->find($studentId);
|
|
if (! $student) {
|
|
return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
|
|
}
|
|
|
|
$student->update(['is_active' => 0]);
|
|
|
|
return response()->json(['ok' => true]);
|
|
}
|
|
|
|
public function classes(Request $request, int $studentId): JsonResponse
|
|
{
|
|
$validator = Validator::make($request->query->all(), [
|
|
'school_year' => ['nullable', 'string', 'max:50'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$schoolYear = $validator->validated()['school_year'] ?? null;
|
|
|
|
$rows = StudentClass::query()
|
|
->select('student_class.*', 'cs.class_section_name')
|
|
->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'student_class.class_section_id')
|
|
->where('student_class.student_id', $studentId)
|
|
->when($schoolYear !== null && $schoolYear !== '', fn ($q) => $q->where('student_class.school_year', $schoolYear))
|
|
->orderBy('cs.class_section_name')
|
|
->get()
|
|
->toArray();
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'student_id' => $studentId,
|
|
'school_year' => $schoolYear,
|
|
'classes' => $rows,
|
|
]);
|
|
}
|
|
|
|
public function assignClassForStudent(Request $request, int $studentId): JsonResponse
|
|
{
|
|
$data = array_merge($request->all(), $request->json()->all());
|
|
$validator = Validator::make($data, [
|
|
'student_id' => ['required', 'integer', 'min:1'],
|
|
'class_section_id' => ['nullable', 'integer', 'min:1'],
|
|
'class_section_ids' => ['nullable', 'array'],
|
|
'class_section_ids.*' => ['integer', 'min:1'],
|
|
'is_event_only' => ['nullable', 'boolean'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$payload = $validator->validated();
|
|
if ((int) $payload['student_id'] !== $studentId) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => ['student_id' => ['student_id does not match the route studentId.']],
|
|
], 422);
|
|
}
|
|
|
|
$ids = $payload['class_section_ids'] ?? [];
|
|
if (empty($ids) && isset($payload['class_section_id'])) {
|
|
$ids = [(int) $payload['class_section_id']];
|
|
}
|
|
|
|
$result = $this->assignmentService->assignClasses(
|
|
$studentId,
|
|
$ids,
|
|
(bool) ($payload['is_event_only'] ?? false),
|
|
(int) (Auth::id() ?? 0)
|
|
);
|
|
|
|
$status = $result['ok'] ? 200 : 422;
|
|
|
|
return response()->json($result + ['ok' => (bool) $result['ok']], $status);
|
|
}
|
|
|
|
public function removeClassForStudent(int $studentId, int $classSectionId): JsonResponse
|
|
{
|
|
$result = $this->assignmentService->removeClass(
|
|
$studentId,
|
|
$classSectionId,
|
|
(int) (Auth::id() ?? 0)
|
|
);
|
|
|
|
$status = $result['ok'] ? 200 : 422;
|
|
|
|
return response()->json($result + ['ok' => (bool) $result['ok']], $status);
|
|
}
|
|
|
|
public function promote(Request $request, int $studentId): JsonResponse
|
|
{
|
|
$data = array_merge($request->all(), $request->json()->all());
|
|
$validator = Validator::make($data, [
|
|
'class_section_id' => ['required', 'integer', 'min:1'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$payload = $validator->validated();
|
|
$result = $this->assignmentService->assignClasses(
|
|
$studentId,
|
|
[(int) $payload['class_section_id']],
|
|
false,
|
|
(int) (Auth::id() ?? 0)
|
|
);
|
|
|
|
$status = $result['ok'] ? 200 : 422;
|
|
|
|
return response()->json($result + ['ok' => (bool) $result['ok']], $status);
|
|
}
|
|
|
|
public function attendance(Request $request, int $studentId): JsonResponse
|
|
{
|
|
$validator = Validator::make($request->query->all(), [
|
|
'school_year' => ['nullable', 'string', 'max:50'],
|
|
'semester' => ['nullable', 'string', 'max:50'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$payload = $validator->validated();
|
|
|
|
$rows = AttendanceRecord::query()
|
|
->where('student_id', $studentId)
|
|
->when(! empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year']))
|
|
->when(! empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester']))
|
|
->orderBy('school_year', 'desc')
|
|
->orderBy('semester', 'desc')
|
|
->get()
|
|
->toArray();
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'student_id' => $studentId,
|
|
'rows' => $rows,
|
|
]);
|
|
}
|
|
|
|
public function incidents(Request $request, int $studentId): JsonResponse
|
|
{
|
|
$validator = Validator::make($request->query->all(), [
|
|
'school_year' => ['nullable', 'string', 'max:50'],
|
|
'semester' => ['nullable', 'string', 'max:50'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$payload = $validator->validated();
|
|
|
|
$rows = Incident::query()
|
|
->where('student_id', $studentId)
|
|
->when(! empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year']))
|
|
->when(! empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester']))
|
|
->orderByDesc('incident_datetime')
|
|
->get()
|
|
->toArray();
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'student_id' => $studentId,
|
|
'rows' => $rows,
|
|
]);
|
|
}
|
|
|
|
public function scores(Request $request, int $studentId): JsonResponse
|
|
{
|
|
$validator = Validator::make($request->query->all(), [
|
|
'school_year' => ['nullable', 'string', 'max:50'],
|
|
'semester' => ['nullable', 'string', 'max:50'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$payload = $validator->validated();
|
|
|
|
$rows = SemesterScore::query()
|
|
->where('student_id', $studentId)
|
|
->when(! empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year']))
|
|
->when(! empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester']))
|
|
->orderBy('school_year', 'desc')
|
|
->orderBy('semester', 'desc')
|
|
->get()
|
|
->toArray();
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'student_id' => $studentId,
|
|
'rows' => $rows,
|
|
]);
|
|
}
|
|
|
|
public function notes(int $studentId): JsonResponse
|
|
{
|
|
return response()->json([
|
|
'ok' => true,
|
|
'student_id' => $studentId,
|
|
'rows' => [],
|
|
]);
|
|
}
|
|
|
|
public function parents(int $studentId): JsonResponse
|
|
{
|
|
$student = Student::query()->find($studentId);
|
|
if (! $student) {
|
|
return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
|
|
}
|
|
|
|
$parent = null;
|
|
if (! empty($student->parent_id)) {
|
|
$parent = User::query()
|
|
->select(['id', 'firstname', 'lastname', 'email', 'cellphone'])
|
|
->find((int) $student->parent_id);
|
|
}
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'student_id' => $studentId,
|
|
'parent' => $parent,
|
|
]);
|
|
}
|
|
|
|
public function emergencyContacts(int $studentId): JsonResponse
|
|
{
|
|
$student = Student::query()->find($studentId);
|
|
if (! $student) {
|
|
return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
|
|
}
|
|
|
|
$query = EmergencyContact::query();
|
|
if (Schema::hasColumn('emergency_contacts', 'student_id')) {
|
|
$query->where('student_id', $studentId);
|
|
}
|
|
if (! empty($student->parent_id)) {
|
|
$query->orWhere('parent_id', $student->parent_id);
|
|
}
|
|
|
|
$rows = $query
|
|
->orderBy('emergency_contact_name')
|
|
->get()
|
|
->toArray();
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'student_id' => $studentId,
|
|
'rows' => $rows,
|
|
]);
|
|
}
|
|
|
|
public function addEmergencyContact(Request $request, int $studentId): JsonResponse
|
|
{
|
|
$student = Student::query()->find($studentId);
|
|
if (! $student) {
|
|
return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
|
|
}
|
|
|
|
$data = array_merge($request->all(), $request->json()->all());
|
|
$validator = Validator::make($data, [
|
|
'name' => ['required', 'string', 'max:150'],
|
|
'cellphone' => ['required', 'string', 'max:30'],
|
|
'email' => ['nullable', 'email', 'max:150'],
|
|
'relation' => ['nullable', 'string', 'max:80'],
|
|
'semester' => ['nullable', 'string', 'max:50'],
|
|
'school_year' => ['nullable', 'string', 'max:50'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$payload = $validator->validated();
|
|
$create = [
|
|
'parent_id' => $student->parent_id,
|
|
'emergency_contact_name' => $payload['name'],
|
|
'cellphone' => $payload['cellphone'],
|
|
'email' => $payload['email'] ?? null,
|
|
'relation' => $payload['relation'] ?? null,
|
|
'semester' => $payload['semester'] ?? null,
|
|
'school_year' => $payload['school_year'] ?? null,
|
|
];
|
|
if (Schema::hasColumn('emergency_contacts', 'student_id')) {
|
|
$create['student_id'] = $studentId;
|
|
}
|
|
|
|
$row = EmergencyContact::query()->create($create);
|
|
|
|
return response()->json(['ok' => true, 'row' => $row], 201);
|
|
}
|
|
|
|
public function updateEmergencyContact(Request $request, int $studentId, int $contactId): JsonResponse
|
|
{
|
|
$student = Student::query()->find($studentId);
|
|
if (! $student) {
|
|
return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
|
|
}
|
|
|
|
$data = array_merge($request->all(), $request->json()->all());
|
|
$validator = Validator::make($data, [
|
|
'name' => ['nullable', 'string', 'max:150'],
|
|
'cellphone' => ['nullable', 'string', 'max:30'],
|
|
'email' => ['nullable', 'email', 'max:150'],
|
|
'relation' => ['nullable', 'string', 'max:80'],
|
|
'semester' => ['nullable', 'string', 'max:50'],
|
|
'school_year' => ['nullable', 'string', 'max:50'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$contact = EmergencyContact::query()->find($contactId);
|
|
if (! $contact) {
|
|
return response()->json(['ok' => false, 'message' => 'Contact not found.'], 404);
|
|
}
|
|
|
|
$payload = $validator->validated();
|
|
$contact->update(array_filter([
|
|
'emergency_contact_name' => $payload['name'] ?? null,
|
|
'cellphone' => $payload['cellphone'] ?? null,
|
|
'email' => $payload['email'] ?? null,
|
|
'relation' => $payload['relation'] ?? null,
|
|
'semester' => $payload['semester'] ?? null,
|
|
'school_year' => $payload['school_year'] ?? null,
|
|
], static fn ($v) => $v !== null));
|
|
|
|
return response()->json(['ok' => true, 'row' => $contact]);
|
|
}
|
|
|
|
public function updateBadgeScan(Request $request, int $studentId): JsonResponse
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'badge_scan' => ['required', 'string', 'max:100'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$student = Student::query()->find($studentId);
|
|
if (! $student) {
|
|
return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
|
|
}
|
|
|
|
$student->update(['rfid_tag' => $validator->validated()['badge_scan']]);
|
|
|
|
return response()->json(['ok' => true]);
|
|
}
|
|
|
|
public function uploadPhoto(Request $request, int $studentId): JsonResponse
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'photo' => ['required', 'file', 'image', 'max:5120'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json([
|
|
'message' => 'Validation failed.',
|
|
'errors' => $validator->errors(),
|
|
], 422);
|
|
}
|
|
|
|
$student = Student::query()->find($studentId);
|
|
if (! $student) {
|
|
return response()->json(['ok' => false, 'message' => 'Student not found.'], 404);
|
|
}
|
|
|
|
$file = $request->file('photo');
|
|
$ext = $file->getClientOriginalExtension() ?: 'jpg';
|
|
$path = "student-photos/{$studentId}.".strtolower($ext);
|
|
Storage::disk('public')->putFileAs('student-photos', $file, "{$studentId}.".strtolower($ext));
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'photo_url' => Storage::disk('public')->url($path),
|
|
]);
|
|
}
|
|
|
|
public function photo(int $studentId): JsonResponse
|
|
{
|
|
$base = "student-photos/{$studentId}";
|
|
$candidates = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
|
$disk = Storage::disk('public');
|
|
|
|
foreach ($candidates as $ext) {
|
|
$path = $base.'.'.$ext;
|
|
if ($disk->exists($path)) {
|
|
return response()->json([
|
|
'ok' => true,
|
|
'photo_url' => $disk->url($path),
|
|
]);
|
|
}
|
|
}
|
|
|
|
return response()->json(['ok' => false, 'message' => 'Photo not found.'], 404);
|
|
}
|
|
|
|
private function generateSchoolId(int $studentId): string
|
|
{
|
|
$year = (int) date('y');
|
|
|
|
return 'STU'.$year.str_pad((string) $studentId, 4, '0', STR_PAD_LEFT);
|
|
}
|
|
|
|
public function scoreCard(int $studentId): JsonResponse
|
|
{
|
|
$result = $this->scoreCardService->scoreCard($studentId);
|
|
if (! $result['ok']) {
|
|
return response()->json(['ok' => false, 'message' => $result['message'] ?? 'Not found.'], 404);
|
|
}
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'student' => $result['student'],
|
|
'rows' => StudentScoreCardRowResource::collection($result['rows']),
|
|
]);
|
|
}
|
|
}
|