File "LessonController.php"
Full Path: /home/trinadezambia/public_html/admin_panel/app/Http/Controllers/LessonController.php
File size: 24.66 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace App\Http\Controllers;
use App\Models\ClassSubject;
use App\Models\LessonCommon;
use App\Models\LessonTopic;
use App\Models\Syllabus;
use App\Repositories\ClassSection\ClassSectionInterface;
use App\Repositories\ClassSubject\ClassSubjectInterface;
use App\Repositories\Files\FilesInterface;
use App\Repositories\Lessons\LessonsInterface;
use App\Repositories\LessonsCommon\LessonsCommonInterface;
use App\Repositories\Semester\SemesterInterface;
use App\Repositories\Student\StudentInterface;
use App\Repositories\Subject\SubjectInterface;
use App\Repositories\SubjectTeacher\SubjectTeacherInterface;
use App\Repositories\StudentSubject\StudentSubjectInterface;
use App\Rules\DynamicMimes;
use App\Rules\MaxFileSize;
use App\Rules\uniqueLessonInClass;
use App\Rules\YouTubeUrl;
use App\Services\BootstrapTableService;
use App\Services\CachingService;
use App\Services\LessonNotificationService;
use App\Services\ResponseService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use App\Repositories\SessionYear\SessionYearInterface;
use Illuminate\Support\Str;
use Throwable;
class LessonController extends Controller
{
private SubjectTeacherInterface $subjectTeacher;
private ClassSectionInterface $classSection;
private LessonsInterface $lesson;
private FilesInterface $files;
private CachingService $cache;
private LessonsCommonInterface $lessonCommon;
private StudentInterface $student;
private SubjectInterface $subject;
private ClassSubjectInterface $class_subjects;
private SemesterInterface $semester;
private SessionYearInterface $sessionYear;
private StudentSubjectInterface $studentSubject;
private LessonNotificationService $lessonNotificationService;
public function __construct(ClassSectionInterface $classSection, LessonsInterface $lesson, FilesInterface $files, SubjectTeacherInterface $subjectTeacher, CachingService $cache, LessonsCommonInterface $lessonCommon, StudentInterface $student, SubjectInterface $subject, ClassSubjectInterface $class_subjects, SemesterInterface $semester, SessionYearInterface $sessionYear, StudentSubjectInterface $studentSubject, LessonNotificationService $lessonNotificationService)
{
$this->subjectTeacher = $subjectTeacher;
$this->classSection = $classSection;
$this->lesson = $lesson;
$this->files = $files;
$this->cache = $cache;
$this->lessonCommon = $lessonCommon;
$this->student = $student;
$this->subject = $subject;
$this->class_subjects = $class_subjects;
$this->semester = $semester;
$this->sessionYear = $sessionYear;
$this->studentSubject = $studentSubject;
$this->lessonNotificationService = $lessonNotificationService;
}
public function index()
{
ResponseService::noFeatureThenRedirect('Lesson Management');
ResponseService::noPermissionThenRedirect('lesson-list');
$class_section = $this->classSection->builder()->with('class.stream', 'class.shift', 'section', 'medium')->get();
$subjectTeachers = $this->subjectTeacher->builder()->with('subject:id,name,type')->get();
$lessons = $this->lesson->builder()->get();
$semesters = $this->semester->builder()->get();
return response(view('lessons.index', compact('class_section', 'subjectTeachers', 'lessons', 'semesters')));
}
public function store(Request $request)
{
ResponseService::noFeatureThenRedirect('Lesson Management');
ResponseService::noPermissionThenRedirect('lesson-create');
$file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit');
$validator = Validator::make($request->all(), [
'name' => 'required',
'description' => 'required',
'class_section_id' => 'required|array',
'class_section_id.*' => 'numeric',
'subject_id' => 'required|numeric',
'file_data' => 'nullable|array',
'file_data.*.type' => 'required|in:file_upload,youtube_link,video_upload,other_link',
'file_data.*.name' => 'required_with:file_data.*.type',
'file_data.*.thumbnail' => 'required_if:file_data.*.type,youtube_link,video_upload,other_link',
'file_data.*.link' => ['nullable', 'required_if:file_data.*.type,youtube_link,other_link', new YouTubeUrl],
'file_data.*.file' => [
'nullable',
'required_if:file_data.*.type,file_upload,video_upload',
new DynamicMimes(),
new MaxFileSize($file_upload_size_limit),
],
]);
if ($validator->fails()) {
return ResponseService::errorResponse($validator->errors()->first());
}
try {
DB::beginTransaction();
/* -------------------- Lesson -------------------- */
$lesson = $this->lesson->create([
'name' => $request->name,
'description' => $request->description,
]);
$sessionYear = $this->cache->getSessionYear();
$sectionIds = $request->class_section_id;
$lessonCommonData = [];
foreach ($sectionIds as $sectionId) {
$classSection = $this->classSection
->builder()
->where('id', $sectionId)
->first();
// $syllabus = Syllabus::where('class_id', $classSection->class->id)
// ->where('subject_id', $request->subject_id)
// ->where('status', 'active')
// ->first();
$classSubject = ClassSubject::where('class_id', $classSection->class_id)
->where('subject_id', $request->subject_id)
->where('session_year_id', $sessionYear->id)
->first();
if ($classSubject && $classSubject->syllabus_id) {
$lessonCommonData[] = [
'lesson_id' => $lesson->id,
'class_section_id' => $sectionId,
'syllabus_id' => $classSubject->syllabus_id,
];
} else {
ResponseService::errorResponse('Syllabus not found for class and subject');
}
}
LessonCommon::insert($lessonCommonData);
/* -------------------- Files -------------------- */
if (!empty($request->file_data)) {
$lessonFileData = [];
foreach ($request->file_data as $file) {
if (!empty($file['type'])) {
$lessonFileData[] = $this->prepareFileData($file);
}
}
if (!empty($lessonFileData)) {
$lessonFile = $this->files->model()->modal()->associate($lesson);
foreach ($lessonFileData as &$fileData) {
$fileData['modal_type'] = $lessonFile->modal_type;
$fileData['modal_id'] = $lessonFile->modal_id;
}
$this->files->createBulk($lessonFileData);
}
}
/* ================== PUSH NOTIFICATIONS ================== */
$this->lessonNotificationService->send($lesson, $sectionIds, (int)$request->subject_id, 'created');
DB::commit();
ResponseService::successResponse('Data Stored Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e, 'Lesson Controller -> Store');
ResponseService::errorResponse();
}
}
private function prepareFileData($file)
{
if ($file['type']) {
$tempFileData = [
'file_name' => $file['name']
];
// If File Upload
if ($file['type'] == "file_upload") {
// Add Type And File Url to TempDataArray and make Thumbnail data null
$tempFileData['type'] = 1;
$tempFileData['file_thumbnail'] = null;
$tempFileData['file_url'] = $file['file'];
} elseif ($file['type'] == "youtube_link") {
// Add Type , Thumbnail and Link to TempDataArray
$tempFileData['type'] = 2;
$tempFileData['file_thumbnail'] = $file['thumbnail'];
$tempFileData['file_url'] = $file['link'];
} elseif ($file['type'] == "video_upload") {
// Add Type , File Thumbnail and File URL to TempDataArray
$tempFileData['type'] = 3;
$tempFileData['file_thumbnail'] = $file['thumbnail'];
$tempFileData['file_url'] = $file['file'];
} elseif ($file['type'] == "other_link") {
// Add Type , File Thumbnail and File URL to TempDataArray
$tempFileData['type'] = 4;
$tempFileData['file_thumbnail'] = $file['thumbnail'];
$tempFileData['file_url'] = $file['link'];
}
}
return $tempFileData;
}
public function show()
{
ResponseService::noFeatureThenRedirect('Lesson Management');
ResponseService::noPermissionThenRedirect('lesson-list');
$offset = request('offset', 0);
$limit = request('limit', 10);
$sort = request('sort', 'id');
$order = request('order', 'DESC');
$search = request('search');
$semester_id = request('semester_id');
$sql = $this->lesson->builder()
->with([
'topic',
'file',
'lesson_commons' => function ($q) {
$q->whereHas('syllabus.class_subject', fn($q) => $q->whereNull('deleted_at'));
},
'lesson_commons.syllabus.class_subject.subject' => fn($q) => $q->whereNull('deleted_at'),
'lesson_commons.class_section.class.stream',
'lesson_commons.class_section.class.shift',
'lesson_commons.class_section.section'
])
->when($search, function ($query) use ($search) {
$query->where(function ($query) use ($search) {
$query->where('name', 'LIKE', "%$search%")
->orWhere('description', 'LIKE', "%$search%")
->orWhereHas(
'lesson_commons.class_section.section',
fn($q) =>
$q->where('name', 'LIKE', "%$search%")
)
->orWhereHas(
'lesson_commons.class_section.class',
fn($q) =>
$q->where('name', 'LIKE', "%$search%")
)
->orWhereHas(
'lesson_commons.syllabus.class_subject.subject',
fn($q) =>
$q->where('name', 'LIKE', "%$search%")
);
});
});
if (request('class_id')) {
$class_id = request('class_id');
$sql = $sql->whereHas('lesson_commons', function ($q) use ($class_id) {
$q->where('class_section_id', $class_id);
});
}
if (request('class_subject_id')) {
$subject_id = request('class_subject_id');
$sql = $sql->whereHas('lesson_commons.syllabus.class_subject', function ($q) use ($subject_id) {
$q->where('id', $subject_id);
});
}
$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 $key => $row) {
$row = (object) $row;
// lesson commons with class section details
$lessonCommons = $row->lesson_commons->map(function ($common) {
return $common->class_section ? $common->class_section->full_name : null;
});
// Get Subject Name
$subject = $row->lesson_commons->first()?->syllabus?->subject;
$subjectName = $subject ? $subject->name . ' - ' . $subject->type : null;
$lessonCommons->filter()->map(function ($name) {
return "{$name},";
})->toArray();
// dd( $lessonCommons);
// $operate = BootstrapTableService::button(route('lesson.edit', $row->id), ['btn-gradient-primary'], ['title' => 'Edit'], ['fa fa-edit']);
$operate = BootstrapTableService::button('fa fa-edit', route('lesson.edit', $row->id), ['btn-gradient-primary'], ['title' => 'Edit']);
$operate .= BootstrapTableService::deleteButton(route('lesson.destroy', $row->id));
$tempRow = $row->toArray();
$tempRow['no'] = $no++;
$tempRow['class_section_with_medium'] = $lessonCommons;
$tempRow['subject_with_name'] = $subjectName;
$tempRow['operate'] = $operate;
$rows[] = $tempRow;
}
$bulkData['rows'] = $rows;
return response()->json($bulkData);
}
public function edit($id)
{
ResponseService::noFeatureThenRedirect('Lesson Management');
ResponseService::noPermissionThenRedirect('lesson-edit');
$class_section = $this->classSection->builder()->with('class', 'class.stream', 'class.shift', 'section', 'medium')->get();
$subjectTeachers = $this->subjectTeacher->builder()->with('subject:id,name,type')->get();
$lesson = $this->lesson->builder()->with('file', 'lesson_commons.syllabus.class_subject')->where('id', $id)->first();
$lessonCommonClassSections = $lesson->lesson_commons->pluck('class_section_id');
$subjectId = $lesson->lesson_commons->first()->syllabus?->subject_id;
return response(view('lessons.edit_lesson', compact('class_section', 'lessonCommonClassSections', 'subjectTeachers', 'lesson', 'subjectId')));
}
public function update(Request $request, $id)
{
ResponseService::noFeatureThenRedirect('Lesson Management');
ResponseService::noPermissionThenSendJson('lesson-edit');
$file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit');
$validator = Validator::make(
$request->all(),
[
'name' => 'required',
'description' => 'required',
'class_section_id' => 'required|array',
'class_section_id.*' => 'numeric',
'class_subject_id' => 'required|numeric',
'file_data' => 'nullable|array',
'file_data.*.type' => 'required|in:file_upload,youtube_link,video_upload,other_link',
'file_data.*.name' => 'required_with:file_data.*.type',
'file_data.*.link' => ['nullable', 'required_if:file_data.*.type,youtube_link', new YouTubeUrl],
'file_data.*.link' => ['nullable', 'required_if:file_data.*.type,other_link', 'url'],
'file_data.*.file' => [
'nullable',
new DynamicMimes,
new MaxFileSize($file_upload_size_limit)
],
]
);
if ($validator->fails()) {
return ResponseService::errorResponse($validator->errors()->first());
}
$classSectionIds = $request->class_section_id;
try {
DB::beginTransaction();
/* -------------------- Update lesson -------------------- */
$lesson = $this->lesson->update($id, [
'name' => $request->name,
'description' => $request->description,
]);
$sessionYear = $this->cache->getSessionYear();
/* -------------------- Rebuild lesson_common -------------------- */
LessonCommon::where('lesson_id', $id)->delete();
$lessonCommonData = [];
foreach ($classSectionIds as $sectionId) {
$classSection = $this->classSection
->builder()
->where('id', $sectionId)
->with('class')
->first();
$classSubject = ClassSubject::where('class_id', $classSection->class_id)
->where('subject_id', $request->subject_id)
->where('session_year_id', $sessionYear->id)
->first();
if (!$classSubject->syllabus_id) {
ResponseService::errorResponse('Syllabus not found for class ' . $classSection->class->name . ' and subject ' . $request->subject_id);
}
$lessonCommonData[] = [
'lesson_id' => $lesson->id,
'class_section_id' => $sectionId,
'syllabus_id' => $classSubject->syllabus_id,
];
// $classSubject = $this->class_subjects
// ->builder()
// ->where('class_id', $classSection->class->id)
// ->where('subject_id', $request->class_subject_id)
// ->first();
// $lessonCommonData[] = [
// 'lesson_id' => $id,
// 'class_section_id' => $sectionId,
// 'class_subject_id' => $classSubject->id,
// ];
}
LessonCommon::insert($lessonCommonData);
/* -------------------- Files -------------------- */
if (!empty($request->file_data)) {
foreach ($request->file_data as $key => $file) {
if (empty($file['type'])) {
continue;
}
$lessonFile = $this->files->model();
$lessonModelAssociate = $lessonFile->modal()->associate($lesson);
// Base data (SAFE — no file fields here)
$tempFileData = [
'id' => $file['id'] ?? null,
'modal_type' => $lessonModelAssociate->modal_type,
'modal_id' => $lessonModelAssociate->modal_id,
'file_name' => $file['name'],
'updated_at' => now(),
];
// Only set created_at for new records
if (empty($file['id'])) {
$tempFileData['created_at'] = now();
}
switch ($file['type']) {
case 'file_upload':
$tempFileData['type'] = 1;
// ✅ only update if a NEW file is uploaded
if ($request->hasFile("file_data.$key.file")) {
$tempFileData['file_url'] = $file['file'];
}
break;
case 'youtube_link':
$tempFileData['type'] = 2;
// ✅ link change only if provided
if (!empty($file['link'])) {
$tempFileData['file_url'] = $file['link'];
}
if ($request->hasFile("file_data.$key.thumbnail")) {
$tempFileData['file_thumbnail'] = $file['thumbnail'];
}
break;
case 'video_upload':
$tempFileData['type'] = 3;
if ($request->hasFile("file_data.$key.file")) {
$tempFileData['file_url'] = $file['file'];
}
if ($request->hasFile("file_data.$key.thumbnail")) {
$tempFileData['file_thumbnail'] = $file['thumbnail'];
}
break;
case 'other_link':
$tempFileData['type'] = 4;
if (!empty($file['link'])) {
$tempFileData['file_url'] = $file['link'];
}
if ($request->hasFile("file_data.$key.thumbnail")) {
$tempFileData['file_thumbnail'] = $file['thumbnail'];
}
break;
}
// ✅ SAFE now — no null overwrites
$this->files->updateOrCreate(
['id' => $file['id'] ?? null],
$tempFileData
);
}
}
/* ================== PUSH NOTIFICATIONS ================== */
$this->lessonNotificationService->send($lesson, $classSectionIds, (int)$request->class_subject_id, 'updated');
DB::commit();
return ResponseService::successResponse('Data Updated Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e, 'Lesson Controller -> update');
return ResponseService::errorResponse();
}
}
public function destroy($id)
{
ResponseService::noFeatureThenRedirect('Lesson Management');
ResponseService::noPermissionThenSendJson('lesson-delete');
try {
$lesson_topics = LessonTopic::where('lesson_id', $id)->count();
if ($lesson_topics) {
$response = array('error' => true, 'message' => trans('cannot_delete_because_data_is_associated_with_other_data'));
} else {
// Find the Data By ID
$lesson = $this->lesson->findById($id);
// If File exists
if ($lesson->file) {
// Loop on the Files
foreach ($lesson->file as $file) {
// Remove the Files From the Local
if (Storage::disk('public')->exists($file->file_url)) {
Storage::disk('public')->delete($file->file_url);
}
}
}
// Delete File Data
$lesson->file()->delete();
// Delete Lesson Data
$lesson->delete();
ResponseService::successResponse('Data Deleted Successfully');
}
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e, "Lesson Controller -> Destroy method");
ResponseService::errorResponse();
}
return response()->json($response);
}
public function search(Request $request)
{
ResponseService::noFeatureThenRedirect('Lesson Management');
ResponseService::noPermissionThenRedirect('lesson-list');
try {
// Get the new Instance of Lesson Model
$lesson = $this->lesson->model();
if (isset($request->subject_id)) {
$lesson = $lesson->where('subject_id', $request->subject_id);
}
if (isset($request->class_section_id)) {
$lesson = $lesson->where('class_section_id', $request->class_section_id);
}
$lesson = $lesson->get();
$response = array(
'error' => false,
'data' => $lesson,
'message' => 'Lesson fetched successfully'
);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e, "Lesson Controller -> Search Method");
ResponseService::errorResponse();
}
return response()->json($response);
}
public function deleteFile($id)
{
ResponseService::noFeatureThenRedirect('Lesson Management');
ResponseService::noAnyPermissionThenRedirect(['lesson-delete', 'topic-delete']);
try {
DB::beginTransaction();
// Find the Data by FindByID
$file = $this->files->findById($id);
// Delete the file data
$file->delete();
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e, "Lesson Controller -> deleteFile Method");
ResponseService::errorResponse();
}
}
}