feat: update CourseService and CourseController to accept User parameter for improved scoping
This commit is contained in:
parent
2b458e26fa
commit
1c901e9103
@ -161,6 +161,21 @@ ## 4. Controller, Request, dan Service
|
||||
- **Controller** hanya mengatur alur request → response (validasi input dipanggil, service dipanggil, redirect/Inertia render dikembalikan). **Tidak boleh** ada query Eloquent kompleks atau business logic langsung di controller.
|
||||
- **Selalu gunakan FormRequest**, sekecil apapun validasinya — jangan validasi inline di controller dengan `$request->validate()`.
|
||||
- Business logic (kalkulasi, orkestrasi antar model, side effect seperti notifikasi/log) **disimpan di Service**, bukan di controller atau model.
|
||||
- **Kalau method Service butuh user yang sedang login untuk scoping/filtering** (mis. dosen cuma lihat kelasnya sendiri, mahasiswa cuma lihat jurusannya sendiri), terima sebagai parameter eksplisit `User $user` (non-nullable, tanpa default) di **posisi pertama** — jangan panggil `auth()->user()` langsung di dalam Service. Controller yang menyuplainya lewat `$request->user()`. Ini soal testability (Service tidak bergantung diam-diam ke global state) dan konsistensi lintas Service.
|
||||
```php
|
||||
// Bad
|
||||
public function paginated(int $perPage = 25): LengthAwarePaginator
|
||||
{
|
||||
$user = auth()->user();
|
||||
// ...
|
||||
}
|
||||
|
||||
// Good
|
||||
public function paginated(User $user, int $perPage = 25): LengthAwarePaginator
|
||||
{
|
||||
// ...
|
||||
}
|
||||
```
|
||||
- Otorisasi berbasis permission (Spatie) dicek di dua tempat:
|
||||
- Route-level: middleware `permission:create-xxx` dipasang per-route/per-group.
|
||||
- Object-level (mis. user hanya boleh mengubah record miliknya sendiri): di `authorize()` milik FormRequest, kombinasikan `$this->user()->can('permission-name')` dengan pengecekan kepemilikan record.
|
||||
|
||||
@ -23,6 +23,7 @@ public function index(PaginatedRequest $request): Response
|
||||
{
|
||||
return Inertia::render('admin/master/courses/index', [
|
||||
'courses' => $this->service->paginated(
|
||||
$request->user(),
|
||||
...$request->validatedWithDefaults(),
|
||||
departmentId: $request->validated('department_id'),
|
||||
semesterNumber: $request->validated('semester_number'),
|
||||
|
||||
@ -2,52 +2,28 @@
|
||||
|
||||
namespace App\Services\Admin\Master;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
use App\Models\Course;
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class CourseService
|
||||
{
|
||||
public function getAllForSelect(): Collection
|
||||
public function paginated(User $user, int $perPage = 25, string $search = '', ?int $departmentId = null, ?int $semesterNumber = null): LengthAwarePaginator
|
||||
{
|
||||
return Course::query()
|
||||
->select(['courses.id', 'courses.code', 'courses.name', 'courses.department_id', 'courses.semester_number'])
|
||||
->join('departments', 'departments.id', '=', 'courses.department_id')
|
||||
->with('department:id,name')
|
||||
->orderBy('departments.name')
|
||||
->orderBy('courses.semester_number')
|
||||
->orderBy('courses.name')
|
||||
->get();
|
||||
}
|
||||
|
||||
public function getSemesterNumbers(): array
|
||||
{
|
||||
return Course::query()
|
||||
->select('semester_number')
|
||||
->whereNotNull('semester_number')
|
||||
->distinct()
|
||||
->orderBy('semester_number')
|
||||
->pluck('semester_number')
|
||||
->all();
|
||||
}
|
||||
|
||||
public function paginated(int $perPage = 25, string $search = '', ?int $departmentId = null, ?int $semesterNumber = null): LengthAwarePaginator
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
return Course::query()
|
||||
->select(['courses.id', 'courses.code', 'courses.name', 'courses.credits', 'courses.department_id', 'courses.semester_number'])
|
||||
->join('departments', 'departments.id', '=', 'courses.department_id')
|
||||
->with('department:id,name')
|
||||
->when($search, fn ($q) => $q->where('courses.name', 'like', "%{$search}%")->orWhere('courses.code', 'like', "%{$search}%"))
|
||||
->when($departmentId, fn ($q) => $q->where('courses.department_id', $departmentId))
|
||||
->when($semesterNumber, fn ($q) => $q->where('courses.semester_number', $semesterNumber))
|
||||
->when($user->hasRole('mahasiswa'), fn ($q) => $q->where('courses.department_id', $user->student?->department_id))
|
||||
->when($user->hasRole('dosen'), fn ($q) => $q->whereIn('courses.department_id', $user->lecturer?->departments()->pluck('departments.id') ?? []))
|
||||
->when($user->hasRole(UserRole::Mahasiswa->value), fn ($q) => $q->where('courses.department_id', $user->student?->department_id))
|
||||
->when($user->hasRole(UserRole::Dosen->value), fn ($q) => $q->whereIn('courses.department_id', $user->lecturer?->departments()->pluck('departments.id') ?? []))
|
||||
->orderBy('departments.name')
|
||||
->orderBy('courses.semester_number')
|
||||
->orderBy('courses.name')
|
||||
->paginate($perPage);
|
||||
->paginate($perPage, ['courses.id', 'courses.code', 'courses.name', 'courses.credits', 'courses.department_id', 'courses.semester_number']);
|
||||
}
|
||||
|
||||
public function create(array $data): Course
|
||||
@ -57,12 +33,13 @@ public function create(array $data): Course
|
||||
|
||||
public function update(Course $course, array $data): Course
|
||||
{
|
||||
$course->code = $data['code'];
|
||||
$course->name = $data['name'];
|
||||
$course->credits = $data['credits'];
|
||||
$course->department_id = $data['department_id'];
|
||||
$course->semester_number = $data['semester_number'] ?? null;
|
||||
$course->update();
|
||||
$course->update([
|
||||
'code' => $data['code'],
|
||||
'name' => $data['name'],
|
||||
'credits' => $data['credits'],
|
||||
'department_id' => $data['department_id'],
|
||||
'semester_number' => $data['semester_number'] ?? null,
|
||||
]);
|
||||
|
||||
return $course;
|
||||
}
|
||||
@ -71,4 +48,25 @@ public function delete(Course $course): bool
|
||||
{
|
||||
return $course->delete();
|
||||
}
|
||||
|
||||
public function getAllForSelect(): Collection
|
||||
{
|
||||
return Course::query()
|
||||
->join('departments', 'departments.id', '=', 'courses.department_id')
|
||||
->with('department:id,name')
|
||||
->orderBy('departments.name')
|
||||
->orderBy('courses.semester_number')
|
||||
->orderBy('courses.name')
|
||||
->get(['courses.id', 'courses.code', 'courses.name', 'courses.department_id', 'courses.semester_number']);
|
||||
}
|
||||
|
||||
public function getSemesterNumbers(): array
|
||||
{
|
||||
return Course::query()
|
||||
->whereNotNull('semester_number')
|
||||
->distinct()
|
||||
->orderBy('semester_number')
|
||||
->pluck('semester_number')
|
||||
->all();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user