File "TimetableController.php"
Full Path: /home/trinadezambia/public_html/admin_panel/app/Http/Controllers/TimetableController.php
File size: 26.09 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace App\Http\Controllers;
use App\Models\Timetable;
use App\Repositories\ClassSchool\ClassSchoolInterface;
use App\Repositories\ClassSection\ClassSectionInterface;
use App\Repositories\Medium\MediumInterface;
use App\Repositories\SchoolSetting\SchoolSettingInterface;
use App\Repositories\Subject\SubjectInterface;
use App\Repositories\SubjectTeacher\SubjectTeacherInterface;
use App\Repositories\Timetable\TimetableInterface;
use App\Repositories\User\UserInterface;
use App\Services\BootstrapTableService;
use App\Services\CachingService;
use App\Services\ResponseService;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Throwable;
class TimetableController extends Controller
{
private SubjectTeacherInterface $subjectTeacher;
private SubjectInterface $subject;
private TimetableInterface $timetable;
private ClassSectionInterface $classSection;
private UserInterface $user;
private SchoolSettingInterface $schoolSettings;
private CachingService $cache;
private ClassSchoolInterface $class;
private MediumInterface $medium;
public function __construct(SubjectTeacherInterface $subjectTeacher, SubjectInterface $subject, TimetableInterface $timetable, ClassSectionInterface $classSection, UserInterface $user, SchoolSettingInterface $schoolSettings, CachingService $cache, ClassSchoolInterface $class, MediumInterface $medium)
{
$this->subjectTeacher = $subjectTeacher;
$this->subject = $subject;
$this->timetable = $timetable;
$this->classSection = $classSection;
$this->user = $user;
$this->schoolSettings = $schoolSettings;
$this->cache = $cache;
$this->class = $class;
$this->medium = $medium;
}
public function index()
{
ResponseService::noFeatureThenRedirect('Timetable Management');
ResponseService::noPermissionThenRedirect('timetable-list');
// Get Timetable Settings Data
$timetableData = $this->schoolSettings->getBulkData([
'timetable_start_time',
'timetable_end_time',
'timetable_duration'
]);
// Convert Timetable Duration time to number
$timetableData['timetable_duration'] = Carbon::parse($timetableData['timetable_duration'] ?? "00:00:00")->diffInMinutes(Carbon::parse('00:00:00'));
$classes = $this->class->builder()->with('stream', 'shift')->get()->pluck('full_name', 'id');
$mediums = $this->medium->builder()->pluck('name', 'id');
return view('timetable.index', compact('timetableData', 'classes', 'mediums'));
}
public function store(Request $request)
{
ResponseService::noFeatureThenRedirect('Timetable Management');
ResponseService::noPermissionThenRedirect(['timetable-create']);
$request->validate([
'subject_teacher_id' => 'nullable|numeric',
'class_section_id' => 'required|numeric',
'subject_id' => 'nullable|numeric',
'start_time' => 'required',
'end_time' => 'required',
'day' => 'required',
'note' => 'nullable',
]);
try {
$sessionYearId = $this->cache->getSessionYear()->id;
$classSection = $this->classSection->findById($request->class_section_id, ['*'], ['class.shift']);
if ($classSection->class->shift) {
$timetable_start_time = Carbon::parse($classSection->class->shift->getRawOriginal('start_time'))->format('H:i:s');
$timetable_end_time = Carbon::parse($classSection->class->shift->getRawOriginal('end_time'))->format('H:i:s');
} else {
$schoolSettings = $this->cache->getSchoolSettings();
$timetable_start_time = Carbon::parse($schoolSettings['timetable_start_time'])->format('H:i:s');
$timetable_end_time = Carbon::parse($schoolSettings['timetable_end_time'])->format('H:i:s');
}
$start_time = Carbon::parse($request->start_time)->format('H:i:s');
$end_time = Carbon::parse($request->end_time)->format('H:i:s');
if ($start_time < $timetable_start_time || $start_time >= $timetable_end_time) {
ResponseService::errorResponse(__('Please select a valid time within school hours'));
}
if ($end_time > $timetable_end_time) {
$end_time = $timetable_end_time;
$request->merge(['end_time' => $end_time]);
}
if ($request->note == null && ($request->subject_teacher_id == null || $request->subject_teacher_id == '')) {
ResponseService::errorResponse(__('please_assign_a_teacher_to_the_subject_before_scheduling'));
}
// Check for teacher conflicts
if ($request->subject_teacher_id) {
// First get the teacher_id from the subject_teacher record
$subjectTeacher = $this->subjectTeacher->findById($request->subject_teacher_id);
if ($subjectTeacher) {
$teacher_id = $subjectTeacher->teacher_id;
// Now check for conflicts using the teacher_id
$conflictingTimetable = $this->timetable->builder()
->where('session_year_id', $sessionYearId)
->where('day', $request->day)
->whereHas('subject_teacher', function ($q) use ($teacher_id) {
$q->where('teacher_id', $teacher_id);
})
->where(function ($query) use ($start_time, $end_time) {
$query->where('start_time', '<', $end_time)
->where('end_time', '>', $start_time);
})
->first();
if ($conflictingTimetable) {
ResponseService::errorResponse(__('teacher_is_already_scheduled_for_another_class_at_this_time'));
}
}
}
$timetable = $this->timetable->create([
...$request->all(),
'type' => (!empty($request->subject_id)) ? "Lecture" : "Break",
'session_year_id' => $sessionYearId
]);
ResponseService::successResponse('Data Stored Successfully', $timetable);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function edit($classSectionID)
{
ResponseService::noFeatureThenRedirect('Timetable Management');
ResponseService::noPermissionThenRedirect('timetable-edit');
$currentSemester = $this->cache->getDefaultSemesterData();
$sessionYearId = $this->cache->getSessionYear()->id;
$classSection = $this->classSection->findById($classSectionID, ['*'], ['class', 'class.stream', 'class.shift', 'section', 'medium']);
$subjectTeachers = $this->subjectTeacher->builder()
->where('session_year_id', $sessionYearId)
->with([
'subject:id,name,type,bg_color',
'teacher:id,first_name,last_name',
'class_subject'
])
->whereHas('class_section', function ($q) use ($classSectionID) {
$q->where('id', $classSectionID);
})
->whereHas('class_subject', function ($q) {
$q->whereNull('deleted_at');
})
->orderBy('subject_id', 'ASC')
->CurrentSemesterData()
->get();
$subjectWithoutTeacherAssigned = $this->subject->builder()
->with([
'class_subjects' => function ($query) use ($classSection, $sessionYearId) {
$query->where('class_id', $classSection->class_id)
->where('session_year_id', $sessionYearId)
->CurrentSemesterData();
}
])
->whereHas('class_subjects', function ($q) use ($classSection, $sessionYearId) {
$q->where('class_id', $classSection->class_id)
->where('session_year_id', $sessionYearId)
->CurrentSemesterData();
})
->select(['id', 'name', 'type', 'bg_color'])
->whereNotIn('id', $subjectTeachers->pluck('subject_id'))
->get();
$timetables = $this->timetable->builder()
->where('class_section_id', $classSectionID)
->where('session_year_id', $sessionYearId)
->with([
'teacher:users.id,first_name,last_name',
'subject:id,name,type,bg_color',
'subject.class_subjects',
'subject_teacher.class_subject'
])
->CurrentSemesterData()
->get();
// Get Timetable Settings Data
$timetableSettingsData = $this->schoolSettings->getBulkData([
'timetable_start_time',
'timetable_end_time',
'timetable_duration'
]);
if ($classSection->class->shift) {
$timetableSettingsData['timetable_start_time'] = $classSection->class->shift->getRawOriginal('start_time');
$timetableSettingsData['timetable_end_time'] = $classSection->class->shift->getRawOriginal('end_time');
}
return view('timetable.edit', compact('subjectTeachers', 'subjectWithoutTeacherAssigned', 'classSection', 'timetables', 'timetableSettingsData', 'currentSemester'));
}
public function update(Request $request, $id)
{
ResponseService::noFeatureThenRedirect('Timetable Management');
ResponseService::noPermissionThenRedirect(['timetable-edit']);
$request->validate([
'start_time' => 'required',
'end_time' => 'required',
'day' => 'required',
]);
$start_time = $request->start_time;
$end_time = $request->end_time;
$sessionYearId = $this->cache->getSessionYear()->id;
// check if teacher is already scheduled for another class at this time
$timetable = $this->timetable->findById($id);
$day = $request->day;
$start_time = Carbon::parse($request->start_time)->format('H:i:s');
$end_time = Carbon::parse($request->end_time)->format('H:i:s');
if ($timetable->subject_teacher_id) {
// First get the teacher_id from the subject_teacher record
$subjectTeacher = $this->subjectTeacher->findById($timetable->subject_teacher_id);
if ($subjectTeacher) {
$teacher_id = $subjectTeacher->teacher_id;
// Now check for conflicts using the teacher_id
$conflictingTimetable = $this->timetable->builder()
->where('session_year_id', $sessionYearId)
->where('id', '!=', $id) // Exclude current record
->where('day', $request->day)
->whereHas('subject_teacher', function ($q) use ($teacher_id) {
$q->where('teacher_id', $teacher_id);
})
->where(function ($query) use ($request) {
$query->where('start_time', '<', $request->end_time)
->where('end_time', '>', $request->start_time);
})
->first();
if ($conflictingTimetable) {
ResponseService::errorResponse(__('teacher_is_already_scheduled_for_another_class_at_this_time'));
}
}
}
$start_time = Carbon::parse($request->start_time)->format('H:i:s');
$end_time = Carbon::parse($request->end_time)->format('H:i:s');
$classSection = $this->classSection->findById($timetable->class_section_id, ['*'], ['class.shift']);
if ($classSection->class->shift) {
$timetable_start_time = Carbon::parse($classSection->class->shift->getRawOriginal('start_time'))->format('H:i:s');
$timetable_end_time = Carbon::parse($classSection->class->shift->getRawOriginal('end_time'))->format('H:i:s');
} else {
$schoolSettings = $this->cache->getSchoolSettings();
$timetable_start_time = Carbon::parse($schoolSettings['timetable_start_time'])->format('H:i:s');
$timetable_end_time = Carbon::parse($schoolSettings['timetable_end_time'])->format('H:i:s');
}
try {
if ($timetable_start_time <= $start_time && $timetable_end_time >= $end_time) {
$this->timetable->updateOrCreate(['id' => $id,], $request->all());
ResponseService::successResponse('Data Stored Successfully');
} else if ($timetable_start_time <= $start_time && $start_time < $timetable_end_time) {
// If start_time is valid but end_time is beyond school hours, clip it
$end_time = $timetable_end_time;
$request->merge(['end_time' => $end_time]);
$this->timetable->updateOrCreate(['id' => $id,], $request->all());
ResponseService::successResponse('Data Stored Successfully');
} else {
ResponseService::errorResponse(__('Please select a valid time within school hours'));
}
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function show(Request $request)
{
ResponseService::noFeatureThenRedirect('Timetable Management');
ResponseService::noPermissionThenRedirect('timetable-list');
$offset = request('offset', 0);
$limit = request('limit', 10);
$sort = request('sort', 'id');
$order = request('order', 'DESC');
$sessionYearId = $this->cache->getSessionYear()->id;
$schoolSettings = $this->cache->getSchoolSettings([
'timetable_start_time',
'timetable_end_time',
'timetable_duration'
]);
$sql = $this->classSection->builder()->with([
'class:id,name,stream_id,shift_id',
'class.stream',
'class.shift',
'section:id,name',
'medium:id,name',
'timetable' => function ($query) use ($sessionYearId) {
$query->where('session_year_id', $sessionYearId)->CurrentSemesterData()->with('subject:id,name,type');
}
]);
if (!empty($request->search)) {
$search = $request->search;
$sql->where(function ($query) use ($search) {
$query->orWhereHas('section', function ($q) use ($search) {
$q->where('name', 'LIKE', "%$search%");
})->orWhereHas('medium', function ($q) use ($search) {
$q->where('name', 'LIKE', "%$search%");
})->orWhereHas('class', function ($q) use ($search) {
$q->where('name', 'LIKE', "%$search%");
})->orWhereHas('class.stream', function ($q) use ($search) {
$q->where('name', 'LIKE', "%$search%");
})->orWhereHas('class.shift', function ($q) use ($search) {
$q->where('name', 'LIKE', "%$search%");
});
});
}
if (!empty($request->medium_id)) {
$sql = $sql->where('medium_id', $request->medium_id);
}
if (!empty($request->class_id)) {
$sql = $sql->where('class_id', $request->class_id);
}
if (!empty($request->section_id)) {
$sql = $sql->where('section_id', $request->section_id);
}
if (!empty($request->medium_id)) {
$sql = $sql->where('medium_id', $request->medium_id);
}
if (!empty($request->teacher_id)) {
$sql = $sql->whereHas('class_teachers', function ($q) use ($request) {
$q->where('teacher_id', $request->teacher_id);
});
}
if (!empty($request->subject_id)) {
$sql = $sql->whereHas('subject_teachers', function ($q) use ($request) {
$q->where('subject_id', $request->subject_id);
});
}
if (!empty($request->class_subject_id)) {
$sql = $sql->whereHas('subject_teachers.class_subject', function ($q) use ($request) {
$q->where('id', $request->class_subject_id);
});
}
if (!empty($showDeleted)) {
$sql = $sql->onlyTrashed();
}
$total = $sql->count();
if ($offset >= $total && $total > 0) {
$lastPage = floor(($total - 1) / $limit) * $limit; // calculate last page offset
$offset = $lastPage;
}
$sql->orderBy($sort, $order)->skip($offset)->take($limit);
$res = $sql->get();
$bulkData = array();
$bulkData['total'] = $total;
$rows = array();
$no = 1;
foreach ($res as $row) {
$operate = BootstrapTableService::editButton(route('timetable.edit', $row->id), false);
$operate .= BootstrapTableService::button('fa fa-trash', '#', ['delete-class-timetable', 'btn-gradient-danger'], ['title' => trans("Delete Class Timetable"), 'data-id' => $row->id]);
$tempRow = $row->toArray();
$timetable = $row->timetable->groupBy('day')->sortBy('start_time');
$tempRow['no'] = $no++;
$tempRow['Monday'] = $timetable['Monday'] ?? [];
$tempRow['Tuesday'] = $timetable['Tuesday'] ?? [];
$tempRow['Wednesday'] = $timetable['Wednesday'] ?? [];
$tempRow['Thursday'] = $timetable['Thursday'] ?? [];
$tempRow['Friday'] = $timetable['Friday'] ?? [];
$tempRow['Saturday'] = $timetable['Saturday'] ?? [];
$tempRow['Sunday'] = $timetable['Sunday'] ?? [];
$tempRow['operate'] = $operate;
$rows[] = $tempRow;
}
$bulkData['rows'] = $rows;
return response()->json($bulkData);
}
public function destroy($id)
{
ResponseService::noFeatureThenRedirect('Timetable Management');
ResponseService::noPermissionThenSendJson('timetable-delete');
try {
$sessionYearId = $this->cache->getSessionYear()->id;
Timetable::where('id', $id)->where('session_year_id', $sessionYearId)->delete();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function teacherIndex()
{
ResponseService::noFeatureThenRedirect('Timetable Management');
ResponseService::noPermissionThenRedirect('timetable-list');
// Get Timetable Settings Data
$timetableSettingsData = $this->schoolSettings->getBulkData([
'timetable_start_time',
'timetable_end_time',
'timetable_duration'
]);
return view('timetable.teacher.index', compact('timetableSettingsData'));
}
public function teacherList(Request $request)
{
ResponseService::noFeatureThenRedirect('Timetable Management');
ResponseService::noPermissionThenRedirect('timetable-list');
$offset = request('offset', 0);
$limit = request('limit', 10);
$sort = request('sort', 'id');
$order = request('order', 'DESC');
$sessionYearId = $this->cache->getSessionYear()->id;
$sql = $this->user->builder()->role('Teacher')->with([
'timetable' => function ($query) use ($sessionYearId) {
$query->where('timetables.session_year_id', $sessionYearId)->CurrentSemesterData()->with('subject:id,name,type', 'class_section.class', 'class_section.class.shift', 'class_section.class.stream', 'class_section.medium', 'class_section.section');
}
]);
if (!empty($request->search)) {
$search = $request->search;
$sql->where(function ($query) use ($search) {
$query->where('id', 'LIKE', "%$search%")->orwhereRaw("concat(first_name,' ',last_name) LIKE '%" . $search . "%'");
});
}
if (!empty($request->class_id)) {
$sql->whereHas('timetable.class_section.class', function ($q) use ($request) {
$q->where('id', $request->class_id);
});
}
if (!empty($request->section_id)) {
$sql->whereHas('timetable.class_section.section', function ($q) use ($request) {
$q->where('id', $request->section_id);
});
}
if (!empty($request->subject_id)) {
$sql->whereHas('timetable.subject', function ($q) use ($request) {
$q->where('id', $request->subject_id);
});
}
if (!empty($request->teacher_id)) {
$sql->where('id', $request->teacher_id);
}
if (!empty($request->status)) {
$sql->where('status', $request->status);
}
if (!empty($request->role)) {
$sql->where('role', $request->role);
}
if (!empty($request->created_at)) {
$sql->whereDate('created_at', '=', $request->created_at);
}
$total = $sql->count();
if ($offset >= $total && $total > 0) {
$lastPage = floor(($total - 1) / $limit) * $limit; // calculate last page offset
$offset = $lastPage;
}
$sql->orderBy($sort, $order)->skip($offset)->take($limit);
$res = $sql->get();
$bulkData = array();
$bulkData['total'] = $total;
$rows = array();
$no = 1;
foreach ($res as $row) {
$operate = BootstrapTableService::button('fa fa-eye', route('timetable.teacher.show', $row->id), ['btn-gradient-success'], ['title' => "View Timetable"]);
$tempRow = $row->toArray();
$timetable = $row->timetable->groupBy('day')->sortBy('start_time');
$tempRow['no'] = $no++;
$tempRow['Monday'] = $timetable['Monday'] ?? [];
$tempRow['Tuesday'] = $timetable['Tuesday'] ?? [];
$tempRow['Wednesday'] = $timetable['Wednesday'] ?? [];
$tempRow['Thursday'] = $timetable['Thursday'] ?? [];
$tempRow['Friday'] = $timetable['Friday'] ?? [];
$tempRow['Saturday'] = $timetable['Saturday'] ?? [];
$tempRow['Sunday'] = $timetable['Sunday'] ?? [];
$tempRow['operate'] = $operate;
$rows[] = $tempRow;
}
$bulkData['rows'] = $rows;
return response()->json($bulkData);
}
public function teacherShow($teacherID)
{
ResponseService::noFeatureThenRedirect('Timetable Management');
$sessionYearId = $this->cache->getSessionYear()->id;
$teacher = $this->user->findById($teacherID, ['id', 'first_name', 'last_name']);
$timetables = $this->timetable->builder()->whereHas('subject_teacher', function ($q) use ($teacherID) {
$q->where('teacher_id', $teacherID);
})->where('session_year_id', $sessionYearId)->with('subject:id,name,type,bg_color', 'class_section.class', 'class_section.section', 'class_section.medium', 'class_section.class.stream', 'class_section.class.shift')->get();
// Get Timetable Settings Data
$timetableSettingsData = $this->schoolSettings->getBulkData([
'timetable_start_time',
'timetable_end_time',
'timetable_duration'
]);
return view('timetable.teacher.view', compact('timetables', 'teacher', 'timetableSettingsData'));
}
public function updateTimetableSettings(Request $request)
{
ResponseService::noFeatureThenRedirect('Timetable Management');
ResponseService::noPermissionThenRedirect('timetable-list');
try {
DB::beginTransaction();
$settings = array(
'timetable_start_time',
'timetable_end_time',
'timetable_duration'
);
// $timeTableExistsBeforeStartTime = $this->timetable->builder()->where('start_time', '<', date('H:i:s', strtotime($request->time_table_start_time)))->get();
// if (!empty($timeTableExistsBeforeStartTime->toArray())) {
// ResponseService::errorResponse("Updates are prohibited as there are pre-existing lectures scheduled before " . $request->time_table_start_time);
// }
// $timeTableExistsAfterEndTime = $this->timetable->builder()->where('end_time', '>', date('H:i:s', strtotime($request->time_table_end_time)))->get();
// if (!empty($timeTableExistsAfterEndTime->toArray())) {
// ResponseService::errorResponse("Updates are prohibited as there are pre-existing lectures scheduled after " . $request->time_table_end_time);
// }
$data = array();
foreach ($settings as $row) {
$data[] = [
"name" => $row,
"data" => $row == 'timetable_duration' ? Carbon::createFromTimestampUTC($request->$row * 60)->format('H:i:s') : date("H:i:s", strtotime($request->$row)),
"type" => 'time'
];
}
$this->schoolSettings->upsert($data, ["name"], ["data", "type"]);
$this->cache->removeSchoolCache(config('constants.CACHE.SCHOOL.SETTINGS'));
DB::commit();
ResponseService::successResponse('Data Updated Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e, "Timetable Controller -> updateTimetableSettings");
ResponseService::errorResponse();
}
}
public function deleteClassTimetable($id)
{
ResponseService::noFeatureThenRedirect('Timetable Management');
ResponseService::noPermissionThenSendJson('timetable-delete');
try {
$sessionYearId = $this->cache->getSessionYear()->id;
$this->timetable->builder()->where('class_section_id', $id)->where('session_year_id', $sessionYearId)->delete();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
}