Files
presensi/app/Modules/Academic/Controllers/StudentController.php
2026-03-05 14:37:36 +07:00

196 lines
7.5 KiB
PHP

<?php
namespace App\Modules\Academic\Controllers;
use App\Core\BaseApiController;
use App\Modules\Academic\Models\StudentModel;
use App\Modules\Academic\Models\ClassModel;
use CodeIgniter\HTTP\ResponseInterface;
/**
* Student CRUD API (ADMIN only).
*/
class StudentController extends BaseApiController
{
/**
* GET /api/academic/students
* Query: class_id (optional), search (optional), unmapped_only (optional), page (default 1), per_page (default 25).
*/
public function index(): ResponseInterface
{
$classId = $this->request->getGet('class_id');
$search = $this->request->getGet('search');
$search = is_string($search) ? trim($search) : '';
$unmappedOnly = $this->request->getGet('unmapped_only') === '1' || $this->request->getGet('unmapped_only') === 'true';
$page = max(1, (int) ($this->request->getGet('page') ?? 1));
$perPage = max(5, min(100, (int) ($this->request->getGet('per_page') ?? 20)));
$db = \Config\Database::connect();
$builder = $db->table('students')
->select('students.id, students.nisn, students.name, students.gender, students.class_id, students.is_active,
classes.grade, classes.major, classes.name AS class_name')
->join('classes', 'classes.id = students.class_id', 'left')
->orderBy('students.name', 'ASC');
if ($unmappedOnly) {
$builder->where('students.class_id', null);
} elseif ($classId !== null && $classId !== '') {
$builder->where('students.class_id', (int) $classId);
}
if ($search !== '') {
$builder->groupStart()
->like('students.name', $search)
->orLike('students.nisn', $search)
->groupEnd();
}
$total = $builder->countAllResults(false);
$rows = $builder->limit($perPage, ($page - 1) * $perPage)->get()->getResultArray();
$data = array_map(static function ($r) {
$grade = $r['grade'] !== null ? trim((string) $r['grade']) : '';
$major = $r['major'] !== null ? trim((string) $r['major']) : '';
$cName = $r['class_name'] !== null ? trim((string) $r['class_name']) : '';
$parts = array_filter([$grade, $major, $cName], static fn ($v) => $v !== '');
$fullLabel = $parts !== [] ? implode(' ', $parts) : null;
return [
'id' => (int) $r['id'],
'nisn' => (string) $r['nisn'],
'name' => (string) $r['name'],
'gender' => $r['gender'] !== null ? (string) $r['gender'] : null,
'class_id' => $r['class_id'] !== null ? (int) $r['class_id'] : null,
'class_label' => $fullLabel,
'is_active' => (int) ($r['is_active'] ?? 1),
];
}, $rows);
$totalPages = $total > 0 ? (int) ceil($total / $perPage) : 0;
$meta = [
'total' => $total,
'page' => $page,
'per_page' => $perPage,
'total_pages' => $totalPages,
];
return $this->successResponse($data, 'Students', $meta);
}
/**
* POST /api/academic/students
*/
public function create(): ResponseInterface
{
$payload = $this->request->getJSON(true) ?? [];
$data = $this->payloadToStudentData($payload);
$model = new StudentModel();
if (! $model->validate($data)) {
return $this->errorResponse(
implode(' ', $model->errors()),
$model->errors(),
null,
ResponseInterface::HTTP_UNPROCESSABLE_ENTITY
);
}
$id = $model->insert($data);
if ($id === false) {
return $this->errorResponse('Gagal menyimpan siswa', null, null, ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
}
$row = $model->find($id);
return $this->successResponse($this->rowToResponse($row), 'Siswa berhasil ditambahkan', null, ResponseInterface::HTTP_CREATED);
}
/**
* PUT /api/academic/students/{id}
*/
public function update(int $id): ResponseInterface
{
$model = new StudentModel();
$row = $model->find($id);
if (! $row) {
return $this->errorResponse('Siswa tidak ditemukan', null, null, ResponseInterface::HTTP_NOT_FOUND);
}
$payload = $this->request->getJSON(true) ?? [];
$data = $this->payloadToStudentData($payload, $row);
if (! $model->validate(array_merge(['id' => $id], $data))) {
return $this->errorResponse(
implode(' ', $model->errors()),
$model->errors(),
null,
ResponseInterface::HTTP_UNPROCESSABLE_ENTITY
);
}
if ($model->update($id, $data) === false) {
return $this->errorResponse('Gagal mengubah siswa', null, null, ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
}
$updated = $model->find($id);
return $this->successResponse($this->rowToResponse($updated), 'Siswa berhasil diubah');
}
/**
* DELETE /api/academic/students/{id}
*/
public function delete(int $id): ResponseInterface
{
$model = new StudentModel();
if (! $model->find($id)) {
return $this->errorResponse('Siswa tidak ditemukan', null, null, ResponseInterface::HTTP_NOT_FOUND);
}
if ($model->delete($id) === false) {
return $this->errorResponse('Gagal menghapus siswa', null, null, ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
}
return $this->successResponse(null, 'Siswa berhasil dihapus');
}
private function payloadToStudentData(array $payload, $existing = null): array
{
$classId = isset($payload['class_id']) && $payload['class_id'] !== '' && $payload['class_id'] !== null
? (int) $payload['class_id']
: null;
$gender = isset($payload['gender']) && in_array($payload['gender'], ['L', 'P'], true)
? $payload['gender']
: ($existing && isset($existing->gender) ? $existing->gender : null);
$isActive = array_key_exists('is_active', $payload)
? (int) (bool) $payload['is_active']
: ($existing && isset($existing->is_active) ? (int) $existing->is_active : 1);
return [
'nisn' => trim($payload['nisn'] ?? $existing->nisn ?? ''),
'name' => trim($payload['name'] ?? $existing->name ?? ''),
'gender' => $gender,
'class_id' => $classId,
'is_active'=> $isActive,
];
}
private function rowToResponse($row): array
{
$classId = $row->class_id ?? null;
$classLabel = null;
if ($classId !== null) {
$classModel = new ClassModel();
$c = $classModel->find($classId);
if ($c) {
$classLabel = trim($c->grade . ' ' . $c->major . ' ' . $c->name) ?: (string) $c->name;
}
}
return [
'id' => (int) $row->id,
'nisn' => (string) $row->nisn,
'name' => (string) $row->name,
'gender' => $row->gender !== null ? (string) $row->gender : null,
'class_id' => $classId !== null ? (int) $classId : null,
'class_label' => $classLabel,
'is_active' => (int) ($row->is_active ?? 1),
];
}
}