File "AnnouncementController.php"
Full Path: /home/trinadezambia/public_html/admin_panel/app/Http/Controllers/AnnouncementController.php
File size: 30.01 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace App\Http\Controllers;
use App\Repositories\Announcement\AnnouncementInterface;
use App\Repositories\AnnouncementClass\AnnouncementClassInterface;
use App\Repositories\ClassSection\ClassSectionInterface;
use App\Repositories\ClassSubject\ClassSubjectInterface;
use App\Repositories\Files\FilesInterface;
use App\Repositories\Student\StudentInterface;
use App\Repositories\StudentSubject\StudentSubjectInterface;
use App\Repositories\SubjectTeacher\SubjectTeacherInterface;
use App\Rules\MaxFileSize;
use App\Services\BootstrapTableService;
use App\Services\CachingService;
use App\Services\ResponseService;
use App\Services\GeneralFunctionService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Throwable;
use TypeError;
use App\Repositories\SessionYear\SessionYearInterface;
use App\Jobs\BulkNotificationsJobv3_0_0;
use App\Models\Subject;
class AnnouncementController extends Controller
{
private AnnouncementInterface $announcement;
private ClassSectionInterface $classSection;
private SubjectTeacherInterface $subjectTeacher;
private StudentInterface $student;
private FilesInterface $files;
private StudentSubjectInterface $studentSubject;
private ClassSubjectInterface $classSubject;
private CachingService $cache;
private AnnouncementClassInterface $announcementClass;
private SessionYearInterface $sessionYear;
public function __construct(AnnouncementInterface $announcement, ClassSectionInterface $classSection, SubjectTeacherInterface $subjectTeacher, StudentInterface $student, FilesInterface $files, StudentSubjectInterface $studentSubject, ClassSubjectInterface $classSubject, CachingService $cachingService, AnnouncementClassInterface $announcementClass, SessionYearInterface $sessionYear)
{
$this->announcement = $announcement;
$this->classSection = $classSection;
$this->subjectTeacher = $subjectTeacher;
$this->student = $student;
$this->files = $files;
$this->studentSubject = $studentSubject;
$this->classSubject = $classSubject;
$this->cache = $cachingService;
$this->announcementClass = $announcementClass;
$this->sessionYear = $sessionYear;
}
public function index()
{
ResponseService::noFeatureThenRedirect('Announcement Management');
ResponseService::noPermissionThenRedirect('announcement-list');
$sessionYearId = $this->cache->getSessionYear()->id;
if (Auth::user()->hasRole('Teacher')) {
$teacherId = Auth::id() ?? null;
$class_section = $this->classSection->builder()
->whereHas('subject_teachers', function ($query) use ($teacherId, $sessionYearId) {
$query->where('session_year_id', $sessionYearId)->where('teacher_id', $teacherId);
})
->with('class', 'class.stream', 'class.shift', 'section', 'medium')
->get();
// dd($class_section);
} else {
$class_section = $this->classSection->builder()->with('class', 'class.stream', 'class.shift', 'section', 'medium')->get(); // Get the Class Section of Teacher
}
$subjectTeachers = $this->subjectTeacher->builder()->where('session_year_id', $sessionYearId)->with(['subject:id,name,type'])->get();
$file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit');
return view('announcement.index', compact('class_section', 'subjectTeachers', 'file_upload_size_limit'));
}
public function store(Request $request)
{
ResponseService::noFeatureThenSendJson('Announcement Management');
ResponseService::noPermissionThenSendJson('announcement-create');
$file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit');
$request->validate([
'title' => 'required',
'class_section_id' => 'required|array',
'subject_id' => Auth::user() && Auth::user()->hasRole('Teacher')
? 'required|exists:subjects,id'
: 'nullable|exists:subjects,id',
'file' => 'nullable|array',
'file.*' => [
'mimes:jpeg,png,jpg,gif,svg,webp,pdf,doc,docx,xml',
new MaxFileSize($file_upload_size_limit)
],
'add_url' => $request->checkbox_add_url ? 'required' : 'nullable',
], [
'class_section_id.required' => trans('the_class_section_field_id_required'),
'file.*' => trans(
'The file Uploaded must be less than :file_upload_size_limit MB.',
['file_upload_size_limit' => $file_upload_size_limit]
),
]);
try {
DB::beginTransaction();
/* ================= CORE LOGIC (UNCHANGED) ================= */
$sessionYear = $this->cache->getSessionYear();
$section_ids = is_array($request->class_section_id)
? $request->class_section_id
: [$request->class_section_id];
$announcement = $this->announcement->create([
'title' => $request->title,
'description' => $request->description,
'session_year_id' => $sessionYear->id,
'school_id' => Auth::user()->school_id,
]);
/* ================= ANNOUNCEMENT CLASS (UNCHANGED) ================= */
foreach ($section_ids as $section_id) {
$classSection = $this->classSection
->builder()
->where('id', $section_id)
->with('class')
->first();
$classSubject = null;
if (!empty($request->subject_id)) {
$classSubject = $this->classSubject
->builder()
->where('class_id', $classSection->class->id)
->where('subject_id', $request->subject_id)
->first();
}
$announcementClassData = [
'announcement_id' => $announcement->id,
'class_section_id' => $section_id,
];
if ($classSubject) {
$announcementClassData['class_subject_id'] = $classSubject->id;
}
$announcementClass = $this->announcementClass->create($announcementClassData);
}
/* ================= FILE / URL (UNCHANGED) ================= */
if ($request->hasFile('file')) {
$fileData = [];
$assoc = $this->files->model()->modal()->associate($announcement);
foreach ($request->file as $file_upload) {
$fileData[] = [
'modal_type' => $assoc->modal_type,
'modal_id' => $assoc->modal_id,
'file_name' => $file_upload->getClientOriginalName(),
'type' => 1,
'file_url' => $file_upload
];
}
$this->files->createBulk($fileData);
}
if ($request->add_url) {
$urlData = [];
$urls = is_array($request->add_url) ? $request->add_url : [$request->add_url];
foreach ($urls as $url) {
$assoc = $this->files->model()->modal()->associate($announcement);
$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();
/* ================= FIXED NOTIFICATIONS (CORE + ELECTIVE SAFE) ================= */
$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()->with('user')
->whereIn('class_section_id', array_keys($sectionMap));
$coreSectionIds = [];
$electivePairs = [];
foreach ($sectionMap as $sectionId => $classSubjectId) {
if (empty($classSubjectId)) {
// No subject → treat as core
$coreSectionIds[] = $sectionId;
continue;
}
$classSubject = $this->classSubject->findById($classSubjectId, ['type']);
if ($classSubject->type === 'Elective') {
$electivePairs[] = [
'class_section_id' => $sectionId,
'class_subject_id' => $classSubjectId,
];
} else {
$coreSectionIds[] = $sectionId;
}
}
$studentsQuery->where(function ($q) use ($coreSectionIds, $electivePairs) {
if (!empty($coreSectionIds)) {
$q->whereIn('class_section_id', $coreSectionIds);
}
if (!empty($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(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 = null;
if (!empty($request->subject_id)) {
$subjectName = Subject::query()->where('id', $request->subject_id)
->first();
}
$title = $subjectName
? trans('New announcement in') . ' ' . $subjectName->name_with_type
: trans('New announcement');
$body = $request->title;
$type = 'Class Section';
$userIds = [];
$studentMap = []; // user_id => student_id
$guardianMap = []; // guardian_user_id => student_id
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));
if (!empty($userIds)) {
BulkNotificationsJobv3_0_0::dispatch(
auth()->user()->school_id,
$userIds,
$title,
$body,
'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) {
$notificationStatus = app(GeneralFunctionService::class)
->wrongNotificationSetup($e);
if ($notificationStatus) {
DB::rollBack();
ResponseService::logErrorResponse(
$e,
"Announcement Controller -> Store Method"
);
ResponseService::errorResponse();
} else {
DB::commit();
ResponseService::warningResponse(
"Data Stored successfully. But App push notification not send."
);
}
}
}
public function update($id, Request $request)
{
ResponseService::noFeatureThenSendJson('Announcement Management');
ResponseService::noPermissionThenSendJson('announcement-edit');
$file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit');
$validator = Validator::make($request->all(), [
'title' => 'required',
'class_section_id' => 'required',
'file' => 'nullable|array',
'file.*' => [
'mimes:jpeg,png,jpg,gif,svg,webp,pdf,doc,docx,xml',
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::errorResponse($validator->errors()->first());
}
try {
DB::beginTransaction();
/* ================= CORE UPDATE (UNCHANGED) ================= */
$sessionYear = $this->cache->getSessionYear();
$announcement = $this->announcement->update($id, [
'title' => $request->title,
'description' => $request->description,
'session_year_id' => $sessionYear->id,
]);
$oldClassSection = $this->announcement
->findById($id)
->announcement_class
->pluck('class_section_id')
->toArray();
$announcementClassData = [];
$customData = [];
$subjectName = null;
$sectionIds = is_array($request->class_section_id)
? $request->class_section_id
: [$request->class_section_id];
/* ================= SUBJECT / CLASS HANDLING (UNCHANGED) ================= */
if (!empty($request->subject_id)) {
$teacherId = Auth::user()->id;
foreach ($sectionIds as $class_section) {
$classSection = $this->classSection
->builder()
->where('id', $class_section)
->with('class')
->first();
$classSubjects = $this->classSubject
->builder()
->where('class_id', $classSection->class->id)
->where('subject_id', $request->subject_id)
->first();
$subjectTeacherData = $this->subjectTeacher
->builder()
->whereIn('class_section_id', $sectionIds)
->where([
'teacher_id' => $teacherId,
'class_subject_id' => $classSubjects->id
])
->first();
$subjectName = ($subjectTeacherData->subject->name ?? null) . ' - ' . ($subjectTeacherData->subject->type ?? null);
$customData = [
'class_subject_id' => $classSubjects->id ?? null,
'subject_id' => $classSubjects->subject_id ?? null,
'subject_name' => $subjectName ?? null,
];
$announcementClassData[] = [
'announcement_id' => $announcement->id,
'class_section_id' => $class_section,
'class_subject_id' => $classSubjects->id
];
if (($key = array_search($class_section, $oldClassSection)) !== false) {
unset($oldClassSection[$key]);
}
}
$title = trans('Updated announcement in') . $subjectName;
} else {
foreach ($sectionIds as $class_section) {
$announcementClassData[] = [
'announcement_id' => $announcement->id,
'class_section_id' => $class_section
];
if (($key = array_search($class_section, $oldClassSection)) !== false) {
unset($oldClassSection[$key]);
}
}
$customData = [];
$title = trans('Updated announcement');
}
$this->announcementClass->upsert(
$announcementClassData,
['announcement_id', 'class_section_id', 'school_id'],
['announcement_id', 'class_section_id', 'school_id', 'class_subject_id']
);
$this->announcementClass
->builder()
->where('announcement_id', $id)
->whereIn('class_section_id', $oldClassSection)
->delete();
/* ================= FILE / URL (UNCHANGED) ================= */
if ($request->hasFile('file')) {
$fileData = [];
$assoc = $this->files->model()->modal()->associate($announcement);
foreach ($request->file as $file_upload) {
$fileData[] = [
'modal_type' => $assoc->modal_type,
'modal_id' => $assoc->modal_id,
'file_name' => $file_upload->getClientOriginalName(),
'type' => 1,
'file_url' => $file_upload
];
}
$this->files->createBulk($fileData);
}
if ($request->add_url) {
$assoc = $this->files->model()->modal()->associate($announcement);
$this->files->upsert([
[
'id' => $request->add_url_id ?? null,
'modal_type' => $assoc->modal_type,
'modal_id' => $assoc->modal_id,
'file_name' => '',
'type' => 4,
'file_url' => $request->add_url,
]
], ['id'], ['id', 'modal_type', 'modal_id', 'file_name', 'type', 'file_url']);
} else {
if ($request->add_url_id) {
$this->files->deleteById($request->add_url_id);
}
}
DB::commit();
/* ================= FIXED NOTIFICATIONS (CORE + ELECTIVE SAFE) ================= */
$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()->with('user')
->whereIn('class_section_id', array_keys($sectionMap));
$coreSections = [];
$electivePairs = [];
$classSubject = null;
foreach ($sectionMap as $sectionId => $classSubjectId) {
if (empty($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 (!empty($coreSections)) {
$q->whereIn('class_section_id', $coreSections);
}
if (!empty($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(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'
]);
$body = $request->title;
$type = 'Class Section';
$userIds = [];
$studentMap = []; // user_id => student_id
$guardianMap = []; // guardian_user_id => [student_id, student_id...]
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;
if (!isset($guardianMap[$student->guardian_id])) {
$guardianMap[$student->guardian_id] = [];
}
$guardianMap[$student->guardian_id][] = $student->id;
}
}
$userIds = array_values(array_unique($userIds));
if (!empty($userIds)) {
BulkNotificationsJobv3_0_0::dispatch(
Auth::user()->school_id,
$userIds,
$title,
$body,
'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) {
$notificationStatus = app(GeneralFunctionService::class)
->wrongNotificationSetup($e);
if ($notificationStatus) {
DB::rollBack();
ResponseService::logErrorResponse(
$e,
"Announcement Controller -> Update Method"
);
ResponseService::errorResponse();
} else {
DB::commit();
ResponseService::warningResponse(
"Data Stored successfully. But App push notification not send."
);
}
}
}
public function show()
{
ResponseService::noFeatureThenSendJson('Announcement Management');
ResponseService::noPermissionThenSendJson('announcement-list');
$offset = request('offset', 0);
$limit = request('limit', 10);
$sort = request('sort', 'id');
$order = request('order', 'ASC');
$search = request('search');
$class_section_id = request('class_section_id');
$subject_id = request('subject_id');
$session_year_id = $this->cache->getSessionYear()->id;
$sql = $this->announcement->builder()->with('file', 'announcement_class.class_section.class.stream', 'announcement_class.class_section.class.shift', 'announcement_class.class_section.section', 'announcement_class.class_section.medium', 'announcement_class.class_subject.subject')
->where('session_year_id', $session_year_id)
->where(function ($q) use ($search) {
$q->when($search, function ($query) use ($search) {
$query->where('id', 'LIKE', "%$search%")
->orwhere('title', 'LIKE', "%$search%")
->orwhere('description', 'LIKE', "%$search%");
});
})->whereHas('announcement_class', function ($q) {
$q->whereNull('class_subject_id')
->orWhereHas('class_subject', function ($q) {
$q->whereNull('deleted_at');
});
});
// Filter by class section if provided
if ($class_section_id) {
$sql->whereHas('announcement_class', function ($q) use ($class_section_id) {
$q->where('class_section_id', $class_section_id);
});
}
// Filter by subject if provided
if ($subject_id) {
$sql->whereHas('announcement_class.class_subject', function ($q) use ($subject_id) {
$q->where('subject_id', $subject_id);
});
}
$total = $sql->count();
if ($offset >= $total && $total > 0) {
$lastPage = floor(($total - 1) / $limit) * $limit; // calculate last page offset
$offset = $lastPage;
}
$sql->orderBy($sort, $order)->skip($offset)->take($limit);
$res = $sql->get();
$bulkData = array();
$bulkData['total'] = $total;
$rows = array();
$no = 1;
$user = Auth::user();
foreach ($res as $row) {
$operate = '';
$class_section = array();
$class_section_id = array();
$class_subject_id = '';
// $class->roles->id == $user->id
foreach ($row->announcement_class as $index => $class) {
if ($user->hasRole('School Admin') || !$user->hasRole('Teacher')) {
$operate = BootstrapTableService::editButton(route('announcement.update', $row->id));
$operate .= BootstrapTableService::deleteButton(route('announcement.destroy', $row->id));
}
if (($user->hasRole('School Admin') && ($class->class_subject_id == "" || $class->class_subject_id)) || ($user->hasRole('Teacher') && $class->class_subject_id)) {
//Show Edit and Soft Delete Buttons
$operate = BootstrapTableService::editButton(route('announcement.update', $row->id));
$operate .= BootstrapTableService::deleteButton(route('announcement.destroy', $row->id));
}
$class_section_id[] = $class->class_section_id;
// Add teacher subject
if ($class->class_subject_id) {
$class_subject_id = $class->class_subject_id;
$class_section[] = $class->class_section->full_name . ' #' . $class->class_subject->subject->name;
} else {
$class_section[] = $class->class_section->full_name;
}
}
$tempRow = $row->toArray();
$tempRow['id'] = $row->id;
$tempRow['no'] = $no++;
$tempRow['class_subject_id'] = $class_subject_id;
$tempRow['class_sections'] = $class_section_id;
$tempRow['assignto'] = $class_section;
$tempRow['operate'] = $operate;
$rows[] = $tempRow;
}
$bulkData['rows'] = $rows;
return response()->json($bulkData);
}
public function destroy($id)
{
ResponseService::noFeatureThenSendJson('Announcement Management');
ResponseService::noPermissionThenSendJson('announcement-delete');
try {
DB::beginTransaction();
$this->announcement->deleteById($id);
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e, "Announcement Controller -> Destroy Method");
ResponseService::errorResponse();
}
}
public function fileDelete($id)
{
ResponseService::noFeatureThenSendJson('Announcement Management');
ResponseService::noPermissionThenSendJson('announcement-delete');
try {
DB::beginTransaction();
// Find the Data by FindByID
$file = $this->files->findById($id);
// Delete the file data
$file->delete();
DB::commit();
ResponseService::successResponse('Data Deleted Successfully');
} catch (Throwable $e) {
DB::rollBack();
ResponseService::logErrorResponse($e, "Announcement Controller -> fileDelete Method");
ResponseService::errorResponse();
}
}
}