İlk temizlik tamamlandı bir önceki projeden
This commit is contained in:
@@ -0,0 +1,968 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Notification;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class NotificationController extends Controller
|
||||
{
|
||||
/**
|
||||
* Base catalog of notification codes shown in the inbox navigation.
|
||||
* Order matters for sidebar display.
|
||||
*/
|
||||
private const NOTIFICATION_CATALOG = [
|
||||
'notification_deleted_joint_missing_comment' => 'Deleted Joints Missing Comments',
|
||||
'notification_repair_log_missing_new_joint' => 'Repair Logs Missing New Joint Number',
|
||||
'notification_daily_repair_rate_high' => 'Daily Repair Rate Exceeds 12%',
|
||||
'notification_line_list_missing_data' => 'Line List Missing Required Data',
|
||||
'notification_wps_pdf_missing' => 'WPS PDF Document Missing',
|
||||
'notification_naks_welder_cert_missing' => 'NAKS Welder Certificate Missing',
|
||||
'notification_welding_equipment_pdf_missing' => 'Welding Equipment PDF Missing',
|
||||
'notification_wpq_followup_pdf_missing' => 'WPQR PDF Missing',
|
||||
'notification_weldlog_certificate_missing' => 'Certificate Number 1-2 Missing',
|
||||
'notification_incoming_control_cert_missing' => 'Incoming Control Certificate Missing',
|
||||
'notification_welder_id_card_created' => 'Welder ID Card Created',
|
||||
'notification_document_revision_changed' => 'Document Revision Changed',
|
||||
'notification_nde_matrix_not_in_weldlog' => 'NDE Matrix Not in Weldlog',
|
||||
'notification_weldlog_not_in_line_list' => 'Weldlog Not in Line List',
|
||||
'notification_line_list_ndt_missing' => 'Line List NDT Missing',
|
||||
'notification_ndt_pmi_missing' => 'NDT PMI Test Missing',
|
||||
'notification_ndt_fn_missing' => 'NDT FN Test Missing',
|
||||
'notification_manage_ndt_unnecessary' => 'Unnecessary NDT Requests',
|
||||
'notification_support_log_missing_in_weldlog' => 'Support Log Missing in Weldlog',
|
||||
'notification_paint_system_incomplete' => 'Paint System Missing',
|
||||
'notification_overall_repair_rate_high' => 'Overall Repair Rate Exceeds 5%',
|
||||
'notification_weldlog_test_date_invalid' => 'Weldlog Test Date Before Welding Date',
|
||||
'notification_nde_matrix_empty_row' => 'NDE Matrix Empty Row - NDT Assignment Required',
|
||||
'notification_ndt_request_overdue' => 'NDT Request Overdue (10+ Days)',
|
||||
'notification_test_log_pdf_missing_vt' => 'VT Test - PDF Report Missing',
|
||||
'notification_test_log_pdf_missing_rt' => 'RT Test - PDF Report Missing',
|
||||
'notification_test_log_pdf_missing_ut' => 'UT Test - PDF Report Missing',
|
||||
'notification_test_log_pdf_missing_pt' => 'PT Test - PDF Report Missing',
|
||||
'notification_test_log_pdf_missing_mt' => 'MT Test - PDF Report Missing',
|
||||
'notification_test_log_pdf_missing_ht' => 'HT Test - PDF Report Missing',
|
||||
'notification_test_log_pdf_missing_pmi' => 'PMI Test - PDF Report Missing',
|
||||
'notification_test_log_pdf_missing_pwht' => 'PWHT Test - PDF Report Missing',
|
||||
'notification_test_log_pdf_missing_ferrite' => 'Ferrite Test - PDF Report Missing',
|
||||
'notification_feedback_report' => 'Feedback Report Notifications',
|
||||
'notification_calibration_due_soon' => 'Calibration Due Soon',
|
||||
];
|
||||
/**
|
||||
* Get user's notifications with pagination
|
||||
* Performance: Uses indexes, pagination, and selective column loading
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getUserNotifications(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
$perPage = $request->input('per_page', 20);
|
||||
$onlyUnread = $request->input('only_unread', false);
|
||||
|
||||
// Performance: Select only needed columns
|
||||
$query = Notification::select([
|
||||
'id',
|
||||
'notification_code',
|
||||
'title',
|
||||
'message',
|
||||
'link',
|
||||
'is_read',
|
||||
'filter_params',
|
||||
'created_at'
|
||||
])
|
||||
->forUser($user->id)
|
||||
->orderBy('created_at', 'desc');
|
||||
|
||||
// Filter unread if requested
|
||||
if ($onlyUnread) {
|
||||
$query->unread();
|
||||
}
|
||||
|
||||
// Performance: Use pagination to limit results
|
||||
$notifications = $query->paginate($perPage);
|
||||
|
||||
return response()->json($notifications);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's recent notifications (for dropdown)
|
||||
* Performance: Limited results, cached for 30 seconds
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getRecentNotifications()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
// Cache key specific to user
|
||||
$cacheKey = "user_notifications_{$user->id}";
|
||||
|
||||
// Performance: Cache for 30 seconds to reduce database load
|
||||
$notifications = Cache::remember($cacheKey, 30, function () use ($user) {
|
||||
return Notification::select([
|
||||
'id',
|
||||
'notification_code',
|
||||
'title',
|
||||
'message',
|
||||
'link',
|
||||
'is_read',
|
||||
'filter_params',
|
||||
'created_at'
|
||||
])
|
||||
->forUser($user->id)
|
||||
->recent(30) // Last 30 days
|
||||
->orderBy('created_at', 'desc')
|
||||
->limit(10) // Performance: Limit to 10 most recent
|
||||
->get();
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'notifications' => $notifications,
|
||||
'unread_count' => $this->getUnreadCountValue($user->id)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark notification as read
|
||||
* Performance: Single update query
|
||||
*
|
||||
* @param int $id Notification ID
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function markAsRead($id)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
// Performance: Check ownership and update in one query
|
||||
$updated = Notification::where('id', $id)
|
||||
->where('user_id', $user->id)
|
||||
->where('is_read', false)
|
||||
->update(['is_read' => true]);
|
||||
|
||||
if ($updated) {
|
||||
// Clear cache after marking as read
|
||||
Cache::forget("user_notifications_{$user->id}");
|
||||
Cache::forget("user_unread_type_count_{$user->id}");
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'unread_count' => $this->getUnreadCountValue($user->id)
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json(['success' => false, 'message' => 'Notification not found or already read'], 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark notification as unread
|
||||
* Performance: Single update query
|
||||
*
|
||||
* @param int $id Notification ID
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function markAsUnread($id)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
// Performance: Check ownership and update in one query
|
||||
$updated = Notification::where('id', $id)
|
||||
->where('user_id', $user->id)
|
||||
->where('is_read', true)
|
||||
->update(['is_read' => false]);
|
||||
|
||||
if ($updated) {
|
||||
// Clear cache after marking as unread
|
||||
Cache::forget("user_notifications_{$user->id}");
|
||||
Cache::forget("user_unread_type_count_{$user->id}");
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'unread_count' => $this->getUnreadCountValue($user->id)
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json(['success' => false, 'message' => 'Notification not found or already unread'], 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all notifications as read for user
|
||||
* Performance: Bulk update query
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function markAllAsRead()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
// Performance: Bulk update in single query
|
||||
$updated = Notification::where('user_id', $user->id)
|
||||
->where('is_read', false)
|
||||
->update(['is_read' => true]);
|
||||
|
||||
// Clear cache
|
||||
Cache::forget("user_notifications_{$user->id}");
|
||||
Cache::forget("user_unread_type_count_{$user->id}");
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'marked_count' => $updated,
|
||||
'unread_count' => 0
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unread notification count
|
||||
* Performance: Cached count query
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getUnreadCount()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
$count = $this->getUnreadCountValue($user->id);
|
||||
|
||||
return response()->json(['unread_count' => $count]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a notification
|
||||
* Performance: Single delete query with ownership check
|
||||
*
|
||||
* @param int $id Notification ID
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
// Performance: Delete with ownership check in one query
|
||||
$deleted = Notification::where('id', $id)
|
||||
->where('user_id', $user->id)
|
||||
->delete();
|
||||
|
||||
if ($deleted) {
|
||||
// Clear cache
|
||||
Cache::forget("user_notifications_{$user->id}");
|
||||
Cache::forget("user_unread_type_count_{$user->id}");
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
return response()->json(['success' => false, 'message' => 'Notification not found'], 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all read notifications for user
|
||||
* Performance: Bulk delete query
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function deleteReadNotifications()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
// Performance: Bulk delete in single query
|
||||
$deletedCount = Notification::where('user_id', $user->id)
|
||||
->where('is_read', true)
|
||||
->delete();
|
||||
|
||||
// Clear cache
|
||||
Cache::forget("user_notifications_{$user->id}");
|
||||
Cache::forget("user_unread_type_count_{$user->id}");
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'deleted_count' => $deletedCount
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show filtered list page for notification
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showFilteredList(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
abort(401, 'Unauthorized');
|
||||
}
|
||||
|
||||
$notificationId = $request->input('id');
|
||||
|
||||
if (!$notificationId) {
|
||||
abort(404, 'Notification ID is required');
|
||||
}
|
||||
|
||||
// Get notification and verify ownership
|
||||
$notification = Notification::where('id', $notificationId)
|
||||
->where('user_id', $user->id)
|
||||
->first();
|
||||
|
||||
if (!$notification) {
|
||||
abort(404, 'Notification not found');
|
||||
}
|
||||
|
||||
// Mark notification as read if not already read
|
||||
if (!$notification->is_read) {
|
||||
$notification->markAsRead();
|
||||
Cache::forget("user_notifications_{$user->id}");
|
||||
Cache::forget("user_unread_type_count_{$user->id}");
|
||||
}
|
||||
|
||||
return view('admin.type.notification-filtered-list', [
|
||||
'notification' => $notification
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filtered data based on notification filter_params
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getFilteredData(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
$notificationId = $request->input('id');
|
||||
|
||||
if (!$notificationId) {
|
||||
return response()->json(['error' => 'Notification ID is required'], 400);
|
||||
}
|
||||
|
||||
// Get notification and verify ownership
|
||||
$notification = Notification::where('id', $notificationId)
|
||||
->where('user_id', $user->id)
|
||||
->first();
|
||||
|
||||
if (!$notification) {
|
||||
return response()->json(['error' => 'Notification not found'], 404);
|
||||
}
|
||||
|
||||
$filterParams = $notification->filter_params;
|
||||
|
||||
if (empty($filterParams) || !isset($filterParams['table'])) {
|
||||
return response()->json(['error' => 'Filter parameters not found'], 400);
|
||||
}
|
||||
|
||||
$tableName = $filterParams['table'];
|
||||
$conditions = $filterParams['conditions'] ?? [];
|
||||
$columnWhitelist = [
|
||||
'deleted_joints' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'type_of_joint',
|
||||
'welding_date',
|
||||
'spool_number',
|
||||
'comment',
|
||||
'deleted_date',
|
||||
'updated_at'
|
||||
],
|
||||
'weld_logs' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'type_of_joint',
|
||||
'welding_date',
|
||||
'welder_1',
|
||||
'welder_2',
|
||||
'status',
|
||||
'updated_at'
|
||||
],
|
||||
'repair_logs' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'new_joint_no',
|
||||
'repair_status',
|
||||
'repair_date',
|
||||
'updated_at'
|
||||
],
|
||||
'test_packages' => [
|
||||
'id',
|
||||
'package_no',
|
||||
'package_title',
|
||||
'status',
|
||||
'hydro_test_date',
|
||||
'pneumatic_test_date',
|
||||
'updated_at'
|
||||
],
|
||||
'n_c_r_logs' => [
|
||||
'id',
|
||||
'ncr_no',
|
||||
'project',
|
||||
'system',
|
||||
'line_no',
|
||||
'status',
|
||||
'updated_at'
|
||||
],
|
||||
'r_f_i_s' => [
|
||||
'id',
|
||||
'rfi_no',
|
||||
'project',
|
||||
'system',
|
||||
'subject',
|
||||
'status',
|
||||
'updated_at'
|
||||
],
|
||||
'w_p_s' => [
|
||||
'id',
|
||||
'pqr_no',
|
||||
'technology_category',
|
||||
'download',
|
||||
'updated_at'
|
||||
],
|
||||
'naks_welders' => [
|
||||
'id',
|
||||
'welder_name_en',
|
||||
'welder_name_ru',
|
||||
'welder_id',
|
||||
'naks_certificate_no',
|
||||
'updated_at'
|
||||
],
|
||||
'welding_equipment' => [
|
||||
'id',
|
||||
'attestation',
|
||||
'producer',
|
||||
'download',
|
||||
'updated_at'
|
||||
],
|
||||
'welder_tests' => [
|
||||
'id',
|
||||
'name_surname',
|
||||
'wpq_document_no',
|
||||
'naks_id',
|
||||
'download',
|
||||
'updated_at'
|
||||
],
|
||||
'line_lists' => [
|
||||
'id',
|
||||
'line_no',
|
||||
'category',
|
||||
'ndt',
|
||||
'working_pressure_mpa',
|
||||
'design_pressure_mpa',
|
||||
'working_temperature',
|
||||
'design_temperature',
|
||||
'test_media',
|
||||
'updated_at'
|
||||
],
|
||||
'supports' => [
|
||||
'id',
|
||||
'support_code',
|
||||
'materials',
|
||||
'line_number',
|
||||
'weld_or_assembled_date',
|
||||
'updated_at'
|
||||
],
|
||||
'nde_matrices' => [
|
||||
'id',
|
||||
'line',
|
||||
'project',
|
||||
'type_of_joint',
|
||||
'updated_at'
|
||||
],
|
||||
'incoming_controls' => [
|
||||
'id',
|
||||
'component_code',
|
||||
'certificate_no',
|
||||
'heat_number',
|
||||
'updated_at'
|
||||
],
|
||||
'paint_systems' => [
|
||||
'id',
|
||||
'paint_cycle',
|
||||
'primer_coat_name_1',
|
||||
'brand_name_1',
|
||||
'thickness_1',
|
||||
'updated_at'
|
||||
],
|
||||
'v_t_logs' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'vt_result',
|
||||
'report_file',
|
||||
'updated_at'
|
||||
],
|
||||
'radiographic_tests' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'rt_result',
|
||||
'report_file',
|
||||
'updated_at'
|
||||
],
|
||||
'ultrasonic_tests' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'ut_result',
|
||||
'report_file',
|
||||
'updated_at'
|
||||
],
|
||||
'p_t_logs' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'pt_result',
|
||||
'report_file',
|
||||
'updated_at'
|
||||
],
|
||||
'magnetic_tests' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'mt_result',
|
||||
'report_file',
|
||||
'updated_at'
|
||||
],
|
||||
'hardness_tests' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'ht_result',
|
||||
'report_file',
|
||||
'updated_at'
|
||||
],
|
||||
'p_m_i_tests' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'pmi_result',
|
||||
'report_file',
|
||||
'updated_at'
|
||||
],
|
||||
'p_w_h_t_s' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'pwht_result',
|
||||
'report_file',
|
||||
'updated_at'
|
||||
],
|
||||
'ferrits' => [
|
||||
'id',
|
||||
'project',
|
||||
'line_number',
|
||||
'iso_number',
|
||||
'no_of_the_joint_as_per_as_built_survey',
|
||||
'ferrite_result',
|
||||
'report_file',
|
||||
'updated_at'
|
||||
],
|
||||
'test_pack_base_statuses' => [
|
||||
'id',
|
||||
'test_package_no',
|
||||
'drawing_no',
|
||||
'area',
|
||||
'discipline',
|
||||
'subcontractor',
|
||||
'updated_at'
|
||||
],
|
||||
'calibration_logs' => [
|
||||
'id',
|
||||
'item_no',
|
||||
'equipment_name',
|
||||
'manufacturer',
|
||||
'model_no',
|
||||
'serial_no',
|
||||
'quantity',
|
||||
'calibration_date',
|
||||
'calibration_due_date',
|
||||
'status',
|
||||
'certification_document',
|
||||
'updated_at',
|
||||
],
|
||||
];
|
||||
$selectedColumns = $columnWhitelist[$tableName] ?? ['*'];
|
||||
|
||||
// Security: Only allow specific tables (whitelist)
|
||||
$allowedTables = [
|
||||
'deleted_joints',
|
||||
'weld_logs',
|
||||
'repair_logs',
|
||||
'test_packages',
|
||||
'n_c_r_logs',
|
||||
'r_f_i_s',
|
||||
'w_p_s',
|
||||
'naks_welders',
|
||||
'welding_equipment',
|
||||
'welder_tests',
|
||||
'line_lists',
|
||||
'supports',
|
||||
'nde_matrices',
|
||||
'incoming_controls',
|
||||
'paint_systems',
|
||||
'v_t_logs',
|
||||
'radiographic_tests',
|
||||
'ultrasonic_tests',
|
||||
'p_t_logs',
|
||||
'magnetic_tests',
|
||||
'hardness_tests',
|
||||
'p_m_i_tests',
|
||||
'p_w_h_t_s',
|
||||
'ferrits',
|
||||
'test_pack_base_statuses',
|
||||
'calibration_logs',
|
||||
];
|
||||
if (!in_array($tableName, $allowedTables)) {
|
||||
return response()->json(['error' => 'Table not allowed'], 403);
|
||||
}
|
||||
|
||||
try {
|
||||
// Build query based on filter_params
|
||||
$query = DB::table($tableName)->select($selectedColumns);
|
||||
|
||||
// Apply conditions
|
||||
if (!empty($conditions)) {
|
||||
$hasOrConditions = false;
|
||||
$orConditions = [];
|
||||
|
||||
foreach ($conditions as $key => $value) {
|
||||
if ($key === 'or') {
|
||||
// Collect OR conditions to process later
|
||||
$hasOrConditions = true;
|
||||
$orConditions = is_array($value) ? $value : [];
|
||||
} else {
|
||||
// Handle normal WHERE conditions
|
||||
$this->applyColumnFilter($query, $key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle OR conditions (applied after normal conditions)
|
||||
if ($hasOrConditions && !empty($orConditions)) {
|
||||
$query->where(function($q) use ($orConditions) {
|
||||
$isFirst = true;
|
||||
foreach ($orConditions as $orCondition) {
|
||||
if (!is_array($orCondition)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($orCondition as $orKey => $orValue) {
|
||||
$this->applyColumnFilter($q, $orKey, $orValue, !$isFirst);
|
||||
$isFirst = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$maxRows = max(50, min((int) $request->input('max_rows', 1200), 5000));
|
||||
$countQuery = clone $query;
|
||||
$totalRows = $countQuery->count();
|
||||
$data = $query->orderBy('id', 'desc')->limit($maxRows)->get();
|
||||
|
||||
$columns = $selectedColumns === ['*']
|
||||
? (!empty($data) ? array_keys((array) $data->first()) : [])
|
||||
: $selectedColumns;
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $data,
|
||||
'tableName' => $tableName,
|
||||
'columns' => $columns,
|
||||
'count' => $totalRows,
|
||||
'returned_count' => count($data),
|
||||
'has_more' => $totalRows > $maxRows
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Filtered data error: ' . $e->getMessage());
|
||||
return response()->json(['error' => 'Error fetching filtered data: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notification types with counts for inbox sidebar
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getNotificationTypes()
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
$counts = Notification::select(
|
||||
'notification_code',
|
||||
DB::raw('count(*) as total'),
|
||||
DB::raw('sum(case when is_read = 0 then 1 else 0 end) as unread')
|
||||
)
|
||||
->where('user_id', $user->id)
|
||||
->groupBy('notification_code')
|
||||
->get()
|
||||
->keyBy('notification_code');
|
||||
|
||||
$catalog = collect(self::NOTIFICATION_CATALOG);
|
||||
|
||||
$types = $catalog->map(function ($title, $code) use ($counts) {
|
||||
$stat = $counts->get($code);
|
||||
return [
|
||||
'code' => $code,
|
||||
'title' => $title,
|
||||
'total' => $stat ? (int) $stat->total : 0,
|
||||
'unread' => $stat ? (int) $stat->unread : 0,
|
||||
];
|
||||
})
|
||||
->values();
|
||||
|
||||
// Append any notification codes not defined in the catalog to avoid hiding new types
|
||||
$extraTypes = $counts->reject(function ($value, $code) use ($catalog) {
|
||||
return $catalog->has($code);
|
||||
})
|
||||
->map(function ($stat, $code) {
|
||||
return [
|
||||
'code' => $code,
|
||||
'title' => ucwords(str_replace('_', ' ', $code)),
|
||||
'total' => (int) $stat->total,
|
||||
'unread' => (int) $stat->unread,
|
||||
];
|
||||
})
|
||||
->values();
|
||||
|
||||
if ($extraTypes->isNotEmpty()) {
|
||||
$types = $types->merge($extraTypes);
|
||||
}
|
||||
|
||||
// Get all/unread counts
|
||||
$allCount = Notification::where('user_id', $user->id)->count();
|
||||
$unreadCount = Notification::where('user_id', $user->id)->where('is_read', false)->count();
|
||||
|
||||
return response()->json([
|
||||
'types' => $types,
|
||||
'all' => [
|
||||
'total' => $allCount,
|
||||
'unread' => $unreadCount
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notifications by type
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getNotificationsByType(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
$type = $request->input('type', 'all');
|
||||
$perPage = $request->input('per_page', 100);
|
||||
|
||||
$query = Notification::select([
|
||||
'id',
|
||||
'notification_code',
|
||||
'title',
|
||||
'message',
|
||||
'link',
|
||||
'is_read',
|
||||
'filter_params',
|
||||
'created_at'
|
||||
])
|
||||
->where('user_id', $user->id);
|
||||
|
||||
switch ($type) {
|
||||
case 'unread':
|
||||
$query->where('is_read', false);
|
||||
break;
|
||||
case 'batch':
|
||||
$query->whereNotNull('filter_params');
|
||||
break;
|
||||
case 'single':
|
||||
$query->whereNull('filter_params');
|
||||
break;
|
||||
default:
|
||||
if ($type !== 'all') {
|
||||
$query->where('notification_code', $type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$notifications = $query->orderBy('created_at', 'desc')
|
||||
->get();
|
||||
|
||||
// Remove duplicates based on message, link, and normalized filter_params
|
||||
$seen = [];
|
||||
$uniqueNotifications = [];
|
||||
|
||||
foreach ($notifications as $notification) {
|
||||
// Normalize filter_params for comparison
|
||||
$filterParams = normalizeFilterParams($notification->filter_params);
|
||||
|
||||
// Create unique key
|
||||
$key = $notification->message . '|' . ($notification->link ?? '') . '|' . $filterParams;
|
||||
|
||||
// Keep only the first (most recent) occurrence
|
||||
if (!isset($seen[$key])) {
|
||||
$seen[$key] = true;
|
||||
$uniqueNotifications[] = $notification;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to paginated response
|
||||
$currentPage = $request->input('page', 1);
|
||||
$perPage = (int) $perPage;
|
||||
$offset = ($currentPage - 1) * $perPage;
|
||||
$paginatedItems = array_slice($uniqueNotifications, $offset, $perPage);
|
||||
|
||||
$paginated = new LengthAwarePaginator(
|
||||
$paginatedItems,
|
||||
count($uniqueNotifications),
|
||||
$perPage,
|
||||
$currentPage,
|
||||
['path' => $request->url(), 'query' => $request->query()]
|
||||
);
|
||||
|
||||
return response()->json($paginated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Get cached unread count
|
||||
* Performance: Cached for 1 minute
|
||||
*
|
||||
* @param int $userId
|
||||
* @return int
|
||||
*/
|
||||
private function getUnreadCountValue($userId)
|
||||
{
|
||||
$cacheKey = "user_unread_type_count_{$userId}";
|
||||
|
||||
// Performance: Cache count for 1 minute
|
||||
// Return count of distinct notification types with unread notifications (not total notification count)
|
||||
return Cache::remember($cacheKey, 60, function () use ($userId) {
|
||||
return DB::table('notifications')
|
||||
->where('user_id', $userId)
|
||||
->where('is_read', false)
|
||||
->select(DB::raw('COUNT(DISTINCT notification_code) as count'))
|
||||
->value('count') ?? 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a single column filter to the query, supporting scalar equality and list (IN) filters.
|
||||
*/
|
||||
private function applyColumnFilter($query, string $column, $value, bool $useOr = false): void
|
||||
{
|
||||
if (is_array($value) && $this->isScalarList($value)) {
|
||||
$list = array_values(array_unique(array_filter($value, function ($item) {
|
||||
return $item !== null && $item !== '';
|
||||
})));
|
||||
|
||||
if (empty($list)) {
|
||||
$useOr ? $query->orWhereRaw('1 = 0') : $query->whereRaw('1 = 0');
|
||||
return;
|
||||
}
|
||||
|
||||
$useOr
|
||||
? $query->orWhereIn($column, $list)
|
||||
: $query->whereIn($column, $list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($value === null) {
|
||||
$useOr ? $query->orWhereNull($column) : $query->whereNull($column);
|
||||
} elseif ($value === '') {
|
||||
$useOr ? $query->orWhere($column, '') : $query->where($column, '');
|
||||
} else {
|
||||
$useOr ? $query->orWhere($column, $value) : $query->where($column, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given array is a simple list of scalar values (suitable for whereIn).
|
||||
*/
|
||||
private function isScalarList(array $value): bool
|
||||
{
|
||||
if (array_keys($value) !== range(0, count($value) - 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($value as $item) {
|
||||
if (is_array($item) || is_object($item)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user