map(function ($category) { return [ 'id' => $category->id, 'name' => $category->name, ]; }); return $this->success($categories, 'Success', 'Categories List'); } public function index(Request $request) { $request->validate([ 'category_id' => 'nullable|exists:judicial_precedent_categories,id', 'sort_by' => 'nullable|in:date,number', 'sort' => 'nullable|in:asc,desc', 'per_page' => 'nullable|integer|min:1|max:100', ]); $query = JudicialPrecedent::query(); if ($request->filled('category_id')) { $query->where('category_id', $request->category_id); } $sortBy = $request->input('sort_by', 'date'); $sortOrder = $request->input('sort', 'desc'); $sortColumn = $sortBy === 'number' ? 'ruling_number' : 'ruling_date'; if ($sortBy === 'number') { // Natural numeric sorting for string numbers $query->orderByRaw("LENGTH({$sortColumn}) {$sortOrder}")->orderBy($sortColumn, $sortOrder); } else { $query->orderBy($sortColumn, $sortOrder); } $perPage = min(max((int) $request->input('per_page', 15), 10), 100); $paginator = $query->paginate($perPage); $items = collect($paginator->items())->map(function ($item) { return $this->formatItem($item); }); $responseData = [ 'items' => $items, 'current_page' => $paginator->currentPage(), 'last_page' => $paginator->lastPage(), 'per_page' => $paginator->perPage(), 'total' => $paginator->total(), ]; return $this->success($responseData, 'Success', 'Judicial Precedents Fetched'); } public function search(Request $request) { $request->validate([ 'search' => 'nullable|string', 'sort_by' => 'nullable|in:date,number', 'sort' => 'nullable|in:asc,desc', 'per_page' => 'nullable|integer|min:1|max:100', ]); $query = JudicialPrecedent::query(); if ($request->filled('search')) { $searchTerm = $request->input('search'); $query->where(function ($q) use ($searchTerm) { $q->where('ruling_number', 'LIKE', "%{$searchTerm}%") ->orWhere('subject', 'LIKE', "%{$searchTerm}%") ->orWhere('full_text', 'LIKE', "%{$searchTerm}%"); }); } $sortBy = $request->input('sort_by', 'date'); $sortOrder = $request->input('sort', 'desc'); $sortColumn = $sortBy === 'number' ? 'ruling_number' : 'ruling_date'; if ($sortBy === 'number') { $query->orderByRaw("LENGTH({$sortColumn}) {$sortOrder}")->orderBy($sortColumn, $sortOrder); } else { $query->orderBy($sortColumn, $sortOrder); } $perPage = min(max((int) $request->input('per_page', 15), 10), 100); $paginator = $query->paginate($perPage); $items = collect($paginator->items())->map(function ($item) { return $this->formatItem($item); }); $responseData = [ 'items' => $items, 'current_page' => $paginator->currentPage(), 'last_page' => $paginator->lastPage(), 'per_page' => $paginator->perPage(), 'total' => $paginator->total(), ]; return $this->success($responseData, 'Success', 'Search Results Fetched'); } public function fast_search(Request $request) { $searchTerm = $request->input('search'); $query = JudicialPrecedent::query(); if (!empty($searchTerm)) { $query->where(function ($q) use ($searchTerm) { $q->where('ruling_number', 'LIKE', "%{$searchTerm}%") ->orWhere('subject', 'LIKE', "%{$searchTerm}%") ->orWhere('full_text', 'LIKE', "%{$searchTerm}%"); }); } $count = $query->count(); $items = $query->orderBy('ruling_date', 'desc')->take(5)->get()->map(function ($item) { return $this->formatItem($item); }); return $this->success([ 'items' => $items, 'count' => $count ], 'Success', 'Fast Search Results'); } public function pager($id) { $current = JudicialPrecedent::find($id); if (!$current) { return $this->failed([], ['title' => 'خطا', 'message' => 'رأی وحدت رویه مورد نظر یافت نشد.'], 404); } $previousItem = JudicialPrecedent::where(function ($query) use ($current) { $query->where('ruling_date', '<', $current->ruling_date) ->orWhere(function ($subQuery) use ($current) { $subQuery->where('ruling_date', '=', $current->ruling_date)->where('id', '<', $current->id); }); })->orderBy('ruling_date', 'desc')->orderBy('id', 'desc')->first(); $nextItem = JudicialPrecedent::where(function ($query) use ($current) { $query->where('ruling_date', '>', $current->ruling_date) ->orWhere(function ($subQuery) use ($current) { $subQuery->where('ruling_date', '=', $current->ruling_date)->where('id', '>', $current->id); }); })->orderBy('ruling_date', 'asc')->orderBy('id', 'asc')->first(); // Apply format and add Notes & Highlights $formatWithNoteAndHighlight = function($item) { if (!$item) return null; $formatted = $this->formatItem($item); $formatted['note'] = \App\Models\JudicialPrecedentNote::select('id', 'note', 'color_code', 'created_at') ->where('user_id', auth()->id()) ->where('judicial_precedent_id', $item->id) ->get(); // NEW: Fetch Highlights $formatted['highlight'] = \App\Models\JudicialPrecedentHighlight::select('id', 'start_index', 'end_index', 'selected_text', 'color_code') ->where('user_id', auth()->id()) ->where('judicial_precedent_id', $item->id) ->get(); return $formatted; }; $results = array_values(array_filter([ $previousItem ? $formatWithNoteAndHighlight($previousItem) : null, $formatWithNoteAndHighlight($current), $nextItem ? $formatWithNoteAndHighlight($nextItem) : null ])); return $this->success($results, 'Success', 'Pager Window Details'); } private function isLockedForCurrentUser($item): bool { return ! auth()->user()->isSubscriber() && (bool) ($item->is_locked ?? false); } private function formatItem($item) { $isLocked = $this->isLockedForCurrentUser($item); return [ 'id' => $item->id, 'ruling_number' => $item->ruling_number, 'ruling_date' => $item->ruling_date, 'subject' => $item->subject, 'full_text' => $isLocked ? null : $item->full_text, 'issuing_authority' => $item->issuing_authority, 'category_id' => $item->category_id, 'category_name' => optional($item->category)->name, 'is_locked' => $isLocked, ]; } }