<?php namespace App\Http\Controllers; use App\Repositories\Medium\MediumInterface; use App\Repositories\Subject\SubjectInterface; use App\Rules\uniqueForSchool; use App\Services\BootstrapTableService; use App\Services\ResponseService; use App\Services\CachingService; use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; use App\Models\ClassSubject; use App\Rules\MaxFileSize; use Throwable; class SubjectController extends Controller { private MediumInterface $medium; private SubjectInterface $subject; private CachingService $cache; public function __construct(MediumInterface $medium, SubjectInterface $subject, CachingService $cache) { $this->medium = $medium; $this->subject = $subject; $this->cache = $cache; } public function index() { ResponseService::noPermissionThenRedirect('subject-list'); $mediums = $this->medium->builder()->orderBy('id', 'DESC')->get(); return response(view('subjects.index', compact('mediums'))); } public function show(Request $request) { ResponseService::noPermissionThenRedirect('subject-list'); $offset = request('offset', 0); $limit = request('limit', 10); $sort = request('sort', 'id'); $order = request('order', 'DESC'); $search = $_GET['search']; $showDeleted = $request->show_deleted; $sql = $this->subject->builder()->with('medium') ->where(function ($query) use ($search) { $query->when($search, function ($q) use ($search) { $q->where('id', 'LIKE', "%$search%") ->orwhere('name', 'LIKE', "%$search%") ->orwhere('code', 'LIKE', "%$search%") ->orwhere('type', 'LIKE', "%$search%")->Owner(); }); }) ->when(!empty($showDeleted), function ($q) { $q->onlyTrashed()->Owner(); }); if (!empty($_GET['medium_id'])) { $sql = $sql->where('medium_id', $_GET['medium_id']); } $total = $sql->count(); if ($offset >= $total && $total > 0) { $lastPage = floor(($total - 1) / $limit) * $limit; // calculate last page offset $offset = $lastPage; } $sql = $sql->orderBy($sort, $order)->skip($offset)->take($limit); $res = $sql->get(); $bulkData = array(); $bulkData['total'] = $total; $rows = array(); $no = 1; foreach ($res as $row) { if ($request->show_deleted) { //Show Restore and Hard Delete Buttons $operate = BootstrapTableService::restoreButton(route('subjects.restore', $row->id)); $operate .= BootstrapTableService::trashButton(route('subjects.trash', $row->id)); } else { //Show Edit and Soft Delete Buttons $operate = BootstrapTableService::editButton(route('subjects.update', $row->id)); $operate .= BootstrapTableService::deleteButton(route('subjects.destroy', $row->id)); } $tempRow = $row->toArray(); $tempRow['no'] = $no++; $tempRow['type'] = trans($row->type); $tempRow['eng_type'] = $row->type; $tempRow['created_at'] = $row->created_at; $tempRow['updated_at'] = $row->updated_at; $tempRow['operate'] = $operate; $rows[] = $tempRow; } $bulkData['rows'] = $rows; return response()->json($bulkData); } public function store(Request $request) { ResponseService::noPermissionThenSendJson('subject-create'); $file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit'); $supportedImageTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg+xml']; $fileMimeType = $request->file('image')->getMimeType(); if (!in_array($fileMimeType, $supportedImageTypes)) { ResponseService::errorResponse('The image must be a file of type: jpg, jpeg, png, svg.'); } $validator = Validator::make($request->all(), [ 'medium_id' => 'required|numeric', 'type' => 'required|in:Practical,Theory', 'name' => [ 'required', new uniqueForSchool('subjects', ['name' => $request->name, 'medium_id' => $request->medium_id, 'type' => $request->type]) ], 'bg_color' => 'required|not_in:transparent', // 'code' => 'nullable|unique:subjects,code', 'code' => [ 'nullable', new uniqueForSchool('subjects', ['code' => $request->code, 'medium_id' => $request->medium_id, 'type' => $request->type]) ], 'image' => ['required', 'mimetypes:image/jpeg,image/png,image/svg+xml', new MaxFileSize($file_upload_size_limit)], ])->setAttributeNames(['bg_color' => 'Background Color']); if ($validator->fails()) { ResponseService::errorResponse($validator->errors()->first()); } try { $this->subject->create($request->all()); ResponseService::successResponse('Data Stored Successfully'); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function update(Request $request, $id) { ResponseService::noPermissionThenSendJson('subject-edit'); $file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit'); if ($request->hasFile('image')) { $supportedImageTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg+xml']; $fileMimeType = $request->file('image')->getMimeType(); if (!in_array($fileMimeType, $supportedImageTypes)) { ResponseService::errorResponse('The image must be a file of type: jpg, jpeg, png, svg.'); } } $validator = Validator::make($request->all(), [ 'medium_id' => 'required|numeric', 'name' => [ 'required', new uniqueForSchool('subjects', ['name' => $request->name, 'medium_id' => $request->medium_id, 'type' => $request->type], $id) ], 'code' => [ 'nullable', new uniqueForSchool('subjects', ['code' => $request->code, 'medium_id' => $request->medium_id, 'type' => $request->type], $id) ], 'type' => 'required|in:Practical,Theory', 'bg_color' => 'required|not_in:transparent', 'image' => ['nullable', 'mimetypes:image/jpeg,image/png,image/svg+xml', new MaxFileSize($file_upload_size_limit)], ])->setAttributeNames(['bg_color' => 'Background Color']); if ($validator->fails()) { ResponseService::errorResponse($validator->errors()->first()); } try { $this->subject->update($id, $request->all()); ResponseService::successResponse('Data Updated Successfully'); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function destroy($id) { ResponseService::noPermissionThenSendJson('subject-delete'); try { // Safety Check: Prevent deletion if subject is assigned to any class in any session if (ClassSubject::where('subject_id', $id)->exists()) { return ResponseService::errorResponse("Cannot delete this subject because it is assigned to classes in one or more academic sessions. Please remove it from all classes first."); } $this->subject->deleteById($id); ResponseService::successResponse('Data Deleted Successfully'); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function restore(int $id) { ResponseService::noPermissionThenSendJson('subject-delete'); try { $this->subject->findOnlyTrashedById($id)->restore(); ResponseService::successResponse("Data Restored Successfully"); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function trash($id) { ResponseService::noPermissionThenSendJson('subject-delete'); try { // Safety Check: Prevent permanent deletion if subject is assigned to any class in any session if (ClassSubject::where('subject_id', $id)->exists()) { return ResponseService::errorResponse("Cannot delete this subject permanently because it is assigned to classes in one or more academic sessions. Please remove it from all classes first."); } $this->subject->findOnlyTrashedById($id)->forceDelete(); ResponseService::successResponse("Data Deleted Permanently"); } catch (Throwable $e) { ResponseService::logErrorResponse($e, "Subject Controller -> Trash Method", 'cannot_delete_because_data_is_associated_with_other_data'); ResponseService::errorResponse(); } } }