231 lines
8.8 KiB
PHP
231 lines
8.8 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\AcademicYear;
|
|
use App\Models\Department;
|
|
use App\Models\SchoolClass;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\View\View;
|
|
use RealRashid\SweetAlert\Facades\Alert;
|
|
|
|
class SchoolClassController extends Controller
|
|
{
|
|
public function index(Request $request): View
|
|
{
|
|
$search = trim((string) $request->get('search'));
|
|
|
|
$classes = SchoolClass::with(['academicYear', 'department'])
|
|
->withCount(['students', 'academicHistories'])
|
|
->when($search !== '', function ($query) use ($search) {
|
|
$query->where('name', 'like', "%{$search}%")
|
|
->orWhere('level', 'like', "%{$search}%")
|
|
->orWhere('homeroom_teacher', 'like', "%{$search}%");
|
|
})
|
|
->latest()
|
|
->paginate(10)
|
|
->withQueryString();
|
|
|
|
return view('classes.index', [
|
|
'classes' => $classes,
|
|
'search' => $search,
|
|
'academicYears' => AcademicYear::orderByDesc('start_year')->orderByDesc('end_year')->get(),
|
|
]);
|
|
}
|
|
|
|
public function create(): View
|
|
{
|
|
return view('classes.create', [
|
|
'academicYears' => AcademicYear::where('is_active', true)->orderBy('name')->pluck('name', 'id'),
|
|
'departments' => Department::where('is_active', true)->orderBy('name')->pluck('name', 'id'),
|
|
]);
|
|
}
|
|
|
|
public function store(Request $request): RedirectResponse
|
|
{
|
|
$data = $request->validate([
|
|
'academic_year_id' => ['required', Rule::exists('academic_years', 'id')->where(fn ($query) => $query->where('is_active', true))],
|
|
'department_id' => ['required', Rule::exists('departments', 'id')->where(fn ($query) => $query->where('is_active', true))],
|
|
'name' => ['required', 'string', 'max:100', Rule::unique('classes', 'name')->where(fn ($query) => $query->where('academic_year_id', $request->academic_year_id))],
|
|
'level' => ['required', 'string', 'max:10'],
|
|
'homeroom_teacher' => ['nullable', 'string', 'max:100'],
|
|
'capacity' => ['nullable', 'integer', 'min:1'],
|
|
'is_active' => ['nullable', 'boolean'],
|
|
]);
|
|
|
|
$data['is_active'] = $request->boolean('is_active');
|
|
|
|
SchoolClass::create($data);
|
|
|
|
return redirect()->route('classes.index')->with('success', 'Kelas berhasil ditambahkan.');
|
|
}
|
|
|
|
public function generate(Request $request): RedirectResponse
|
|
{
|
|
$data = $request->validate([
|
|
'source_academic_year_id' => ['required', 'exists:academic_years,id', 'different:target_academic_year_id'],
|
|
'target_academic_year_id' => ['required', 'exists:academic_years,id'],
|
|
'generation_mode' => ['required', Rule::in(['same_level', 'promote_level'])],
|
|
]);
|
|
|
|
$sourceClasses = SchoolClass::query()
|
|
->where('academic_year_id', $data['source_academic_year_id'])
|
|
->orderBy('department_id')
|
|
->orderBy('level')
|
|
->orderBy('name')
|
|
->get();
|
|
|
|
if ($sourceClasses->isEmpty()) {
|
|
Alert::error('Gagal', 'Tidak ada kelas pada tahun ajaran sumber yang bisa digenerate.');
|
|
|
|
return redirect()->route('classes.index')->withErrors([
|
|
'generate' => 'Tidak ada kelas pada tahun ajaran sumber yang bisa digenerate.',
|
|
]);
|
|
}
|
|
|
|
$created = 0;
|
|
$skipped = 0;
|
|
|
|
DB::transaction(function () use ($sourceClasses, $data, &$created, &$skipped) {
|
|
foreach ($sourceClasses as $sourceClass) {
|
|
$targetLevel = $data['generation_mode'] === 'promote_level'
|
|
? $this->nextLevel($sourceClass->level)
|
|
: $sourceClass->level;
|
|
|
|
if ($targetLevel === null) {
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
$targetName = $this->transformClassName($sourceClass->name, $sourceClass->level, $targetLevel);
|
|
|
|
$exists = SchoolClass::query()
|
|
->where('academic_year_id', $data['target_academic_year_id'])
|
|
->where('name', $targetName)
|
|
->exists();
|
|
|
|
if ($exists) {
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
SchoolClass::create([
|
|
'academic_year_id' => $data['target_academic_year_id'],
|
|
'department_id' => $sourceClass->department_id,
|
|
'name' => $targetName,
|
|
'level' => $targetLevel,
|
|
'homeroom_teacher' => $sourceClass->homeroom_teacher,
|
|
'capacity' => $sourceClass->capacity,
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$created++;
|
|
}
|
|
});
|
|
|
|
Alert::success('Berhasil', "Generate kelas selesai. {$created} kelas dibuat, {$skipped} dilewati.");
|
|
|
|
return redirect()->route('classes.index')->with('success', "Generate kelas selesai. {$created} kelas dibuat, {$skipped} dilewati.");
|
|
}
|
|
|
|
public function show(SchoolClass $class): RedirectResponse
|
|
{
|
|
return redirect()->route('classes.edit', $class);
|
|
}
|
|
|
|
public function edit(SchoolClass $class): View
|
|
{
|
|
$class->loadMissing(['academicYear', 'department']);
|
|
|
|
$academicYears = AcademicYear::query()
|
|
->where(function ($query) use ($class) {
|
|
$query->where('is_active', true)
|
|
->orWhere('id', $class->academic_year_id);
|
|
})
|
|
->orderBy('name')
|
|
->pluck('name', 'id');
|
|
|
|
$departments = Department::query()
|
|
->where(function ($query) use ($class) {
|
|
$query->where('is_active', true)
|
|
->orWhere('id', $class->department_id);
|
|
})
|
|
->orderBy('name')
|
|
->pluck('name', 'id');
|
|
|
|
return view('classes.edit', [
|
|
'class' => $class,
|
|
'academicYears' => $academicYears,
|
|
'departments' => $departments,
|
|
]);
|
|
}
|
|
|
|
public function update(Request $request, SchoolClass $class): RedirectResponse
|
|
{
|
|
$data = $request->validate([
|
|
'academic_year_id' => ['required', Rule::exists('academic_years', 'id')->where(fn ($query) => $query->where('is_active', true)->orWhere('id', $class->academic_year_id))],
|
|
'department_id' => ['required', Rule::exists('departments', 'id')->where(fn ($query) => $query->where('is_active', true)->orWhere('id', $class->department_id))],
|
|
'name' => ['required', 'string', 'max:100', Rule::unique('classes', 'name')->ignore($class->id)->where(fn ($query) => $query->where('academic_year_id', $request->academic_year_id))],
|
|
'level' => ['required', 'string', 'max:10'],
|
|
'homeroom_teacher' => ['nullable', 'string', 'max:100'],
|
|
'capacity' => ['nullable', 'integer', 'min:1'],
|
|
'is_active' => ['nullable', 'boolean'],
|
|
]);
|
|
|
|
$data['is_active'] = $request->boolean('is_active');
|
|
|
|
$class->update($data);
|
|
|
|
return redirect()->route('classes.index')->with('success', 'Kelas berhasil diperbarui.');
|
|
}
|
|
|
|
public function destroy(SchoolClass $class): RedirectResponse
|
|
{
|
|
$class->loadCount(['students', 'academicHistories']);
|
|
|
|
if ($class->students_count > 0) {
|
|
Alert::error('Tidak bisa dihapus', 'Kelas ini masih digunakan oleh data siswa aktif.');
|
|
|
|
return redirect()->route('classes.index')->withErrors([
|
|
'delete' => 'Kelas tidak bisa dihapus karena masih digunakan oleh data siswa aktif.',
|
|
]);
|
|
}
|
|
|
|
if ($class->academic_histories_count > 0) {
|
|
Alert::error('Tidak bisa dihapus', 'Kelas ini masih tercatat pada histori akademik siswa.');
|
|
|
|
return redirect()->route('classes.index')->withErrors([
|
|
'delete' => 'Kelas tidak bisa dihapus karena masih tercatat pada histori akademik siswa. Hapus atau ubah histori terkait terlebih dahulu.',
|
|
]);
|
|
}
|
|
|
|
Alert::success('Berhasil', 'Kelas berhasil dihapus.');
|
|
|
|
$class->delete();
|
|
|
|
return redirect()->route('classes.index')->with('success', 'Kelas berhasil dihapus.');
|
|
}
|
|
|
|
protected function nextLevel(?string $level): ?string
|
|
{
|
|
return match (strtoupper((string) $level)) {
|
|
'X' => 'XI',
|
|
'XI' => 'XII',
|
|
'XII' => null,
|
|
default => $level,
|
|
};
|
|
}
|
|
|
|
protected function transformClassName(string $name, ?string $fromLevel, ?string $toLevel): string
|
|
{
|
|
if (! $fromLevel || ! $toLevel || strtoupper($fromLevel) === strtoupper($toLevel)) {
|
|
return $name;
|
|
}
|
|
|
|
return preg_replace('/^' . preg_quote($fromLevel, '/') . '\b/i', $toLevel, $name) ?: $name;
|
|
}
|
|
}
|