196 lines
7.5 KiB
PHP
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),
|
|
];
|
|
}
|
|
}
|