File "TeacherApiController.php"
Full Path: /home/trinadezambia/public_html/admin_panel/app/Http/Controllers/Api/TeacherApiController.php
File size: 153.54 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\ClassSection;
use App\Models\ClassTeacher;
use App\Models\File;
use App\Models\School;
use App\Models\SubjectTeacher;
use App\Models\User;
use App\Repositories\Announcement\AnnouncementInterface;
use App\Repositories\AnnouncementClass\AnnouncementClassInterface;
use App\Repositories\Assignment\AssignmentInterface;
use App\Repositories\AssignmentCommon\AssignmentCommonInterface;
use App\Repositories\AssignmentSubmission\AssignmentSubmissionInterface;
use App\Repositories\Attendance\AttendanceInterface;
use App\Repositories\ClassSection\ClassSectionInterface;
use App\Repositories\ClassSubject\ClassSubjectInterface;
use App\Repositories\ClassTeachers\ClassTeachersInterface;
use App\Repositories\Diary\DiaryInterface;
use App\Repositories\DiaryCategory\DiaryCategoryInterface;
use App\Repositories\DiaryStudent\DiaryStudentInterface;
use App\Repositories\Exam\ExamInterface;
use App\Repositories\ExamMarks\ExamMarksInterface;
use App\Repositories\ExamResult\ExamResultInterface;
use App\Repositories\ExamTimetable\ExamTimetableInterface;
use App\Repositories\Files\FilesInterface;
use App\Repositories\Grades\GradesInterface;
use App\Repositories\Holiday\HolidayInterface;
use App\Repositories\Lessons\LessonsInterface;
use App\Repositories\LessonsCommon\LessonsCommonInterface;
use App\Repositories\SessionYear\SessionYearInterface;
use App\Repositories\Student\StudentInterface;
use App\Repositories\StudentSubject\StudentSubjectInterface;
use App\Repositories\Subject\SubjectInterface;
use App\Repositories\SubjectTeacher\SubjectTeacherInterface;
use App\Repositories\Timetable\TimetableInterface;
use App\Repositories\Topics\TopicsInterface;
use App\Repositories\User\UserInterface;
use App\Rules\uniqueLessonInClass;
use App\Rules\uniqueTopicInLesson;
use App\Services\CachingService;
use App\Services\FcmTokenService;
use App\Services\ResponseService;
use Carbon\Carbon;
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 JetBrains\PhpStorm\NoReturn;
use Throwable;
use App\Rules\YouTubeUrl;
use App\Rules\DynamicMimes;
use App\Rules\MaxFileSize;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use App\Models\AssignmentCommon;
use PDF;
use Str;
use App\Jobs\BulkNotificationsJobv3_0_0;
use App\Models\ClassSubject;
use App\Models\LessonCommon;
use App\Models\LessonTopicClass;
use App\Models\Subject;
use App\Models\Syllabus;
use App\Services\LessonNotificationService;
//use App\Models\Parents;
class TeacherApiController extends Controller
{
private StudentInterface $student;
private AttendanceInterface $attendance;
private TimetableInterface $timetable;
private AssignmentInterface $assignment;
private AssignmentSubmissionInterface $assignmentSubmission;
private CachingService $cache;
private ClassSubjectInterface $classSubject;
private FilesInterface $files;
private LessonsInterface $lesson;
private TopicsInterface $topic;
private AnnouncementInterface $announcement;
private AnnouncementClassInterface $announcementClass;
private SubjectTeacherInterface $subjectTeacher;
private StudentSubjectInterface $studentSubject;
private HolidayInterface $holiday;
private ExamInterface $exam;
private ExamTimetableInterface $examTimetable;
private ExamMarksInterface $examMarks;
private UserInterface $user;
private ClassSectionInterface $classSection;
private ClassTeachersInterface $classTeacher;
private LessonsCommonInterface $lessonCommon;
private SubjectInterface $subject;
private AssignmentCommonInterface $assignmentCommon;
private DiaryInterface $diary;
private DiaryStudentInterface $diaryStudent;
private SessionYearInterface $sessionYear;
private DiaryCategoryInterface $diaryCategory;
private LessonNotificationService $lessonNotificationService;
public function __construct(StudentInterface $student, AttendanceInterface $attendance, TimetableInterface $timetable, AssignmentInterface $assignment, AssignmentSubmissionInterface $assignmentSubmission, CachingService $cache, ClassSubjectInterface $classSubject, FilesInterface $files, LessonsInterface $lesson, TopicsInterface $topic, AnnouncementInterface $announcement, AnnouncementClassInterface $announcementClass, SubjectTeacherInterface $subjectTeacher, StudentSubjectInterface $studentSubject, HolidayInterface $holiday, ExamInterface $exam, ExamTimetableInterface $examTimetable, ExamMarksInterface $examMarks, UserInterface $user, ClassSectionInterface $classSection, ClassTeachersInterface $classTeacher, LessonsCommonInterface $lessonCommon, SubjectInterface $subject, AssignmentCommonInterface $assignmentCommon, DiaryInterface $diary, SessionYearInterface $sessionYear, DiaryStudentInterface $diaryStudent, DiaryCategoryInterface $diaryCategory, LessonNotificationService $lessonNotificationService)
{
$this->student = $student;
$this->attendance = $attendance;
$this->timetable = $timetable;
$this->assignment = $assignment;
$this->assignmentSubmission = $assignmentSubmission;
$this->cache = $cache;
$this->classSubject = $classSubject;
$this->files = $files;
$this->lesson = $lesson;
$this->topic = $topic;
$this->announcement = $announcement;
$this->announcementClass = $announcementClass;
$this->subjectTeacher = $subjectTeacher;
$this->studentSubject = $studentSubject;
$this->holiday = $holiday;
$this->exam = $exam;
$this->examTimetable = $examTimetable;
$this->examMarks = $examMarks;
$this->user = $user;
$this->classSection = $classSection;
$this->classTeacher = $classTeacher;
$this->subject = $subject;
$this->files = $files;
$this->lessonCommon = $lessonCommon;
$this->assignmentCommon = $assignmentCommon;
$this->diary = $diary;
$this->diaryStudent = $diaryStudent;
$this->sessionYear = $sessionYear;
$this->diaryCategory = $diaryCategory;
$this->lessonNotificationService = $lessonNotificationService;
}
#[NoReturn] public function login(Request $request)
{
$validator = Validator::make($request->all(), [
'school_code' => 'required',
'email' => 'required|email',
'password' => 'required',
], [
'school_code.required' => 'The school code is mandatory.',
'email.required' => 'The email field cannot be empty.',
'email.email' => 'Please provide a valid email address.',
'password.required' => 'The password field cannot be empty.',
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
$school = School::on('mysql')->where('code', $request->school_code)->first();
if ($school) {
DB::setDefaultConnection('school');
Config::set('database.connections.school.database', $school->database_name);
DB::purge('school');
DB::connection('school')->reconnect();
DB::setDefaultConnection('school');
} else {
// return response()->json(['message' => 'Invalid school code'], 400);
ResponseService::errorResponse('Invalid school code', null, config('constants.RESPONSE_CODE.INVALID_LOGIN'));
}
$user = User::withTrashed()
->where('email', $request->email)
->first();
// Check if the user has the 'Teacher' role or 'Staff' role
if ($user && !$user->hasRole('School Admin')) {
if ($user->hasRole('Student') || $user->hasRole('Parent') || $user->hasRole('Guardian')) {
ResponseService::errorResponse('You must have a teacher / Staff role to log in.');
}
}
if ($user && Hash::check($request->password, $user->password)) {
if ($user->trashed()) {
// User is soft-deleted, handle accordingly
ResponseService::errorResponse(trans('your_account_has_been_deactivated_please_contact_admin'), null, config('constants.RESPONSE_CODE.INVALID_LOGIN'));
}
}
if (
Auth::attempt([
'email' => $request->email,
'password' => $request->password
])
) {
$auth = Auth::user();
// $permission = $auth;
// Store FCM tokens using FcmTokenService
$fcmTokenService = app(FcmTokenService::class);
if ($request->fcm_id) {
$fcmTokenService->storeOrUpdateToken(
$auth,
$request->fcm_id,
$request->device_type ?? 'android',
$request->device_id ?? null
);
}
// Check school status is activated or not
if ($auth->school->status == 0 || $auth->status == 0) {
// Remove all FCM tokens for deactivated users
$fcmTokenService->removeToken($auth);
ResponseService::errorResponse(trans('your_account_has_been_deactivated_please_contact_admin'), null, config('constants.RESPONSE_CODE.INVALID_LOGIN'));
}
$token = $auth->createToken($auth->first_name)->plainTextToken;
if (Auth::user()->hasRole('Teacher')) {
$user = $auth->load(['teacher', 'teacher.staffSalary.payrollSetting']);
$customFields = $auth->extra_user_details->map(function ($row) {
$field = $row->form_field;
$value = $row->data;
if ($field->type === 'file' && !empty($row->data)) {
$value = url('storage/' . ltrim($row->data, '/'));
}
return [
'id' => $row->id,
'form_field_id' => $field->id,
'name' => $field->name,
'type' => $field->type,
'is_required' => (bool) $field->is_required,
'default_value' => $field->default_values,
'value' => $value,
'user_type' => $field->user_type == 1 ? 'student' : 'teacher/staff',
'rank' => $field->rank,
];
});
$user->custom_fields = $customFields;
$user->fcm_id = $request->fcm_id;
$user->web_fcm = $request->web_fcm;
$user->device_type = $request->device_type;
unset($user->extra_user_details);
} else {
$user = $auth->load(['staff', 'staff.staffSalary.payrollSetting']);
$customFields = $auth->extra_user_details->map(function ($row) {
$field = $row->form_field;
$value = $row->data;
if ($field->type === 'file' && !empty($row->data)) {
$value = url('storage/' . ltrim($row->data, '/'));
}
return [
'id' => $row->id,
'form_field_id' => $field->id,
'name' => $field->name,
'type' => $field->type,
'is_required' => (bool) $field->is_required,
'default_value' => $field->default_values,
'value' => $value,
'user_type' => $field->user_type == 1 ? 'student' : 'teacher/staff',
'rank' => $field->rank,
];
});
$user->custom_fields = $customFields;
$user->fcm_id = $request->fcm_id;
$user->web_fcm = $request->web_fcm;
$user->device_type = $request->device_type;
unset($user->extra_user_details);
}
ResponseService::successResponse('User logged-in!', $user, ['token' => $token], config('constants.RESPONSE_CODE.LOGIN_SUCCESS'));
}
ResponseService::errorResponse('Invalid Login Credentials', null, config('constants.RESPONSE_CODE.INVALID_LOGIN'));
}
public function subjects(Request $request)
{
$validator = Validator::make($request->all(), [
'class_section_id' => 'required|string',
'subject_id' => 'nullable|numeric',
]);
// \Log::info("class_section_id => ".$request->class_section_id);
// \Log::info("subject_id => ".$request->subject_id);
if ($validator->fails()) {
return ResponseService::validationError($validator->errors()->first());
}
try {
// Extract class_section_id and convert it to an array
$section_ids = explode(',', $request->class_section_id);
// Start building the query
$subjects = $this->subjectTeacher->builder()
->with('subject:id,name,type', 'class_section.class', 'class_section.class.shift', 'class_section.section', 'class_section.medium') // Eager load relations
->whereIn('class_section_id', $section_ids); // Filter by class_section_id
// Filter by subject_id if provided
if ($request->has('subject_id')) {
$subjects = $subjects->where('subject_id', $request->subject_id);
}
// Get the subjects
$subjects = $subjects->get();
// If there are multiple sections, find the common subjects
if (count($section_ids) > 1) {
// Group the subjects by 'subject_with_name' (create a composite name if needed)
$groupedBySubjectName = $subjects->groupBy(function ($subject) {
return $subject->subject->name . ' (' . $subject->subject->type . ')'; // Creating 'subject_with_name'
});
// Filter to only keep subjects that appear in more than one section (common subjects)
$commonSubjects = $groupedBySubjectName->filter(function ($group) {
return $group->count() > 1;
});
// Flatten the result to get a list of common subjects
$commonSubjectsList = $commonSubjects->flatten(1);
// Remove duplicates based on 'subject_with_name' and reset array keys
$commonSubjectsList = $commonSubjectsList->unique(function ($subject) {
return $subject->subject->name . ' (' . $subject->subject->type . ')'; // Unique by 'subject_with_name'
})->values();
// Assign to subjects
$subjects = $commonSubjectsList;
}
return ResponseService::successResponse('Teacher Subject Fetched Successfully.', $subjects->toArray());
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
return ResponseService::errorResponse();
}
}
public function getAssignment(Request $request)
{
ResponseService::noFeatureThenSendJson('Assignment Management');
ResponseService::noPermissionThenSendJson('assignment-list');
$validator = Validator::make($request->all(), [
'class_section_id' => 'nullable|numeric',
'class_subject_id' => 'nullable|numeric'
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
$session_year_id = $request->session_year_id ?? getSchoolSettings('session_year');
$sql = $this->assignment->builder()->with('class_section.class.stream', 'class_section.class.shift', 'file', 'class_subject', 'class_section.medium', 'assignment_commons');
if ($request->class_section_id) {
$sql = $sql->whereHas('assignment_commons', function ($q) use ($request) {
$q->where('class_section_id', $request->class_section_id);
});
}
if ($request->class_subject_id) {
$sql = $sql->whereHas('assignment_commons', function ($q) use ($request) {
$q->where('class_subject_id', $request->class_subject_id);
});
}
$data = $sql->orderBy('id', 'DESC')->paginate();
ResponseService::successResponse('Assignment Fetched Successfully.', $data);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function createAssignment(Request $request)
{
ResponseService::noFeatureThenSendJson('Assignment Management');
ResponseService::noPermissionThenSendJson('assignment-create');
$file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit');
$validator = Validator::make($request->all(), [
'class_section_id' => 'required|array',
'class_section_id.*' => 'numeric',
"class_subject_id" => 'required|numeric',
"name" => 'required',
"instructions" => 'nullable',
"due_date" => 'required|date',
"points" => 'nullable',
"resubmission" => 'nullable|boolean',
"extra_days_for_resubmission" => 'nullable|numeric',
"file" => 'nullable|array',
"file.*" => ['nullable', new DynamicMimes, new MaxFileSize($file_upload_size_limit)],
], [
'file.*' => trans('The file Uploaded must be less than :file_upload_size_limit MB.', [
'file_upload_size_limit' => $file_upload_size_limit,
]),
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
$sessionYear = $this->cache->getDefaultSessionYear();
$assignmentData = array(
...$request->all(),
'due_date' => date('Y-m-d H:i', strtotime($request->due_date)),
'resubmission' => $request->resubmission ? 1 : 0,
'extra_days_for_resubmission' => $request->resubmission ? $request->extra_days_for_resubmission : null,
'session_year_id' => $sessionYear->id,
'created_by' => Auth::user()->id,
);
$section_ids = is_array($request->class_section_id) ? $request->class_section_id : [$request->class_section_id];
$assignment = [];
$assignmentModelAssociate = [];
$assignmentCommonData = [];
foreach ($section_ids as $section_id) {
$assignmentData = array_merge($assignmentData, ['class_section_id' => $section_id]);
}
// Store the assignment data
$assignment = $this->assignment->create($assignmentData);
// Create assignment_commons for each section
$defaultSemester = $this->cache->getDefaultSemesterData();
foreach ($section_ids as $section_id) {
// $classSubject_ids = $this->classSubject->builder()->where('id', $request->class_subject_id)->first();
// $classSection = $this->classSection->builder()->where('id', $section_id)->with('class')->first();
// $classSubjects = $this->classSubject->builder()->where('class_id', $classSection->class->id)->where('subject_id', $classSubject_ids->subject_id)->first();
$classSection = $this->classSection->builder()->where('id', $section_id)->with('class')->first();
$subjectId = $this->classSubject->builder()->where('id', $request->class_subject_id)->pluck('subject_id');
$classSubject_ids = $this->classSubject->builder()->where('subject_id', $subjectId)->where('class_id', $classSection->class_id);
if ($classSection->class->include_semesters) {
$classSubject_ids = $classSubject_ids->where('semester_id', $defaultSemester->id);
} else {
$classSubject_ids = $classSubject_ids->where('semester_id', null);
}
$classSubject_ids = $classSubject_ids->first();
$assignmentCommonData['assignment_id'] = $assignment->id;
$assignmentCommonData['class_section_id'] = $section_id;
$assignmentCommonData['class_subject_id'] = $classSubject_ids->id;
$this->assignmentCommon->create($assignmentCommonData);
}
// Handle File Upload
if ($request->hasFile('file')) {
$fileData = [];
$assignmentModelAssociate = $this->files->model()->modal()->associate($assignment);
foreach ($request->file('file') as $file_upload) {
$tempFileData = array(
'modal_type' => $assignmentModelAssociate->modal_type,
'modal_id' => $assignmentModelAssociate->modal_id,
'file_name' => $file_upload->getClientOriginalName(),
'type' => 1,
'file_url' => $file_upload,
);
$fileData[] = $tempFileData;
}
// Store the files data
$this->files->createBulk($fileData);
}
// Handle URL Upload
if ($request->add_url) {
$urlData = [];
$urls = is_array($request->add_url) ? $request->add_url : [$request->add_url];
foreach ($urls as $url) {
$urlParts = parse_url($url);
$fileName = basename($urlParts['path'] ?? '/');
$assignmentModelAssociate = $this->files->model()->modal()->associate($assignment);
$tempUrlData = array(
'modal_type' => $assignmentModelAssociate->modal_type,
'modal_id' => $assignmentModelAssociate->modal_id,
'file_name' => $fileName,
'type' => 4,
'file_url' => $url,
);
$urlData[] = $tempUrlData;
}
// Store the URL data
$this->files->createBulk($urlData);
}
// $subjectTeacher = $this->subjectTeacher
// ->builder()
// ->with('subject')
// ->whereIn('class_section_id', $section_ids)
// ->where('class_subject_id', $request->class_subject_id)
// ->first();
// $title = 'New assignment added in ' . $subjectTeacher->subject->name;
// $body = $request->name;
// $type = 'assignment';
// $students = $this->student->builder()
// ->whereIn('class_section_id', (array) $request->class_section_id)
// ->get(['id', 'user_id', 'guardian_id']);
// $userIds = [];
// $studentMap = [];
// $guardianMap = [];
$assignmentCommons = AssignmentCommon::query()->where('assignment_id', $assignment->id)
->get(['class_section_id', 'class_subject_id']);
$classSubjectsMap = $this->classSubject
->builder()
->whereIn('id', $assignmentCommons->pluck('class_subject_id'))
->get()
->keyBy('id');
$sectionMap = $assignmentCommons->mapWithKeys(function ($ac) use ($classSubjectsMap) {
return [$ac->class_section_id => $classSubjectsMap[$ac->class_subject_id]];
});
$studentsQuery = \App\Models\Students::query()->with('user')
->whereIn('class_section_id', $sectionMap->keys());
$coreSections = $sectionMap
->filter(fn($cs) => $cs->type === 'Compulsory')
->keys()
->toArray();
$electivePairs = $sectionMap
->filter(fn($cs) => $cs->type === 'Elective')
->map(fn($cs, $sid) => [
'class_section_id' => $sid,
'class_subject_id' => $cs->id,
])
->values();
if ($electivePairs->isNotEmpty()) {
$studentsQuery->where(function ($q) use ($coreSections, $electivePairs) {
if ($coreSections) {
$q->whereIn('class_section_id', $coreSections);
}
$q->orWhereIn('user_id', function ($sub) use ($electivePairs) {
$sub->select('student_id')
->from('student_subjects')
->where(function ($inner) use ($electivePairs) {
foreach ($electivePairs as $pair) {
$inner->orWhere(function ($c) use ($pair) {
$c->where('class_section_id', $pair['class_section_id'])
->where('class_subject_id', $pair['class_subject_id']);
});
}
});
});
});
}
$students = $studentsQuery->get([
'id',
'user_id',
'guardian_id',
'class_section_id'
]);
$subjectName = $this->classSubject
->builder()
->where('id', $request->class_subject_id)
->with('subject')
->first();
$title = 'New assignment added in ' . $subjectName->subject->name . ' (' . $subjectName->subject->type . ')';
$body = $request->name;
$type = 'assignment';
$allPayloads = [];
foreach ($students as $student) {
if ($student->user_id) {
$userIds[] = $student->user_id;
$studentMap[$student->user_id] = $student->id;
}
if ($student->guardian_id) {
$userIds[] = $student->guardian_id;
$guardianMap[$student->guardian_id][] = $student->id;
}
}
$userIds = array_values(array_unique($userIds));
DB::commit();
if (!empty($userIds)) {
BulkNotificationsJobv3_0_0::dispatch(
Auth::user()->school_id,
$userIds,
$title,
$body,
'assignment',
[
// 🔒 INTERNAL LOGIC DATA
'internal' => [
'student_map' => $studentMap,
'guardian_map' => $guardianMap,
],
// 📦 FCM PAYLOAD DATA (FUTURE-PROOF)
'payload' => [
'notification_type' => 'assignment',
'assignment_id' => (int) $assignment->id,
'class_subject_id' => (int) $request->class_subject_id,
],
]
);
}
// DB::statement('SET FOREIGN_KEY_CHECKS=1;');
ResponseService::successResponse('Data Stored Successfully');
} catch (Throwable $e) {
if (
Str::contains($e->getMessage(), [
'does not exist',
'file_get_contents'
])
) {
DB::commit();
ResponseService::warningResponse("Data Stored successfully. But App push notification not send.");
} else {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
}
public function updateAssignment(Request $request)
{
ResponseService::noFeatureThenSendJson('Assignment Management');
ResponseService::noPermissionThenSendJson('assignment-edit');
$validator = Validator::make($request->all(), [
"assignment_id" => 'required|numeric',
'class_section_id' => 'required|array',
'class_section_id.*' => 'numeric',
"class_subject_id" => 'required|numeric',
"name" => 'required',
"instructions" => 'nullable',
"due_date" => 'required|date',
"points" => 'nullable',
"resubmission" => 'nullable|boolean',
"extra_days_for_resubmission" => 'nullable|numeric'
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
// $sessionYearId = getSchoolSettings('session_year');
$sessionYear = $this->cache->getDefaultSessionYear();
$assignmentData = array(
...$request->all(),
'due_date' => date('Y-m-d H:i', strtotime($request->due_date)),
'resubmission' => $request->resubmission ? 1 : 0,
'extra_days_for_resubmission' => $request->resubmission ? $request->extra_days_for_resubmission : null,
'session_year_id' => $sessionYear->id,
'edited_by' => Auth::user()->id,
);
$section_ids = is_array($request->class_section_id) ? $request->class_section_id : [$request->class_section_id];
foreach ($section_ids as $section_id) {
$assignmentData = array_merge($assignmentData, ['class_section_id' => $section_id]);
}
// DB::enableQueryLog();
$assignment = $this->assignment->update($request->assignment_id, $assignmentData);
// If File Exists
if ($request->hasFile('file')) {
$fileData = array(); // Empty FileData Array
// Create A File Model Instance
$assignmentModelAssociate = $this->files->model()->modal()->associate($assignment); // Get the Association Values of File with Assignment
foreach ($request->file as $file_upload) {
// Create Temp File Data Array
$tempFileData = array(
'modal_type' => $assignmentModelAssociate->modal_type,
'modal_id' => $assignmentModelAssociate->modal_id,
'file_name' => $file_upload->getClientOriginalName(),
'type' => 1,
'file_url' => $file_upload
);
$fileData[] = $tempFileData; // Store Temp File Data in Multi-Dimensional File Data Array
}
$this->files->createBulk($fileData); // Store File Data
}
if ($request->add_url) {
$urlData = array(); // Empty URL data array
$urls = is_array($request->add_url) ? $request->add_url : [$request->add_url];
foreach ($urls as $url) {
$urlParts = parse_url($url);
$fileName = basename($urlParts['path'] ?? '/'); // Extract the file name from the URL
$assignmentModelAssociate = $this->files->model()->modal()->associate($assignment);
$tempUrlData = array(
'modal_type' => $assignmentModelAssociate->modal_type,
'modal_id' => $assignmentModelAssociate->modal_id,
'file_name' => $fileName,
'type' => 4,
'file_url' => $url,
);
$urlData[] = $tempUrlData; // Store temp URL data in the array
}
// Store the URL data
$this->files->createBulk($urlData);
}
// $subjectTeacher = $this->subjectTeacher
// ->builder()
// ->with('subject')
// ->whereIn('class_section_id', $section_ids)
// ->where('class_subject_id', $request->class_subject_id)
// ->first();
// $title = 'Update assignment in ' . $subjectTeacher->subject->name;
// $body = $request->name;
// $type = 'assignment';
$baseCustomData = [
'assignment_id' => $assignment->id,
'class_subject_id' => $request->class_subject_id,
];
// /**
// * KEEP EXISTING STUDENT SELECTION — DO NOT CHANGE
// */
// $students = $this->student->builder()
// ->where('class_section_id', $request->class_section_id)
// ->get();
/**
* Build bulk payloads
*/
$assignmentCommons = AssignmentCommon::query()->where('assignment_id', $assignment->id)
->get(['class_section_id', 'class_subject_id']);
$classSubjectsMap = $this->classSubject
->builder()
->whereIn('id', $assignmentCommons->pluck('class_subject_id'))
->with('subject')
->get()
->keyBy('id');
$sectionMap = $assignmentCommons->mapWithKeys(function ($ac) use ($classSubjectsMap) {
return [$ac->class_section_id => $classSubjectsMap[$ac->class_subject_id]];
});
$studentsQuery = \App\Models\Students::query()->with('user')
->whereIn('class_section_id', $sectionMap->keys());
$coreSections = $sectionMap
->filter(fn($cs) => $cs->type === 'Compulsory')
->keys()
->toArray();
$electivePairs = $sectionMap
->filter(fn($cs) => $cs->type === 'Elective')
->map(fn($cs, $sid) => [
'class_section_id' => $sid,
'class_subject_id' => $cs->id,
])
->values();
if ($electivePairs->isNotEmpty()) {
$studentsQuery->where(function ($q) use ($coreSections, $electivePairs) {
if (!empty($coreSections)) {
$q->whereIn('class_section_id', $coreSections);
}
$q->orWhereIn('user_id', function ($sub) use ($electivePairs) {
$sub->select('student_id')
->from('student_subjects')
->where(function ($inner) use ($electivePairs) {
foreach ($electivePairs as $pair) {
$inner->orWhere(function ($c) use ($pair) {
$c->where('class_section_id', $pair['class_section_id'])
->where('class_subject_id', $pair['class_subject_id']);
});
}
});
});
});
}
$students = $studentsQuery->get([
'id',
'user_id',
'guardian_id',
'class_section_id'
]);
$subjectName = $this->classSubject
->builder()
->where('id', $request->class_subject_id)
->with('subject')
->first();
$title = 'Updated assignment in ' . $subjectName->subject->name . ' (' . $subjectName->subject->type . ')';
$body = $request->name;
$type = 'assignment';
$allPayloads = [];
foreach ($students as $student) {
// Student notification
if (!empty($student->user_id)) {
$allPayloads = array_merge(
$allPayloads,
buildPayloads(
[$student->user_id],
$title,
$body,
$type,
$baseCustomData
)
);
}
// Guardian notification with child_id
if (!empty($student->guardian_id)) {
$allPayloads = array_merge(
$allPayloads,
buildPayloads(
[$student->guardian_id],
$title,
$body,
$type,
array_merge($baseCustomData, [
'child_id' => $student->id
])
)
);
}
}
$assignment->save();
DB::commit();
if (!empty($allPayloads)) {
sendBulk($allPayloads);
}
ResponseService::successResponse('Data Updated Successfully');
} catch (Throwable $e) {
if (
Str::contains($e->getMessage(), [
'does not exist',
'file_get_contents'
])
) {
DB::commit();
ResponseService::warningResponse("Data Stored successfully. But App push notification not send.");
} else {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
}
public function deleteAssignment(Request $request)
{
ResponseService::noFeatureThenSendJson('Assignment Management');
ResponseService::noPermissionThenSendJson('assignment-delete');
try {
DB::beginTransaction();
$this->assignment->deleteById($request->assignment_id);
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function getAssignmentSubmission(Request $request)
{
ResponseService::noFeatureThenSendJson('Assignment Management');
ResponseService::noPermissionThenSendJson('assignment-submission');
$validator = Validator::make($request->all(), ['assignment_id' => 'required|nullable|numeric']);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
$data = $this->assignmentSubmission->builder()->with('assignment.class_subject.subject:id,name,type', 'file', 'student:id,first_name,last_name,image')->where('assignment_id', $request->assignment_id)->get();
ResponseService::successResponse('Assignment Fetched Successfully', $data);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function updateAssignmentSubmission(Request $request)
{
ResponseService::noFeatureThenSendJson('Assignment Management');
ResponseService::noPermissionThenSendJson('assignment-submission');
$validator = Validator::make($request->all(), [
'assignment_submission_id' => 'required|numeric',
'status' => 'required|numeric|in:1,2',
'points' => 'nullable|numeric',
'feedback' => 'nullable'
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
$updateAssignmentSubmissionData = array(
'feedback' => $request->feedback,
'points' => $request->status == 1 ? $request->points : NULL,
'status' => $request->status,
);
$assignmentSubmission = $this->assignmentSubmission->update($request->assignment_submission_id, $updateAssignmentSubmissionData);
$assignmentData = $this->assignment->builder()->where('id', $assignmentSubmission->assignment_id)->with('class_subject.subject')->first();
if ($request->status == 1) {
$title = "Assignment accepted";
$body = $assignmentData->name . " accepted in " . $assignmentData->class_subject->subject->name_with_type . " subject";
} else {
$title = "Assignment rejected";
$body = $assignmentData->name . " rejected in " . $assignmentData->class_subject->subject->name_with_type . " subject";
}
$type = "assignment";
$students = $this->student->builder()->select('user_id', 'guardian_id')->where('user_id', $assignmentSubmission->student_id)->get();
$guardian_id = $students->pluck('guardian_id')->toArray();
$student_id = $students->pluck('user_id')->toArray();
$user = array_merge($student_id, $guardian_id);
DB::commit();
send_notification($user, $title, $body, $type);
ResponseService::successResponse('Data Updated Successfully');
} catch (Throwable $e) {
if (
Str::contains($e->getMessage(), [
'does not exist',
'file_get_contents'
])
) {
DB::commit();
ResponseService::warningResponse("Data Stored successfully. But App push notification not send.");
} else {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
}
public function getLesson(Request $request)
{
ResponseService::noFeatureThenSendJson('Lesson Management');
ResponseService::noPermissionThenSendJson('lesson-list');
$validator = Validator::make($request->all(), [
'lesson_id' => 'nullable|numeric',
'class_section_id' => 'nullable|numeric',
'class_subject_id' => 'nullable|numeric'
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
$session_year_id = $request->session_year_id ?? getSchoolSettings('session_year');
$sql = $this->lesson->builder()->with('file', 'lesson_commons.syllabus.class_subject.subject:id,name', 'lesson_commons.class_section.class:id,name', 'lesson_commons.class_section.class.shift')->withCount('topic');
if ($request->lesson_id) {
$sql = $sql->where('id', $request->lesson_id);
}
if ($request->class_section_id) {
\Log::info("class_section_id => " . $request->class_section_id);
$sql = $sql->whereHas('lesson_commons', function ($q) use ($request) {
$q->where('class_section_id', $request->class_section_id);
});
}
if ($request->class_subject_id) {
\Log::info("class_subject_id => " . $request->class_subject_id);
$sql = $sql->whereHas('lesson_commons.syllabus.class_subject', function ($q) use ($request) {
$q->where('id', $request->class_subject_id);
});
}
$data = $sql->orderBy('id', 'DESC')->get();
ResponseService::successResponse('Lesson Fetched Successfully', $data);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function createLesson(Request $request)
{
ResponseService::noFeatureThenSendJson('Lesson Management');
ResponseService::noPermissionThenSendJson('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',
'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.*.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 ================= */
$sectionIds = array_values(array_unique($request->class_section_id));
$lessonData = $request->all();
$lessonData['class_section_id'] = $sectionIds[0];
$lesson = $this->lesson->create($lessonData);
$sessionYear = $this->cache->getSessionYear();
/* ========== LESSON COMMON (UNCHANGED LOGIC) ========== */
$subjectId = $this->classSubject
->builder()
->where('id', $request->class_subject_id)
->value('subject_id');
$lessonData = [];
foreach ($sectionIds as $sectionId) {
$classSection = $this->classSection
->builder()
->with('class')
->where('id', $sectionId)
->first();
$classSubject = ClassSubject::where('class_id', $classSection->class_id)
->where('id', $request->class_subject_id)
->where('session_year_id', $sessionYear->id)
->first();
if ($classSubject && $classSubject->syllabus_id) {
$lessonData[] = [
'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($lessonData);
/* ================= FILES ================= */
if (!empty($request->file)) {
$lessonFileData = [];
foreach ($request->file as $file) {
if (!empty($file['type'])) {
$lessonFileData[] = $this->prepareFileData($file);
}
}
if ($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);
}
}
DB::commit();
/* ================== PUSH NOTIFICATIONS ================== */
$this->lessonNotificationService->send($lesson, $sectionIds, (int)$subjectId, 'created');
ResponseService::successResponse('Data Stored Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e, 'Lesson Controller -> createLesson');
return ResponseService::errorResponse();
}
}
public function updateLesson(Request $request)
{
ResponseService::noFeatureThenSendJson('Lesson Management');
ResponseService::noPermissionThenSendJson('lesson-edit');
$file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit');
$validator = Validator::make($request->all(), [
'lesson_id' => 'required|numeric',
'name' => 'required',
'description' => 'required',
'file' => 'nullable|array',
'file.*.type' => 'nullable|in:file_upload,youtube_link,video_upload,other_link',
'file.*.name' => 'required_with:file.*.type',
'file.*.thumbnail' => 'required_if:file.*.type,youtube_link,video_upload,other_link',
'file.*.file' => [
'required_if:file.*.type,file_upload,video_upload',
new MaxFileSize($file_upload_size_limit)
],
'file.*.link' => 'required_if:file.*.type,youtube_link,other_link',
]);
if ($validator->fails()) {
return ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
/* -------------------- Update lesson -------------------- */
$lesson = $this->lesson->update($request->lesson_id, $request->all());
// load lesson commons with syllabus
$lesson = $lesson->load('lesson_commons.syllabus');
/* -------------------- Update / Add files -------------------- */
if (!empty($request->file)) {
foreach ($request->file as $file) {
if (empty($file['type'])) {
continue;
}
$lessonFile = $this->files->model();
$lessonModelAssociate = $lessonFile->modal()->associate($lesson);
$tempFileData = [
'id' => $file['id'] ?? null,
'modal_type' => $lessonModelAssociate->modal_type,
'modal_id' => $lessonModelAssociate->modal_id,
'file_name' => $file['name'],
'created_at' => now(),
'updated_at' => now(),
];
switch ($file['type']) {
case 'file_upload':
$tempFileData['type'] = 1;
$tempFileData['file_thumbnail'] = null;
$tempFileData['file_url'] = $file['file'] ?? null;
break;
case 'youtube_link':
$tempFileData['type'] = 2;
$tempFileData['file_thumbnail'] = $file['thumbnail'] ?? null;
$tempFileData['file_url'] = $file['link'];
break;
case 'video_upload':
$tempFileData['type'] = 3;
$tempFileData['file_thumbnail'] = $file['thumbnail'] ?? null;
$tempFileData['file_url'] = $file['file'] ?? null;
break;
case 'other_link':
$tempFileData['type'] = 4;
$tempFileData['file_thumbnail'] = $file['thumbnail'] ?? null;
$tempFileData['file_url'] = $file['link'];
break;
}
$this->files->updateOrCreate(
['id' => $file['id'] ?? null],
$tempFileData
);
}
}
DB::commit();
$sectionIds = $lesson->lesson_commons->pluck('class_section_id')->toArray();
$subjectId = $lesson->lesson_commons->first()->syllabus->subject_id;
/* ================== BULK NOTIFICATIONS ================== */
$this->lessonNotificationService->send($lesson, $sectionIds, (int)$subjectId, 'updated');
ResponseService::successResponse('Data Updated Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e, 'Lesson Controller -> updateLesson');
ResponseService::errorResponse();
}
}
public function deleteLesson(Request $request)
{
ResponseService::noFeatureThenSendJson('Lesson Management');
ResponseService::noPermissionThenSendJson('lesson-delete');
$validator = Validator::make($request->all(), ['lesson_id' => 'required|numeric',]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
$this->lesson->deleteById($request->lesson_id);
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable) {
ResponseService::errorResponse();
}
}
public function getTopic(Request $request)
{
ResponseService::noFeatureThenSendJson('Lesson Management');
ResponseService::noPermissionThenSendJson('topic-list');
$validator = Validator::make($request->all(), ['lesson_id' => 'required|numeric',]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
// $sql = $this->topic->builder()->with('lesson_topics.class_section', 'lesson_topics.class_subject.subject', 'file');
// $data = $sql->where('lesson_id', $request->lesson_id)->orderBy('id', 'DESC')->get();
$sql = $this->topic->builder()->with('lesson.lesson_commons.syllabus.subject', 'file')
->with([
'lesson_topic_class' => function ($q) {
$q->with('class_section.class.stream')->with('class_section.medium', 'class_section.section');
}
]);
if ($request->lesson_id) {
$sql = $sql->where('lesson_id', $request->lesson_id);
}
if ($request->class_section_id) {
$sql = $sql->whereHas('lesson_topic_class', function ($q) use ($request) {
$q->where('class_section_id', $request->class_section_id);
});
}
$data = $sql->orderBy('id', 'DESC')->get();
ResponseService::successResponse('Topic Fetched Successfully', $data);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function createTopic(Request $request)
{
ResponseService::noFeatureThenSendJson('Lesson Management');
ResponseService::noPermissionThenSendJson('topic-create');
$file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit');
$validator = Validator::make($request->all(), [
'lesson_id' => 'required|numeric',
'name' => 'required',
'description' => 'required',
'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.*.link' => [
'nullable',
'required_if:file_data.*.type,other_link',
'url',
],
'file_data.*.file' => [
'nullable',
'required_if:file_data.*.type,file_upload,video_upload',
new DynamicMimes(),
new MaxFileSize($file_upload_size_limit), // Max file size validation
],
], [
'file_data.*.file.required_if' => trans('The file field is required when uploading a file.'),
'file_data.*.file.dynamic_mimes' => trans('The uploaded file type is not allowed.'),
'file_data.*.file.max_file_size' => trans('The file uploaded must be less than :file_upload_size_limit MB.', [
'file_upload_size_limit' => $file_upload_size_limit,
]),
'file_data.*.link.required_if' => trans('The link field is required when the type is YouTube link or Other link.'),
'file_data.*.link.url' => trans('The provided link must be a valid URL.'),
'file_data.*.link.youtube_url' => trans('The provided YouTube URL is not valid.'),
'file_data.*.file.required_if' => trans('The file is required when uploading a video or file.'),
]);
if ($validator->fails()) {
ResponseService::errorResponse($validator->errors()->first());
}
try {
DB::beginTransaction();
$lessonTopicFileData = [];
// Prepare file data if provided
if (!empty($request->file)) {
foreach ($request->file as $file) {
if ($file['type']) {
$lessonTopicFileData[] = $this->prepareFileData($file);
}
$file_type = $file['type'];
if ($file_type == "youtube_link") {
$file_type = "other_link";
}
}
}
$lessonTopicData = array(
'lesson_id' => $request->lesson_id,
'name' => $request->name,
'description' => $request->description,
'school_id' => Auth::user()->school_id,
);
// Create topics and store them
$topics = $this->topic->create($lessonTopicData);
// Store lesson topic classes
$lessonTopicClasses = [];
foreach ($request->class_section_id ?? [] as $class_section_id) {
$lessonTopicClasses[] = [
'lesson_topic_id' => $topics->id,
'class_section_id' => $class_section_id,
'school_id' => Auth::user()->school_id,
];
}
if (!empty($lessonTopicClasses)) {
LessonTopicClass::upsert($lessonTopicClasses, ['lesson_topic_id', 'class_section_id'], ['school_id']);
}
// load relations
$topics = $topics->load('lesson_topic_class', 'lesson.lesson_commons.syllabus');
$lessonFile = $this->files->model();
$lessonModelAssociate = $lessonFile->modal()->associate($topics);
// Create a file model instance
if (!empty($lessonTopicFileData)) {
foreach ($lessonTopicFileData as &$fileData) {
// Set modal_type and modal_id for each fileData
$fileData['modal_type'] = $lessonModelAssociate->modal_type; // Adjust according to your model's name
$fileData['modal_id'] = $topics->id; // Use the last created topic's id (or adjust logic if needed)
}
// Bulk create files
$this->files->createBulk($lessonTopicFileData);
}
DB::commit();
$sectionIds = $topics->lesson_topic_class->pluck('class_section_id')->toArray();
$subject_id = $topics->lesson->lesson_commons->first()->syllabus->subject_id;
$this->lessonNotificationService->send($topics->lesson, $sectionIds, (int)$subject_id, 'added');
ResponseService::successResponse('Data Stored Successfully');
} catch (Throwable $e) {
if (
Str::contains($e->getMessage(), [
'does not exist',
'file_get_contents'
])
) {
DB::commit();
ResponseService::warningResponse("Data Stored successfully. But App push notification not send.");
} else {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
}
public function updateTopic(Request $request)
{
ResponseService::noFeatureThenSendJson('Lesson Management');
ResponseService::noPermissionThenSendJson('topic-edit');
$file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit');
$validator = Validator::make($request->all(), [
'topic_id' => 'required|numeric',
'name' => 'required',
'description' => 'required',
'file' => 'nullable|array',
'file.*.type' => 'nullable|in:file_upload,youtube_link,video_upload,other_link',
'file.*.name' => 'required_with:file.*.type',
'file.*.thumbnail' => 'required_if:file.*.type,youtube_link,video_upload,other_link',
'file.*.file' => ['required_if:file.*.type,file_upload,video_upload', new MaxFileSize($file_upload_size_limit)],
'file.*.link' => 'required_if:file.*.type,youtube_link,other_link',
], [
'file.*.file' => trans('The file Uploaded must be less than :file_upload_size_limit MB.', [
'file_upload_size_limit' => $file_upload_size_limit,
]),
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
$topic = $this->topic->update($request->topic_id, $request->all());
// load relations
$topics = $topic->load('lesson_topic_class', 'lesson.lesson_commons.syllabus');
//Add the new Files
if ($request->file) {
foreach ($request->file as $file) {
if ($file['type']) {
// Create A File Model Instance
$topicFile = $this->files->model();
// Get the Association Values of File with Topic
$topicModelAssociate = $topicFile->modal()->associate($topic);
// Make custom Array for storing the data in fileData
$fileData = array(
'id' => $file['id'] ?? null,
'modal_type' => $topicModelAssociate->modal_type,
'modal_id' => $topicModelAssociate->modal_id,
'file_name' => $file['name'],
);
$file_type = $file['type'];
if ($file_type == "youtube_link") {
$file_type = "other_link";
}
// If File Upload
if ($file['type'] == "file_upload") {
// Add Type And File Url to TempDataArray and make Thumbnail data null
$fileData['type'] = 1;
$fileData['file_thumbnail'] = null;
if (!empty($file['file'])) {
$fileData['file_url'] = $file['file'];
}
} elseif ($file['type'] == "youtube_link") {
// Add Type , Thumbnail and Link to TempDataArray
$fileData['type'] = 2;
if (!empty($file['thumbnail'])) {
$fileData['file_thumbnail'] = $file['thumbnail'];
}
$fileData['file_url'] = $file['link'];
} elseif ($file['type'] == "video_upload") {
// Add Type , File Thumbnail and File URL to TempDataArray
$fileData['type'] = 3;
if (!empty($file['thumbnail'])) {
$fileData['file_thumbnail'] = $file['thumbnail'];
}
if (!empty($file['file'])) {
$fileData['file_url'] = $file['file'];
}
} elseif ($file['type'] == "other_link") {
// Add Type , File Thumbnail and File URL to TempDataArray
$fileData['type'] = 4;
if ($file['thumbnail']) {
$fileData['file_thumbnail'] = $file['thumbnail'];
}
$fileData['file_url'] = $file['link'];
}
$fileData['created_at'] = date('Y-m-d H:i:s');
$fileData['updated_at'] = date('Y-m-d H:i:s');
$this->files->updateOrCreate(['id' => $file['id'] ?? null], $fileData);
}
}
}
DB::commit();
/* ================= NOTIFICATIONS ================= */
$sectionIds = $topics->lesson_topic_class->pluck('class_section_id')->toArray();
$subject_id = $topics->lesson->lesson_commons->first()->syllabus->subject_id;
$this->lessonNotificationService->send($topics->lesson, $sectionIds, (int)$subject_id, 'updated');
ResponseService::successResponse('Data Updated Successfully');
} catch (Throwable $e) {
if (
Str::contains($e->getMessage(), [
'does not exist',
'file_get_contents'
])
) {
DB::commit();
ResponseService::warningResponse("Data Stored successfully. But App push notification not send.");
} else {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
}
public function deleteTopic(Request $request)
{
ResponseService::noFeatureThenSendJson('Lesson Management');
ResponseService::noPermissionThenSendJson('topic-delete');
try {
DB::beginTransaction();
$this->topic->deleteById($request->topic_id);
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function updateFile(Request $request)
{
$validator = Validator::make($request->all(), ['file_id' => 'required|numeric',]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
$file = File::find($request->file_id);
$file->file_name = $request->name;
if ($file->type == "1") {
// Type File :- File Upload
if (!empty($request->file)) {
if (Storage::disk('public')->exists($file->getRawOriginal('file_url'))) {
Storage::disk('public')->delete($file->getRawOriginal('file_url'));
}
if ($file->modal_type == "App\Models\Lesson") {
$file->file_url = $request->file->store('lessons', 'public');
} else if ($file->modal_type == "App\Models\LessonTopic") {
$file->file_url = $request->file->store('topics', 'public');
} else {
$file->file_url = $request->file->store('other', 'public');
}
}
} elseif ($file->type == "2") {
// Type File :- YouTube Link Upload
if (!empty($request->thumbnail)) {
if (Storage::disk('public')->exists($file->getRawOriginal('file_url'))) {
Storage::disk('public')->delete($file->getRawOriginal('file_url'));
}
if ($file->modal_type == "App\Models\Lesson") {
$file->file_thumbnail = $request->thumbnail->store('lessons', 'public');
} else if ($file->modal_type == "App\Models\LessonTopic") {
$file->file_thumbnail = $request->thumbnail->store('topics', 'public');
} else {
$file->file_thumbnail = $request->thumbnail->store('other', 'public');
}
}
$file->file_url = $request->link;
} elseif ($file->type == "3") {
// Type File :- Video Upload
if (!empty($request->file)) {
if (Storage::disk('public')->exists($file->getRawOriginal('file_url'))) {
Storage::disk('public')->delete($file->getRawOriginal('file_url'));
}
if ($file->modal_type == "App\Models\Lesson") {
$file->file_url = $request->file->store('lessons', 'public');
} else if ($file->modal_type == "App\Models\LessonTopic") {
$file->file_url = $request->file->store('topics', 'public');
} else {
$file->file_url = $request->file->store('other', 'public');
}
}
if (!empty($request->thumbnail)) {
if (Storage::disk('public')->exists($file->getRawOriginal('file_url'))) {
Storage::disk('public')->delete($file->getRawOriginal('file_url'));
}
if ($file->modal_type == "App\Models\Lesson") {
$file->file_thumbnail = $request->thumbnail->store('lessons', 'public');
} else if ($file->modal_type == "App\Models\LessonTopic") {
$file->file_thumbnail = $request->thumbnail->store('topics', 'public');
} else {
$file->file_thumbnail = $request->thumbnail->store('other', 'public');
}
}
}
$file->save();
ResponseService::successResponse('Data Stored Successfully', $file);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function deleteFile(Request $request)
{
$validator = Validator::make($request->all(), ['file_id' => 'required|numeric',]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
$this->files->deleteById($request->file_id);
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function getAnnouncement(Request $request)
{
ResponseService::noFeatureThenSendJson('Announcement Management');
ResponseService::noPermissionThenSendJson('announcement-list');
$validator = Validator::make($request->all(), [
'class_section_id' => 'nullable|numeric',
'subject_id' => 'nullable|numeric',
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
$session_year_id = $request->session_year_id ?? getSchoolSettings('session_year');
$sql = $this->announcement->builder()->select('id', 'title', 'description')->with('file', 'announcement_class.class_section.class.stream', 'announcement_class.class_section.class.shift', 'announcement_class.class_section.section', 'announcement_class.class_section.medium')->SubjectTeacher();
if ($request->class_section_id) {
$sql = $sql->whereHas('announcement_class', function ($q) use ($request) {
$q->where('class_section_id', $request->class_section_id);
});
}
if ($request->subject_id) {
// $classSection = $this->classSection->builder()->where('id', $section_id)->with('class')->first();
// $classSubjects = $this->classSubject->builder()->where('class_id', $classSection->class->id)->where('subject_id', $request->subject_id)->first();
$sql = $sql->with('class_subjects')->whereHas('announcement_class', function ($q) use ($request) {
$q->where('class_subjects.subject_id', $request->subject_id);
});
// $sql = $sql->whereHas('announcement_class', function ($q) use ($request) {
// $q->where('class_subject_id', $request->class_subject_id);
// });
}
$data = $sql->orderBy('id', 'DESC')->paginate();
ResponseService::successResponse('Announcement Fetched Successfully.', $data);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function sendAnnouncement(Request $request)
{
ResponseService::noFeatureThenSendJson('Announcement Management');
ResponseService::noPermissionThenSendJson('announcement-create');
$file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit');
$validator = Validator::make($request->all(), [
'class_section_id' => 'required|array',
'class_section_id.*' => 'numeric',
'class_subject_id' => 'required|numeric',
'title' => 'required',
'description' => 'nullable',
'add_url' => 'nullable|url',
'file' => 'nullable|array',
'file.*' => [
'file',
'mimes:jpeg,png,pdf',
new MaxFileSize($file_upload_size_limit)
],
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
/* ================= CORE DATA ================= */
$sessionYear = $this->cache->getDefaultSessionYear();
$announcement = $this->announcement->create([
'title' => $request->title,
'description' => $request->description,
'session_year_id' => $sessionYear->id,
'school_id' => Auth::user()->school_id,
]);
/* ================= ANNOUNCEMENT CLASS ================= */
foreach ($request->class_section_id as $sectionId) {
$classSection = $this->classSection
->builder()
->where('id', $sectionId)
->with('class')
->first();
$classSubject = $this->classSubject
->builder()
->where('id', $request->class_subject_id)
->first();
$this->announcementClass->create([
'announcement_id' => $announcement->id,
'class_section_id' => $sectionId,
'class_subject_id' => $classSubject?->id,
]);
}
/* ================= FILE / URL ================= */
if ($request->hasFile('file')) {
$files = [];
$assoc = $this->files->model()->modal()->associate($announcement);
foreach ($request->file as $file) {
$files[] = [
'modal_type' => $assoc->modal_type,
'modal_id' => $assoc->modal_id,
'file_name' => $file->getClientOriginalName(),
'type' => 1,
'file_url' => $file,
];
}
$this->files->createBulk($files);
}
if ($request->add_url) {
$urls = is_array($request->add_url) ? $request->add_url : [$request->add_url];
$urlData = [];
$assoc = $this->files->model()->modal()->associate($announcement);
foreach ($urls as $url) {
$urlData[] = [
'modal_type' => $assoc->modal_type,
'modal_id' => $assoc->modal_id,
'file_name' => basename(parse_url($url, PHP_URL_PATH) ?? '/'),
'type' => 4,
'file_url' => $url,
];
}
$this->files->createBulk($urlData);
}
DB::commit();
/* ================= NOTIFICATIONS (NEW ARCHITECTURE) ================= */
$announcementClasses = $this->announcementClass
->builder()
->where('announcement_id', $announcement->id)
->get(['class_section_id', 'class_subject_id']);
$sectionMap = [];
foreach ($announcementClasses as $ac) {
$sectionMap[$ac->class_section_id] = $ac->class_subject_id;
}
$students = \App\Models\Students::query()
->whereIn('class_section_id', array_keys($sectionMap))
->get(['id', 'user_id', 'guardian_id', 'class_section_id']);
$userIds = [];
$studentMap = [];
$guardianMap = [];
foreach ($students as $student) {
if ($student->user_id) {
$userIds[] = $student->user_id;
$studentMap[$student->user_id] = $student->id;
}
if ($student->guardian_id) {
$userIds[] = $student->guardian_id;
$guardianMap[$student->guardian_id][] = $student->id;
}
}
$userIds = array_values(array_unique($userIds));
$subject = ClassSubject::with('subject')->find($request->class_subject_id);
$title = $subject
? trans('New announcement in') . ' ' . $subject->subject->name_with_type
: trans('New announcement');
if (!empty($userIds)) {
BulkNotificationsJobv3_0_0::dispatch(
Auth::user()->school_id,
$userIds,
$title,
$request->title,
'Announcement',
[
// 🔒 INTERNAL (logic only)
'internal' => [
'section_map' => $sectionMap,
'student_map' => $studentMap,
'guardian_map' => $guardianMap,
],
// 📦 PAYLOAD (FCM only)
'payload' => [
'subject_id' => $classSubject?->subject_id,
],
]
);
}
ResponseService::successResponse('Data Stored Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function updateAnnouncement(Request $request)
{
ResponseService::noFeatureThenSendJson('Announcement Management');
ResponseService::noPermissionThenSendJson('announcement-edit');
$validator = Validator::make($request->all(), [
'announcement_id' => 'required|numeric',
'class_section_id' => 'required|array',
'class_section_id.*' => 'numeric',
'class_subject_id' => 'required|numeric',
'title' => 'required',
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
/* ================= CORE UPDATE ================= */
$sessionYear = $this->cache->getDefaultSessionYear();
$announcement = $this->announcement->update($request->announcement_id, [
'title' => $request->title,
'description' => $request->description,
'session_year_id' => $sessionYear->id,
]);
$oldSections = $this->announcement
->findById($request->announcement_id)
->announcement_class
->pluck('class_section_id')
->toArray();
$announcementClassData = [];
foreach ($request->class_section_id as $sectionId) {
$announcementClassData[] = [
'announcement_id' => $announcement->id,
'class_section_id' => $sectionId,
'class_subject_id' => $request->class_subject_id,
];
if (($k = array_search($sectionId, $oldSections)) !== false) {
unset($oldSections[$k]);
}
}
$this->announcementClass->upsert(
$announcementClassData,
['announcement_id', 'class_section_id', 'school_id'],
['class_subject_id']
);
if (!empty($oldSections)) {
$this->announcementClass
->builder()
->where('announcement_id', $announcement->id)
->whereIn('class_section_id', $oldSections)
->delete();
}
DB::commit();
/* ================= BULK NOTIFICATIONS ================= */
$announcementClasses = $this->announcementClass
->builder()
->where('announcement_id', $announcement->id)
->get(['class_section_id', 'class_subject_id']);
$sectionMap = [];
foreach ($announcementClasses as $ac) {
$sectionMap[$ac->class_section_id] = $ac->class_subject_id;
}
$studentsQuery = \App\Models\Students::query()
->whereIn('class_section_id', array_keys($sectionMap));
$coreSections = [];
$electivePairs = [];
foreach ($sectionMap as $sectionId => $classSubjectId) {
if (!$classSubjectId) {
$coreSections[] = $sectionId;
continue;
}
$classSubject = $this->classSubject->findById($classSubjectId, ['type']);
if ($classSubject?->type === 'Elective') {
$electivePairs[] = [
'class_section_id' => $sectionId,
'class_subject_id' => $classSubjectId,
];
} else {
$coreSections[] = $sectionId;
}
}
$studentsQuery->where(function ($q) use ($coreSections, $electivePairs) {
if ($coreSections) {
$q->whereIn('class_section_id', $coreSections);
}
if ($electivePairs) {
$q->orWhereIn('user_id', function ($sub) use ($electivePairs) {
$sub->select('student_id')
->from('student_subjects')
->where(function ($inner) use ($electivePairs) {
foreach ($electivePairs as $pair) {
$inner->orWhere(
fn($c) =>
$c->where('class_section_id', $pair['class_section_id'])
->where('class_subject_id', $pair['class_subject_id'])
);
}
});
});
}
});
$students = $studentsQuery->get([
'id',
'user_id',
'guardian_id',
'class_section_id'
]);
$userIds = [];
$studentMap = [];
$guardianMap = [];
foreach ($students as $student) {
if ($student->user_id) {
$userIds[] = $student->user_id;
$studentMap[$student->user_id] = $student->id;
}
if ($student->guardian_id) {
$userIds[] = $student->guardian_id;
$guardianMap[$student->guardian_id][] = $student->id;
}
}
$userIds = array_values(array_unique($userIds));
$subject = \App\Models\Subject::find($request->class_subject_id);
$title = $subject
? trans('Updated announcement in') . ' ' . $subject->name_with_type
: trans('Updated announcement');
if ($userIds) {
BulkNotificationsJobv3_0_0::dispatch(
auth()->user()->school_id,
$userIds,
$title,
$request->title,
'Announcement',
[
// 🔒 INTERNAL (logic only)
'internal' => [
'section_map' => $sectionMap,
'student_map' => $studentMap,
'guardian_map' => $guardianMap,
],
// 📦 PAYLOAD (FCM only)
'payload' => [
'subject_id' => $classSubject?->subject_id,
],
]
);
}
ResponseService::successResponse('Data Updated Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function deleteAnnouncement(Request $request)
{
ResponseService::noFeatureThenSendJson('Announcement Management');
ResponseService::noPermissionThenSendJson('announcement-delete');
$validator = Validator::make($request->all(), ['announcement_id' => 'required|numeric',]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
$this->announcement->deleteById($request->announcement_id);
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function getAttendance(Request $request)
{
ResponseService::noFeatureThenSendJson('Attendance Management');
ResponseService::noAnyPermissionThenSendJson(['attendance-list', 'class-teacher']);
$class_section_id = $request->class_section_id;
$attendance_type = $request->type;
$date = date('Y-m-d', strtotime($request->date));
$validator = Validator::make($request->all(), [
'class_section_id' => 'required',
'date' => 'required|date',
'type' => 'in:0,1',
]);
if ($validator->fails()) {
ResponseService::validationError();
}
try {
$sql = $this->attendance->builder()->with('user:id,first_name,last_name,image', 'user.student:id,user_id,roll_number')->where('class_section_id', $class_section_id)->where('date', $date);
if (isset($attendance_type) && $attendance_type != '') {
$sql->where('type', $attendance_type);
}
$data = $sql->get();
$holiday = $this->holiday->builder()->where('date', $date)->get();
if ($holiday->count()) {
ResponseService::successResponse("Data Fetched Successfully", $data, [
'is_holiday' => true,
'holiday' => $holiday,
]);
} else if ($data->count()) {
if ($data->first()->type == 3) {
ResponseService::successResponse("Data Fetched Successfully", $data, ['is_holiday' => true]);
} else {
ResponseService::successResponse("Data Fetched Successfully", $data, ['is_holiday' => false]);
}
} else {
ResponseService::successResponse("Attendance not recorded", $data, ['is_holiday' => false]);
}
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function submitAttendance(Request $request)
{
ResponseService::noFeatureThenSendJson('Attendance Management');
ResponseService::noAnyPermissionThenSendJson([
'attendance-create',
'attendance-edit',
'class-teacher'
]);
$validator = Validator::make($request->all(), [
'class_section_id' => 'required',
// 'attendance.*.student_id' => 'required',
// 'attendance.*.type' => 'required|in:0,1,3',
'date' => 'required|date',
'holiday' => 'in:0,1',
]);
if ($validator->fails()) {
ResponseService::validationError();
}
try {
DB::beginTransaction();
$sessionYear = $this->cache->getDefaultSessionYear();
$date = date('Y-m-d', strtotime($request->date));
$student_ids = array();
if ($request->holiday) {
$users = $this->student->builder()->where('class_section_id', $request->class_section_id)->get();
foreach ($users as $key => $user) {
$attendanceData = [
'class_section_id' => $request->class_section_id,
'student_id' => $user->user_id,
'session_year_id' => $sessionYear->id,
'type' => 3,
'date' => date('Y-m-d', strtotime($request->date)),
];
$attendance = $this->attendance->builder()->where('class_section_id', $request->class_section_id)->where('student_id', $user->user_id)->whereDate('date', $date)->first();
if ($attendance) {
$this->attendance->update($attendance->id, $attendanceData);
} else {
$this->attendance->create($attendanceData);
}
}
DB::commit();
} else {
for ($i = 0, $iMax = count($request->attendance); $i < $iMax; $i++) {
$attendanceData = [
'class_section_id' => $request->class_section_id,
'student_id' => $request->attendance[$i]['student_id'],
'session_year_id' => $sessionYear->id,
'type' => $request->attendance[$i]['type'],
'date' => date('Y-m-d', strtotime($request->date)),
];
if ($request->attendance[$i]['type'] == 0) {
$student_ids[] = $request->attendance[$i]['student_id'];
}
$attendance = $this->attendance->builder()->where('class_section_id', $request->class_section_id)->where('student_id', $request->attendance[$i]['student_id'])->whereDate('date', $date)->first();
if ($attendance) {
$this->attendance->update($attendance->id, $attendanceData);
} else {
$this->attendance->create($attendanceData);
}
}
DB::commit();
if ($request->absent_notification) {
// Load students with user & guardian
$students = $this->student->builder()
->whereIn('user_id', $student_ids)
->with('user')
->get(['id', 'user_id', 'guardian_id']);
$dateFormatted = Carbon::parse($request->date)->format('F jS, Y');
$title = 'Absent';
$type = 'attendance';
$allPayloads = [];
foreach ($students as $student) {
// Skip if guardian missing
if (!$student->guardian_id) {
continue;
}
// Child name
$childName = trim($student->user->full_name ?? '');
if ($childName === '') {
$childName = "Student #{$student->id}";
}
$body = "Your child {$childName} is absent on {$dateFormatted}";
// Build payload
$payloads = buildPayloads(
[$student->guardian_id],
$title,
$body,
$type,
['child_id' => $student->id]
);
$allPayloads = array_merge($allPayloads, $payloads);
}
// Send in bulk
if (!empty($allPayloads)) {
sendBulk($allPayloads);
}
}
}
ResponseService::successResponse('Data Stored Successfully');
} catch (Throwable $e) {
if (
Str::contains($e->getMessage(), [
'does not exist',
'file_get_contents'
])
) {
DB::commit();
ResponseService::warningResponse("Data Stored successfully. But App push notification not send.");
} else {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
}
public function getStudentList(Request $request)
{
$validator = Validator::make($request->all(), ['class_section_id' => 'required|numeric',]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
$sessionYear = $this->cache->getDefaultSessionYear();
$sessionYearId = $sessionYear->id;
if ($request->student_id) {
$sql = $this->user->builder()->with([
'extra_student_details' => function ($q) use ($sessionYearId) {
$q->whereHas('form_field', function ($ff) {
$ff->whereNull('deleted_at');
})->with([
'form_field' => function ($ff) {
$ff->whereNull('deleted_at');
}
]);
}
])->whereHas('student', function ($q) use ($request, $sessionYearId) {
$q->where('class_section_id', $request->class_section_id)->where('session_year_id', $sessionYearId);
})->with('student.guardian', 'student.class_section.class', 'student.class_section.class.shift', 'student.class_section.section', 'student.class_section.medium')->withTrashed()->first();
} else {
// $subjectTeacherIds = SubjectTeacher::where('teacher_id', Auth::user()->id)
// ->where('class_section_id', $request->class_section_id)
// ->pluck('class_subject_id')->toArray();
$sql = $this->user->builder()->with([
'extra_student_details' => function ($q) {
$q->whereHas('form_field', function ($ff) {
$ff->whereNull('deleted_at');
})
->with([
'form_field' => function ($ff) {
$ff->whereNull('deleted_at');
}
]);
}
])->where('status', 1)->whereHas('student', function ($q) use ($request, $sessionYearId) {
$q->where('class_section_id', $request->class_section_id)->where('session_year_id', $sessionYearId);
})
->with('student.guardian', 'student.class_section', 'student.class_section.class', 'student.class_section.class.shift', 'student.class_section.section', 'student.class_section.medium')->has('student')->role('Student');
// ->whereHas('student_subject',function($q) use($subjectTeacherIds) {
// $q->whereIn('class_subject_id',$subjectTeacherIds);
// });
if ($request->status != 1) {
if ($request->status == 2) {
$sql->onlyTrashed();
} else if ($request->status == 0) {
$sql->withTrashed();
} else {
$sql->withTrashed();
}
}
if ($request->search) {
$sql->where(function ($q) use ($request) {
$q->when($request->search, function ($q) use ($request) {
$q->where('first_name', 'LIKE', "%$request->search%")
->orwhere('last_name', 'LIKE', "%$request->search%")
->orwhere('mobile', 'LIKE', "%$request->search%")
->orwhere('email', 'LIKE', "%$request->search%")
->orwhere('gender', 'LIKE', "%$request->search%")
->orWhereRaw('concat(first_name," ",last_name) like ?', "%$request->search%");
// ->where(function ($q) use ($request) {
// $q->when($request->session_year_id, function ($q) use ($request) {
// $q->whereHas('student', function ($q) use ($request) {
// $q->where('session_year_id', $request->session_year_id);
// });
// });
// });
});
});
}
if ($request->session_year_id) {
$sql = $sql->whereHas('student', function ($q) use ($request) {
$q->where('session_year_id', $request->session_year_id);
});
}
if (($request->paginate || $request->paginate != 0 || $request->paginate == null)) {
$sql = $sql->has('student')->orderBy('id')->paginate(10);
} else {
$sql = $sql->has('student')->orderBy('id')->get();
}
//
if ($request->exam_id) {
$validator = Validator::make($request->all(), ['class_subject_id' => 'required']);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
$exam = $this->exam->builder()->with('timetable:id,date,exam_id,start_time,end_time')->where('id', $request->exam_id)->first();
// Get Student ids according to Subject is elective or compulsory
// $classSubject = $this->classSubject->findById($request->class_subject_id);
$classSubject = $this->classSubject->builder()->where('id', $request->class_subject_id)->withTrashed()->first();
if ($classSubject->type == "Elective") {
$studentIds = $this->studentSubject->builder()->where(['class_section_id' => $request->class_section_id, 'class_subject_id' => $classSubject->id])->pluck('student_id');
} else {
$studentIds = $this->user->builder()->role('student')->whereHas('student', function ($query) use ($request) {
$query->where('class_section_id', $request->class_section_id);
})->pluck('id');
}
// Get Timetable Data
$timetable = $exam->timetable()->where('class_subject_id', $request->class_subject_id)->first();
// return $timetable;
// IF Timetable is empty then show error message
if (!$timetable) {
return response()->json(['error' => true, 'message' => trans('Exam Timetable Does not Exists')]);
}
// IF Exam status is not 2 that is exam not completed then show error message
if ($exam->exam_status != 2) {
ResponseService::errorResponse('Exam not completed yet');
}
$sessionYear = $this->cache->getDefaultSessionYear(); // Get Students Data on the basis of Student ids
$sql = $this->user->builder()->select('id', 'first_name', 'last_name', 'image')->role('Student')->with([
'extra_student_details' => function ($q) {
$q->whereHas('form_field', function ($ff) {
$ff->whereNull('deleted_at');
})->with([
'form_field' => function ($ff) {
$ff->whereNull('deleted_at');
}
]);
}
])->whereIn('id', $studentIds)->with([
'marks' => function ($query) use ($timetable) {
$query->where('exam_timetable_id', $timetable->id)->select('id', 'exam_timetable_id', 'student_id', 'obtained_marks');
}
])
->whereHas('student', function ($q) use ($sessionYear) {
$q->where('session_year_id', $sessionYear->id);
})->get();
}
}
ResponseService::successResponse("Student Details Fetched Successfully", $sql);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function getStudentDetails(Request $request)
{
$validator = Validator::make($request->all(), ['student_id' => 'required|numeric',]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
$student_data = $this->student->findById($request->student_id, ['user_id', 'class_section_id', 'guardian_id'], ['user', 'guardian']);
$student_total_present = $this->attendance->builder()->where('student_id', $student_data->user_id)->where('type', 1)->count();
$student_total_absent = $this->attendance->builder()->where('student_id', $student_data->user_id)->where('type', 0)->count();
$today_date_string = Carbon::now();
$today_date_string->toDateTimeString();
$today_date = date('Y-m-d', strtotime($today_date_string));
$student_today_attendance = $this->attendance->builder()->where('student_id', $student_data->user_id)->where('date', $today_date)->first();
if ($student_today_attendance) {
if ($student_today_attendance->type == 1) {
$today_attendance = 'Present';
} else {
$today_attendance = 'Absent';
}
} else {
$today_attendance = 'Not Taken';
}
ResponseService::successResponse("Student Details Fetched Successfully", null, [
'data' => $student_data,
'total_present' => $student_total_present,
'total_absent' => $student_total_absent,
'today_attendance' => $today_attendance ?? ''
]);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function getTeacherTimetable(Request $request)
{
ResponseService::noFeatureThenSendJson('Timetable Management');
try {
$timetable = $this->timetable->builder()->whereHas('subject_teacher', function ($q) {
$q->where('teacher_id', Auth::user()->id);
})->with('class_section.class.stream', 'class_section.class.shift', 'class_section.section', 'subject')->orderBy('start_time', 'ASC')->get();
ResponseService::successResponse("Timetable Fetched Successfully", $timetable);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function submitExamMarksBySubjects(Request $request)
{
ResponseService::noFeatureThenSendJson('Exam Management');
$validator = Validator::make($request->all(), [
'exam_id' => 'required|numeric',
'class_subject_id' => 'required|numeric',
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
$exam_published = $this->exam->builder()->where('id', $request->exam_id)->first();
if (isset($exam_published) && $exam_published->publish == 1) {
ResponseService::errorResponse('exam_published', null, config('constants.RESPONSE_CODE.EXAM_ALREADY_PUBLISHED'));
}
// Get raw date values to avoid accessor formatting issues
$startDateRaw = $exam_published->getRawOriginal('start_date');
$endDateRaw = $exam_published->getRawOriginal('end_date');
// Compare dates properly using Carbon
$startDate = Carbon::parse($startDateRaw);
$endDate = Carbon::parse($endDateRaw);
$today = Carbon::today();
if ($today->between($startDate, $endDate)) {
$exam_status = "1"; // Upcoming = 0 , On Going = 1 , Completed = 2
} elseif ($today->lt($startDate)) {
$exam_status = "0"; // Upcoming = 0 , On Going = 1 , Completed = 2
} else {
$exam_status = "2"; // Upcoming = 0 , On Going = 1 , Completed = 2
}
if ($exam_status != 2) {
ResponseService::errorResponse('exam_not_completed_yet', null, config('constants.RESPONSE_CODE.EXAM_ALREADY_PUBLISHED'));
} else {
$exam_timetable = $this->examTimetable->builder()->where('exam_id', $request->exam_id)->where('class_subject_id', $request->class_subject_id)->firstOrFail();
foreach ($request->marks_data as $marks) {
if ($marks['obtained_marks'] > $exam_timetable['total_marks']) {
ResponseService::errorResponse('The obtained marks that did not exceed the total marks');
}
$passing_marks = $exam_timetable->passing_marks;
if ($marks['obtained_marks'] >= $passing_marks) {
$status = 1;
} else {
$status = 0;
}
$marks_percentage = ($marks['obtained_marks'] / $exam_timetable['total_marks']) * 100;
$exam_grade = findExamGrade($marks_percentage);
if ($exam_grade == null) {
ResponseService::errorResponse('grades_data_does_not_exists', null, config('constants.RESPONSE_CODE.GRADES_NOT_FOUND'));
}
$exam_marks = $this->examMarks->builder()->where('exam_timetable_id', $exam_timetable->id)->where('class_subject_id', $request->class_subject_id)->where('student_id', $marks['student_id'])->first();
if ($exam_marks) {
$exam_data = [
'obtained_marks' => $marks['obtained_marks'],
'passing_status' => $status,
'grade' => $exam_grade,
'status' => $request->status ?? 0
];
$this->examMarks->update($exam_marks->id, $exam_data);
} else {
$exam_result_marks[] = array(
'exam_timetable_id' => $exam_timetable->id,
'student_id' => $marks['student_id'],
'class_subject_id' => $request->class_subject_id,
'obtained_marks' => $marks['obtained_marks'],
'passing_status' => $status,
'session_year_id' => $exam_timetable->session_year_id,
'grade' => $exam_grade,
'status' => $request->status ?? 0
);
}
}
if (isset($exam_result_marks)) {
$this->examMarks->createBulk($exam_result_marks);
}
DB::commit();
if ($request->status == 0) {
ResponseService::successResponse('Data stored in draft successfully');
}
ResponseService::successResponse('Data stored and published successfully');
}
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function submitExamMarksByStudent(Request $request)
{
ResponseService::noFeatureThenSendJson('Exam Management');
$validator = Validator::make($request->all(), [
'exam_id' => 'required|numeric',
'student_id' => 'required|numeric',
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
$exam_published = $this->exam->findById($request->exam_id);
if (isset($exam_published) && $exam_published->publish == 1) {
ResponseService::errorResponse('exam_published', null, config('constants.RESPONSE_CODE.EXAM_ALREADY_PUBLISHED'));
}
$currentTime = Carbon::now();
$current_date = date($currentTime->toDateString());
if ($current_date >= $exam_published->start_date && $current_date <= $exam_published->end_date) {
$exam_status = "1"; // Upcoming = 0 , On Going = 1 , Completed = 2
} elseif ($current_date < $exam_published->start_date) {
$exam_status = "0"; // Upcoming = 0 , On Going = 1 , Completed = 2
} else {
$exam_status = "2"; // Upcoming = 0 , On Going = 1 , Completed = 2
}
if ($exam_status != 2) {
ResponseService::errorResponse('exam_published', null, config('constants.RESPONSE_CODE.EXAM_NOT_COMPLETED'));
} else {
foreach ($request->marks_data as $marks) {
$exam_timetable = $this->examTimetable->builder()->where('exam_id', $request->exam_id)->where('class_subject_id', $marks['class_subject_id'])->firstOrFail();
if ($marks['obtained_marks'] > $exam_timetable['total_marks']) {
ResponseService::errorResponse('The obtained marks that did not exceed the total marks');
}
$passing_marks = $exam_timetable->passing_marks;
if ($marks['obtained_marks'] >= $passing_marks) {
$status = 1;
} else {
$status = 0;
}
$marks_percentage = ($marks['obtained_marks'] / $exam_timetable->total_marks) * 100;
$exam_grade = findExamGrade($marks_percentage);
if ($exam_grade == null) {
ResponseService::errorResponse('grades_data_does_not_exists', null, config('constants.RESPONSE_CODE.GRADES_NOT_FOUND'));
}
$exam_marks = $this->examMarks->builder()->where('exam_timetable_id', $exam_timetable->id)->where('class_subject_id', $marks['class_subject_id'])->where('student_id', $request->student_id)->first();
if ($exam_marks) {
$exam_data = [
'obtained_marks' => $marks['obtained_marks'],
'passing_status' => $status,
'grade' => $exam_grade,
'status' => $request->status ?? 0
];
$this->examMarks->update($exam_marks->id, $exam_data);
} else {
$exam_result_marks[] = array(
'exam_timetable_id' => $exam_timetable->id,
'student_id' => $request->student_id,
'class_subject_id' => $marks['class_subject_id'],
'obtained_marks' => $marks['obtained_marks'],
'passing_status' => $status,
'session_year_id' => $exam_timetable->session_year_id,
'grade' => $exam_grade,
'status' => $request->status ?? 0
);
}
}
if (isset($exam_result_marks)) {
$this->examMarks->createBulk($exam_result_marks);
}
DB::commit();
if ($request->status == 0) {
ResponseService::successResponse('Data stored in draft successfully');
}
ResponseService::successResponse('Data stored and published successfully');
}
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function GetStudentExamResult(Request $request)
{
ResponseService::noFeatureThenSendJson('Exam Management');
$validator = Validator::make($request->all(), ['student_id' => 'required|nullable']);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
$exam_marks_db = $this->exam->builder()->with([
'timetable.exam_marks' => function ($q) use ($request) {
$q->where('student_id', $request->student_id);
}
])->has('timetable.exam_marks')->with('timetable.class_subject.subject')->has('results')->with([
'results' => function ($q) use ($request) {
$q->where('student_id', $request->student_id)->with('class_section.class.stream', 'class_section.class.shift', 'class_section.section', 'class_section.medium');
}
])->get();
if (count($exam_marks_db)) {
foreach ($exam_marks_db as $data_db) {
$currentTime = Carbon::now();
$current_date = date($currentTime->toDateString());
if ($current_date >= $data_db->start_date && $current_date <= $data_db->end_date) {
$exam_status = "1"; // Upcoming = 0 , On Going = 1 , Completed = 2
} elseif ($current_date < $data_db->start_date) {
$exam_status = "0"; // Upcoming = 0 , On Going = 1 , Completed = 2
} else {
$exam_status = "2"; // Upcoming = 0 , On Going = 1 , Completed = 2
}
// check whether exam is completed or not
if ($exam_status == 2) {
$marks_array = array();
// check whether timetable exists or not
if (count($data_db->timetable)) {
foreach ($data_db->timetable as $timetable_db) {
$total_marks = $timetable_db->total_marks;
$exam_marks = array();
if (count($timetable_db->exam_marks)) {
foreach ($timetable_db->exam_marks as $marks_data) {
$exam_marks = array(
'marks_id' => $marks_data->id,
'subject_name' => $marks_data->class_subject->subject->name,
'subject_type' => $marks_data->class_subject->subject->type,
'total_marks' => $total_marks,
'obtained_marks' => $marks_data->obtained_marks,
'grade' => $marks_data->grade,
);
}
} else {
$exam_marks = (object) [];
}
$marks_array[] = array(
'subject_id' => $timetable_db->class_subject->subject_id,
'subject_name' => $timetable_db->class_subject->subject->name,
'subject_type' => $timetable_db->class_subject->subject->type,
'total_marks' => $total_marks,
'subject_code' => $timetable_db->class_subject->subject->code,
'marks' => $exam_marks
);
}
$exam_result = array();
if (count($data_db->results)) {
foreach ($data_db->results as $result_data) {
$exam_result = array(
'result_id' => $result_data->id,
'exam_id' => $result_data->exam_id,
'exam_name' => $data_db->name,
'class_name' => $result_data->class_section->full_name,
'student_name' => $result_data->user->first_name . ' ' . $result_data->user->last_name,
'exam_date' => $data_db->start_date,
'total_marks' => $result_data->total_marks,
'obtained_marks' => $result_data->obtained_marks,
'percentage' => $result_data->percentage,
'grade' => $result_data->grade,
'session_year' => $result_data->session_year->name,
);
}
} else {
$exam_result = (object) [];
}
$data[] = array(
'exam_id' => $data_db->id,
'exam_name' => $data_db->name,
'exam_date' => $data_db->start_date,
'marks_data' => $marks_array,
'result' => $exam_result
);
}
}
}
ResponseService::successResponse("Exam Marks Fetched Successfully", $data ?? []);
} else {
ResponseService::successResponse("Exam Marks Fetched Successfully", []);
}
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function GetStudentExamMarks(Request $request)
{
ResponseService::noFeatureThenSendJson('Exam Management');
$validator = Validator::make($request->all(), ['student_id' => 'required|nullable']);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
$sessionYear = $this->cache->getDefaultSessionYear();
$exam_marks_db = $this->exam->builder()->with([
'timetable.exam_marks' => function ($q) use ($request) {
$q->where('student_id', $request->student_id);
}
])->has('timetable.exam_marks')->with('timetable.class_subject')->where('session_year_id', $sessionYear->id)->get();
if (count($exam_marks_db)) {
foreach ($exam_marks_db as $data_db) {
$marks_array = array();
foreach ($data_db->timetable as $marks_db) {
$exam_marks = array();
if (count($marks_db->exam_marks)) {
foreach ($marks_db->exam_marks as $marks_data) {
$exam_marks = array(
'marks_id' => $marks_data->id,
'subject_name' => $marks_data->class_subject->subject->name,
'subject_type' => $marks_data->class_subject->subject->type,
'total_marks' => $marks_data->timetable->total_marks,
'obtained_marks' => $marks_data->obtained_marks,
'grade' => $marks_data->grade,
);
}
} else {
$exam_marks = [];
}
$marks_array[] = array(
'subject_id' => $marks_db->class_subject->subject_id,
'subject_name' => $marks_db->subject_with_name,
'marks' => $exam_marks
);
}
$data[] = array(
'exam_id' => $data_db->id,
'exam_name' => $marks_db->exam->name ?? '',
'marks_data' => $marks_array
);
}
ResponseService::successResponse("Exam Marks Fetched Successfully", $data ?? '');
} else {
ResponseService::successResponse("Exam Marks Fetched Successfully", []);
}
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function getExamList(Request $request)
{
ResponseService::noFeatureThenSendJson('Exam Management');
$validator = Validator::make($request->all(), [
'status' => 'in:0,1,2,3',
'publish' => 'nullable|in:0,1',
]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
$sessionYear = $this->cache->getDefaultSessionYear();
$schoolSettings = $this->cache->getSchoolSettings();
$sql = $this->exam->builder()->with('session_year', 'class')->select('id', 'name', 'description', 'class_id', 'start_date', 'end_date', 'session_year_id', 'publish')
->with([
'timetable.class_subject' => function ($q) {
$q->withTrashed();
}
]);
if (isset($request->publish)) {
$sql = $sql->where('publish', $request->publish);
}
if ($request->session_year_id) {
$sql = $sql->where('session_year_id', $request->session_year_id);
} else {
$sql = $sql->where('session_year_id', $sessionYear->id);
}
if ($request->class_section_id) {
$sql = $sql->whereHas('class.class_sections', function ($q) use ($request) {
$q->where('id', $request->class_section_id);
});
}
$subjectTeacherIds = SubjectTeacher::where('teacher_id', Auth::user()->id)->where('class_section_id', $request->class_section_id)->pluck('subject_id')->toArray();
$classTeacher = ClassTeacher::where('teacher_id', Auth::user()->id)->where('class_section_id', $request->class_section_id)->first();
if ($request->medium_id) {
$sql = $sql->whereHas('class', function ($q) use ($request) {
$q->where('medium_id', $request->medium_id);
});
}
$exam_data_db = $sql->orderBy('id', 'DESC')->get();
foreach ($exam_data_db as $data) {
$startDate = date('Y-m-d', strtotime($data->timetable->min('date')));
$endDate = date('Y-m-d', strtotime($data->timetable->max('date')));
if (!empty($data->start_date) || !empty($data->end_date)) {
$dataStartDate = Carbon::createFromFormat($schoolSettings['date_format'], $data->start_date)->format('Y-m-d');
$dataEndDate = Carbon::createFromFormat($schoolSettings['date_format'], $data->end_date)->format('Y-m-d');
} else {
$dataStartDate = null;
$dataEndDate = null;
}
$currentTime = Carbon::now();
$current_date = date($currentTime->toDateString());
$current_time = Carbon::now();
// 0- Upcoming, 1-On Going, 2-Completed, 3-All Details
$exam_status = "3";
if ($current_date == $dataStartDate && $current_date == $dataEndDate) {
if (count($data->timetable)) {
// $exam_end_time = $startDate;
// $exam_start_time = $endDate;
// if ($current_time->lt($exam_start_time)) {
// $exam_status = "1";
// } elseif ($current_time->gt($exam_end_time)) {
// $exam_status = "2";
// } else {
// $exam_status = "0";
// }
$exam_status = $data->exam_status;
}
} else {
if ($current_date >= $dataStartDate && $current_date <= $dataEndDate) {
$exam_status = "1";
} else if ($current_date < $dataStartDate) {
$exam_status = "0";
} else if ($current_date >= $dataEndDate) {
$exam_status = "2";
} else {
$exam_status = null;
}
}
$timetable_data = array();
if (count($data->timetable)) {
foreach ($data->timetable as $key => $timetable) {
if ($classTeacher) {
$subject = [
'id' => $timetable->class_subject->subject->id,
'name' => $timetable->class_subject->subject->name,
'type' => $timetable->class_subject->subject->type,
];
$class_subject = [
'id' => $timetable->class_subject->id,
'class_id' => $timetable->class_subject->id,
'subject_id' => $timetable->class_subject->id,
'subject' => $subject
];
$timetable_data[] = [
'id' => $timetable->id,
'total_marks' => $timetable->total_marks,
'passing_marks' => $timetable->passing_marks,
'date' => $timetable->date,
'start_time' => $timetable->start_time,
'end_time' => $timetable->end_time,
'subject_name' => $timetable->subject_with_name,
'class_subject' => $class_subject
];
} else if (in_array($timetable->class_subject->subject_id, $subjectTeacherIds)) {
$subject = [
'id' => $timetable->class_subject->subject->id,
'name' => $timetable->class_subject->subject->name,
'type' => $timetable->class_subject->subject->type,
];
$class_subject = [
'id' => $timetable->class_subject->id,
'class_id' => $timetable->class_subject->id,
'subject_id' => $timetable->class_subject->id,
'subject' => $subject
];
$timetable_data[] = [
'id' => $timetable->id,
'total_marks' => $timetable->total_marks,
'passing_marks' => $timetable->passing_marks,
'date' => $timetable->date,
'start_time' => $timetable->start_time,
'end_time' => $timetable->end_time,
'subject_name' => $timetable->subject_with_name,
'class_subject' => $class_subject
];
}
}
}
if (isset($request->status) && $request->status != 3) {
if ($request->status == 0 && $exam_status == 0) {
$exam_data[] = array(
'id' => $data->id,
'name' => $data->name,
'description' => $data->description,
'publish' => $data->publish,
'session_year' => $data->session_year->name,
'exam_starting_date' => $data->start_date,
'exam_ending_date' => $data->end_date,
'exam_status' => $exam_status,
'class_name' => $data->class_name,
'timetable' => $timetable_data,
);
} else if ($request->status == 1) {
if ($exam_status == 1) {
$exam_data[] = array(
'id' => $data->id,
'name' => $data->name,
'description' => $data->description,
'publish' => $data->publish,
'session_year' => $data->session_year->name,
'exam_starting_date' => $data->start_date,
'exam_ending_date' => $data->end_date,
'exam_status' => $exam_status,
'class_name' => $data->class_name,
'timetable' => $timetable_data,
);
}
} else if ($exam_status == 2 && $request->status == 2) {
$exam_data[] = array(
'id' => $data->id,
'name' => $data->name,
'description' => $data->description,
'publish' => $data->publish,
'session_year' => $data->session_year->name,
'exam_starting_date' => $data->start_date,
'exam_ending_date' => $data->end_date,
'exam_status' => $exam_status,
'class_name' => $data->class_name,
'timetable' => $timetable_data,
);
} else if ($request->status == 3 && count($data->timetable) && $data->exam_status == 3) {
$exam_data[] = array(
'id' => $data->id,
'name' => $data->name,
'description' => $data->description,
'publish' => $data->publish,
'session_year' => $data->session_year->name,
'exam_starting_date' => $data->start_date,
'exam_ending_date' => $data->end_date,
'exam_status' => $exam_status,
'class_name' => $data->class_name,
'timetable' => $timetable_data,
);
}
} else {
$exam_data[] = array(
'id' => $data->id,
'name' => $data->name,
'description' => $data->description,
'publish' => $data->publish,
'session_year' => $data->session_year->name,
'exam_starting_date' => $data->start_date,
'exam_ending_date' => $data->end_date,
'exam_status' => $exam_status,
'class_name' => $data->class_name,
'timetable' => $timetable_data,
);
}
// $exam_data['timetable'] = $timetable_data;
}
ResponseService::successResponse('Data Fetched Successfully', $exam_data ?? []);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function getExamDetails(Request $request)
{
ResponseService::noFeatureThenSendJson('Exam Management');
$validator = Validator::make($request->all(), ['exam_id' => 'required|nullable',]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
$exam_data = $this->exam->builder()->select('id', 'name', 'description', 'session_year_id', 'publish')->with('timetable.class_subject.subject')->where('id', $request->exam_id)->first();
ResponseService::successResponse('Data Fetched Successfully', $exam_data);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function getClassDetail(Request $request)
{
try {
$defaultSessionYear = $this->cache->getDefaultSessionYear();
$sql = $this->classSection->builder()->with('class.stream', 'class.shift', 'medium', 'section')
->whereHas('class_teachers', function ($q) use ($defaultSessionYear) {
$q->where('session_year_id', $defaultSessionYear->id);
})
->whereHas('subject_teachers', function ($q) use ($defaultSessionYear) {
$q->where('session_year_id', $defaultSessionYear->id);
})
->with(['class_teachers' => function ($q) use ($defaultSessionYear) {
$q->where('session_year_id', $defaultSessionYear->id)->with('teacher:id,first_name,last_name,mobile');
}])
->with(['subject_teachers' => function ($q) use ($defaultSessionYear) {
$q->where('session_year_id', $defaultSessionYear->id)->with('subject:id,name,code,type')->with('teacher:id,first_name,last_name,mobile');
}]);
if ($request->class_id) {
$sql = $sql->where('class_id', $request->class_id);
}
// $sql = $this->classSection->builder()->with(['class.stream', 'medium', 'section', 'class_teachers.teacher:id,first_name,last_name', 'subject_teachers'=> function ($q) {
// $q->with('teacher:id,first_name,last_name')
// ->has('class_subject')->with(['class_subject' => function($q) {
// $q->whereNull('deleted_at')->with('semester');
// }])
// ->with('subject')->owner();
// }]);
// if ($request->class_id) {
// $sql = $sql->where('class_id', $request->class_id);
// }
$sql = $sql->orderBy('id', 'DESC')->get();
ResponseService::successResponse('Data Fetched Successfully', $sql);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
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;
}
// student diary category
public function getStudentDiaryCategories(Request $request)
{
try {
$diary_categories = $this->diaryCategory->builder();
if ($request->type) {
$diary_categories = $diary_categories->where('type', $request->type);
}
if ($request->search) {
$diary_categories = $diary_categories->where('name', 'like', '%' . $request->search . '%');
}
$diary_categories = $diary_categories->orderBy('id', 'DESC')->get();
ResponseService::successResponse('Data Fetched Successfully', $diary_categories);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function createStudentDiaryCategory(Request $request)
{
ResponseService::noPermissionThenSendJson('student-diary-create');
$validator = Validator::make($request->all(), [
'name' => 'required',
'type' => 'required|in:positive,negative',
], [
'name.required' => 'Name is required.',
'type.required' => 'Please select Type',
]);
if ($validator->fails()) {
ResponseService::errorResponse($validator->errors()->first());
}
try {
$diary_category = $this->diaryCategory->create($request->all());
ResponseService::successResponse('Data Created Successfully', $diary_category);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function updateStudentDiaryCategory(Request $request)
{
ResponseService::noPermissionThenSendJson('student-diary-edit');
$validator = Validator::make($request->all(), [
'diary_category_id' => 'required',
'name' => 'required',
'type' => 'required|in:positive,negative',
], [
'diary_category_id.required' => 'Diary Category id is required',
'name.required' => 'Name is required',
'type.required' => 'Please select Type',
]);
if ($validator->fails()) {
ResponseService::errorResponse($validator->errors()->first());
}
$data = [
'name' => $request->name,
'type' => $request->type,
];
try {
$diary_category = $this->diaryCategory->update($request->diary_category_id, $data);
ResponseService::successResponse('Data Updated Successfully', $diary_category);
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function deleteStudentDiaryCategory(Request $request)
{
ResponseService::noPermissionThenSendJson('student-diary-delete');
$validator = Validator::make($request->all(), [
'diary_category_id' => 'required',
], [
'diary_category_id.required' => 'Diary Category id is required',
]);
if ($validator->fails()) {
ResponseService::errorResponse($validator->errors()->first());
}
try {
DB::beginTransaction();
$existing_data = $this->diary->builder()->where('diary_category_id', $request->diary_category_id)->get();
if (count($existing_data) > 0) {
return ResponseService::errorResponse('This Category is already used in Diary. You can not delete this.');
}
$this->diaryCategory->deleteById($request->diary_category_id);
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function restoreStudentDiaryCategory(Request $request)
{
ResponseService::noPermissionThenSendJson('student-diary-delete');
$validator = Validator::make($request->all(), [
'diary_category_id' => 'required',
], [
'diary_category_id.required' => 'Diary Category id is required',
]);
if ($validator->fails()) {
ResponseService::errorResponse($validator->errors()->first());
}
try {
DB::beginTransaction();
$this->diaryCategory->restoreById($request->diary_category_id);
DB::commit();
ResponseService::successResponse('Data Restored Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function trashStudentDiaryCategory(Request $request)
{
ResponseService::noPermissionThenSendJson('student-diary-delete');
$validator = Validator::make($request->all(), [
'diary_category_id' => 'required',
], [
'diary_category_id.required' => 'Diary Category id is required',
]);
if ($validator->fails()) {
ResponseService::errorResponse($validator->errors()->first());
}
try {
DB::beginTransaction();
$existing_data = $this->diary->builder()->where('diary_category_id', $request->diary_category_id)->get();
if (count($existing_data) > 0) {
return ResponseService::errorResponse('This Category is already used in Diary. You can not delete this.');
}
$this->diaryCategory->findTrashedById($request->diary_category_id)->forceDelete();
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
// Student Diaries
public function getStudentDiaries(Request $request)
{
ResponseService::noPermissionThenSendJson('student-diary-list');
try {
$sortType = strtolower($request->get('sort', 'new'));
$users = $this->user->builder()
->select('id', 'first_name', 'last_name', 'mobile', 'email', 'image', 'dob')
->whereHas('roles', function ($q) {
$q->where('name', 'Student');
});
// Search
if ($request->search) {
$users->where(function ($q) use ($request) {
$q->where('first_name', 'like', '%' . $request->search . '%')
->orWhere('last_name', 'like', '%' . $request->search . '%')
->orWhere('mobile', 'like', '%' . $request->search . '%')
->orWhere('email', 'like', '%' . $request->search . '%');
});
}
if ($request->student_id) {
$users->where('id', $request->student_id);
}
$diaryStudentFilter = function ($q) use ($request, $sortType) {
// Category filter
if ($request->category_id) {
$q->whereHas('diary.diary_category', function ($q) use ($request) {
$q->where('diary_category_id', $request->category_id);
});
}
// Positive / Negative filter
if ($sortType === 'positive') {
$q->whereHas('diary.diary_category', function ($q) {
$q->where('type', 'positive');
});
}
if ($sortType === 'negative') {
$q->whereHas('diary.diary_category', function ($q) {
$q->where('type', 'negative');
});
}
// Subject filter
if ($request->subject_id) {
$q->whereHas('diary.subject', function ($q) use ($request) {
$q->where('id', $request->subject_id);
});
}
// Sorting
if ($sortType === 'new') {
$q->orderBy('created_at', 'DESC');
}
if ($sortType === 'old') {
$q->orderBy('created_at', 'ASC');
}
};
$users->whereHas('diary_student', $diaryStudentFilter);
$users->with([
'diary_student' => $diaryStudentFilter,
'diary_student.diary.subject',
'diary_student.diary.diary_category'
]);
$sql = $users->orderBy('id', 'DESC')->paginate(10);
ResponseService::successResponse("Student Diaries Fetched Successfully", $sql);
} catch (\Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function createStudentDiary(Request $request)
{
ResponseService::noPermissionThenSendJson('student-diary-create');
$validator = Validator::make($request->all(), [
'diary_category_id' => 'required',
'title' => 'required|string|max:255',
'student_class_section_map' => 'required|not_in:0,null',
'date' => 'required|date',
]);
if ($validator->fails()) {
ResponseService::errorResponse($validator->errors()->first());
}
try {
DB::beginTransaction();
$sessionYear = $this->cache->getDefaultSessionYear();
$data = [
'title' => $request->title,
'diary_category_id' => $request->diary_category_id,
'user_id' => Auth::id(),
'subject_id' => $request->subject_id,
'session_year_id' => $sessionYear->id,
'description' => $request->description,
'date' => Carbon::parse($request->date)->format('Y-m-d'),
];
$diary = $this->diary->create($data);
$studentsClassSections = json_decode($request->student_class_section_map, true);
$notifyUser = [];
foreach ($studentsClassSections as $student_id => $class_section_id) {
$this->diaryStudent->create([
'diary_id' => $diary->id,
'student_id' => $student_id,
'class_section_id' => $class_section_id,
]);
$notifyUser[] = [
'student_id' => $student_id,
'class_section_id' => $class_section_id
];
}
/* =================================================
| 🔒 NOTIFICATION FILTER (ONLY CHANGE)
=================================================*/
$finalNotifyStudents = [];
if (!$request->subject_id) {
// General diary → notify all
$finalNotifyStudents = collect($notifyUser)->pluck('student_id')->toArray();
} else {
foreach ($notifyUser as $entry) {
$studentId = $entry['student_id'];
$classSectionId = $entry['class_section_id'];
// Resolve class_id from section
$classId = DB::table('class_sections')
->where('id', $classSectionId)
->value('class_id');
if (!$classId) {
continue;
}
// Resolve class_subject for this class
$classSubject = DB::table('class_subjects')
->where('class_id', $classId)
->where('subject_id', $request->subject_id)
->first();
// Subject not available in this section → skip notification
if (!$classSubject) {
continue;
}
// Compulsory → notify
if ($classSubject->type === 'Compulsory') {
$finalNotifyStudents[] = $studentId;
continue;
}
// Elective → notify only if assigned
$isAssigned = DB::table('student_subjects')
->where('student_id', $studentId)
->where('class_subject_id', $classSubject->id)
->exists();
if ($isAssigned) {
$finalNotifyStudents[] = $studentId;
}
}
}
// Guardian notifications
$guardianIds = $this->student
->builder()
->whereIn('user_id', $finalNotifyStudents)
->pluck('guardian_id')
->toArray();
$finalNotifyUsers = array_unique(array_merge($finalNotifyStudents, $guardianIds));
DB::commit();
if (!empty($finalNotifyUsers)) {
send_notification(
$finalNotifyUsers,
'New Diary Note Received',
$request->title,
'Diary',
[]
);
}
ResponseService::successResponse('Diary Added Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function deleteStudentDiary(Request $request)
{
ResponseService::noPermissionThenSendJson('student-diary-delete');
$validator = Validator::make($request->all(), ['diary_id' => 'required|numeric',]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
$this->diary->findTrashedById($request->diary_id)->forceDelete();
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
public function removeStudent(Request $request)
{
ResponseService::noPermissionThenRedirect('student-diary-delete');
$diaryId = $request->diary_id;
$id = $request->id;
$studentCount = $this->diaryStudent->builder()
->where(['diary_id' => $diaryId])->count();
if ($studentCount == 1 || $studentCount < 2) {
ResponseService::noPermissionThenSendJson('student-diary-delete');
$validator = Validator::make($request->all(), ['diary_id' => 'required|numeric',]);
if ($validator->fails()) {
ResponseService::validationError($validator->errors()->first());
}
try {
DB::beginTransaction();
$this->diary->findTrashedById($request->diary_id)->forceDelete();
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
} else {
try {
DB::beginTransaction();
$this->diaryStudent->findTrashedById($id)->forceDelete();
DB::commit();
ResponseService::successResponse('Student Removed Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e);
ResponseService::errorResponse();
}
}
}
}