File "ExamTimetableController.php"
Full Path: /home/trinadezambia/public_html/admin_panel/app/Http/Controllers/Exam/ExamTimetableController.php
File size: 10.09 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace App\Http\Controllers\Exam;
use Throwable;
use Illuminate\Http\Request;
use App\Services\CachingService;
use App\Services\ResponseService;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use App\Repositories\Exam\ExamInterface;
use Illuminate\Support\Facades\Validator;
use App\Repositories\ExamTimetable\ExamTimetableInterface;
use App\Repositories\ClassSection\ClassSectionInterface;
use App\Repositories\ClassTeachers\ClassTeachersInterface;
use App\Repositories\Student\StudentInterface;
use App\Models\ClassSubject;
use App\Models\Exam;
use Carbon\Carbon;
class ExamTimetableController extends Controller
{
private ExamInterface $exam;
private ExamTimetableInterface $examTimetable;
private CachingService $cache;
private ClassSectionInterface $classSection;
private ClassTeachersInterface $classTeachers;
private StudentInterface $student;
public function __construct(ExamInterface $exam, ExamTimetableInterface $examTimetable, CachingService $cache, ClassSectionInterface $classSection, ClassTeachersInterface $classTeachers, StudentInterface $student)
{
$this->exam = $exam;
$this->examTimetable = $examTimetable;
$this->cache = $cache;
$this->classSection = $classSection;
$this->classTeachers = $classTeachers;
$this->student = $student;
}
public function edit(int $examId)
{
ResponseService::noFeatureThenRedirect('Exam Management');
ResponseService::noPermissionThenRedirect('exam-timetable-list');
$currentSessionYear = $this->cache->getSessionYear();
$semester = $this->cache->getSemester();
// 1. Unified Query with specific column selection for performance
$exam = Exam::owner()
->with([
'class.medium:id,name',
'class.stream:id,name',
'class.shift:id,name',
'class.all_subjects' => function ($query) use ($semester, $currentSessionYear) {
$query->wherePivot('session_year_id', $currentSessionYear->id)
->wherePivotNull('deleted_at')
->when($semester, function ($q) use ($semester) {
$q->where(function ($sq) use ($semester) {
$sq->where('semester_id', $semester->id)->orWhereNull('semester_id');
});
}, function ($q) {
$q->whereNull('semester_id');
});
},
'timetable'
])
->find($examId);
// 2. Simple existence check
if (!$exam) {
return redirect('exams');
}
// 3. Clean variable preparation
$last_result_submission_date = Carbon::parse($exam->getRawOriginal('last_result_submission_date'))->format('d-m-Y');
$disabled = $exam->publish ? 'disabled' : '';
$schoolSettings = $this->cache->getSchoolSettings();
return view('exams.timetable', compact(
'exam',
'currentSessionYear',
'disabled',
'last_result_submission_date',
'schoolSettings'
));
}
public function update(Request $request, $examID)
{
ResponseService::noFeatureThenRedirect('Exam Management');
ResponseService::noPermissionThenSendJson('exam-timetable-create');
$validator = Validator::make($request->all(), [
'timetable' => 'required|array',
'timetable.*.passing_marks' => 'required|lte:timetable.*.total_marks',
'timetable.*.end_time' => 'required|after:timetable.*.start_time',
'timetable.*.date' => 'required|date',
'last_result_submission_date' => 'required|date',
], [
'timetable.*.passing_marks.lte' => trans('passing_marks_should_less_than_or_equal_to_total_marks'),
'timetable.*.end_time.after' => trans('end_time_should_be_greater_than_start_time'),
'last_result_submission_date.after' => trans('the_exam_result_marks_submission_date_should_be_greater_than_last_exam_timetable_date'),
]);
$validator->after(function ($validator) use ($request) {
$timetable = $request->timetable;
$lastResultDate = $request->last_result_submission_date;
if (!empty($timetable) && $lastResultDate) {
try {
// Extract the latest date from the timetable using flexible parsing
$latestExamDate = collect($timetable)
->pluck('date')
->filter()
->map(fn($date) => Carbon::parse($date))
->max()
->format('Y-m-d');
$lastResultDate = Carbon::parse($lastResultDate)->format('Y-m-d');
if ($lastResultDate <= $latestExamDate) {
$validator->errors()->add(
'last_result_submission_date',
trans('the_exam_result_marks_submission_date_should_be_greater_than_last_exam_timetable_date')
);
}
} catch (\Exception $e) {
// If date parsing fails, skip this validation
// The main date validation will catch invalid dates
}
}
});
if ($validator->fails()) {
ResponseService::errorResponse($validator->errors()->first());
}
try {
$examRecord = $this->exam->findById($examID);
$sessionYearId = $examRecord->session_year_id;
$electiveSubjectsIds = [];
foreach ($request->timetable as $timetable) {
$examTimetable = array(
'exam_id' => $examID,
'class_subject_id' => $timetable['class_subject_id'],
'total_marks' => $timetable['total_marks'],
'passing_marks' => $timetable['passing_marks'],
'start_time' => $timetable['start_time'],
'end_time' => $timetable['end_time'],
'date' => date('Y-m-d', strtotime($timetable['date'])),
'session_year_id' => $sessionYearId,
);
$class_subject_ids[] = $timetable['class_subject_id'];
$electiveSubjects = ClassSubject::where('id', $timetable['class_subject_id'])->where('type', 'Elective')->pluck('id')->toArray();
$electiveSubjectsIds = array_merge($electiveSubjectsIds, $electiveSubjects);
$this->examTimetable->updateOrCreate(['id' => $timetable['id'] ?? null], $examTimetable);
}
// Get Start Date & End Date From Exam Timetable
$examTimetable = $this->examTimetable->builder()->where('exam_id', $examID);
$startDate = $examTimetable->min('date');
$endDate = $examTimetable->max('date');
$last_result_submission_date = date('Y-m-d', strtotime($request->last_result_submission_date));
// Update Start Date and End Date to the particular Exam
$exam = $this->exam->update($examID, ['start_date' => $startDate, 'end_date' => $endDate, 'last_result_submission_date' => $last_result_submission_date]);
// dd($exam);
DB::commit();
//Get class sections for notifications
$classSectionIds = $this->classSection->builder()
->where('class_id', $exam->class_id)
->pluck('id');
$classTeacherIds = $this->classTeachers->builder()
->whereIn('class_section_id', $classSectionIds)
->distinct()
->pluck('teacher_id')
->toArray();
$class_subject_ids = array_unique($class_subject_ids);
$electiveSubjectsIds = array_unique($electiveSubjectsIds);
$allSubjectsAreElective =
count($class_subject_ids) === count($electiveSubjectsIds) &&
empty(array_diff($class_subject_ids, $electiveSubjectsIds));
// Send notifications
$title = "Exams Timetable Scheduled";
$body = "Exam Timetable Scheduled Click here to see !!!";
$type = "exam";
$students = $this->student->builder()
->where('application_status', 1)
->whereIn('class_section_id', $classSectionIds)
->whereHas('user', function ($query) {
$query->where('status', 1)->withTrashed();
})
->where('session_year_id', $request->session_year_id);
if ($allSubjectsAreElective) {
$students = $students->whereHas('student_subjects', function ($q) use ($electiveSubjectsIds) {
$q->whereIn('class_subject_id', $electiveSubjectsIds);
});
}
$students = $students->get();
$guardian_ids = $students->pluck('guardian_id')->toArray();
$student_ids = $students->pluck('user_id')->toArray();
$users = array_unique(array_merge($student_ids, $guardian_ids, $classTeacherIds));
$customData = ['exam_id' => $examID, 'exam_name' => $exam->name, 'exam_type' => 'offline'];
send_notification($users, $title, $body, $type, $customData);
ResponseService::successResponse('Data Stored Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e, "Exam Timetable Controller -> Store method");
ResponseService::errorResponse();
}
}
public function destroy($id)
{
ResponseService::noFeatureThenRedirect('Exam Management');
ResponseService::noPermissionThenSendJson('exam-timetable-delete');
try {
$this->examTimetable->deleteById($id);
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
ResponseService::logErrorResponse($e, "Exam Controller -> DeleteTimetable method");
ResponseService::errorResponse();
}
}
}