From d19d4b8667ac3ad023b13ecc820231e7781e0da8 Mon Sep 17 00:00:00 2001 From: "m.razaghi" Date: Fri, 10 Jul 2026 13:38:47 +0330 Subject: [PATCH] updated apis --- .../Controllers/api/BylawApiController.php | 25 ++++++++-- .../api/GlobalSearchApiController.php | 47 +++++++++++++++---- .../api/JudicialPrecedentApiController.php | 26 ++++++++-- .../api/LegalOpinionApiController.php | 25 ++++++++-- 4 files changed, 105 insertions(+), 18 deletions(-) diff --git a/app/Http/Controllers/api/BylawApiController.php b/app/Http/Controllers/api/BylawApiController.php index f7ded7b..1217e0c 100644 --- a/app/Http/Controllers/api/BylawApiController.php +++ b/app/Http/Controllers/api/BylawApiController.php @@ -44,6 +44,7 @@ class BylawApiController extends Controller 'category_id' => 'nullable|exists:bylaw_subject_categories,id', 'approval_authority_id' => 'nullable|exists:bylaw_legal_authorities,id', 'bylaw_type' => 'nullable|in:executive,delegated,independent', + 'sort_by' => 'nullable|in:date,number', 'sort' => 'nullable|in:asc,desc', 'per_page' => 'nullable|integer|min:1|max:100', ]); @@ -63,8 +64,15 @@ class BylawApiController extends Controller $query->where('bylaw_type', $request->bylaw_type); } + $sortBy = $request->input('sort_by', 'date'); $sortOrder = $request->input('sort', 'desc'); - $query->orderBy('approval_date', $sortOrder); + $sortColumn = $sortBy === 'number' ? 'promulgation_number' : 'approval_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); @@ -88,6 +96,8 @@ class BylawApiController extends Controller { $request->validate([ 'search' => 'nullable|string', + 'sort_by' => 'nullable|in:date,number', + 'sort' => 'nullable|in:asc,desc', 'per_page' => 'nullable|integer|min:1|max:100', ]); @@ -105,8 +115,18 @@ class BylawApiController extends Controller }); } + $sortBy = $request->input('sort_by', 'date'); + $sortOrder = $request->input('sort', 'desc'); + $sortColumn = $sortBy === 'number' ? 'promulgation_number' : 'approval_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->orderBy('approval_date', 'desc')->paginate($perPage); + $paginator = $query->paginate($perPage); $items = collect($paginator->items())->map(function ($item) { return $this->formatBylaw($item); @@ -140,7 +160,6 @@ class BylawApiController extends Controller } $count = $query->count(); - // Limit to 5 results for fast search dropdown $items = $query->orderBy('approval_date', 'desc')->take(5)->get()->map(function ($item) { return $this->formatBylaw($item); }); diff --git a/app/Http/Controllers/api/GlobalSearchApiController.php b/app/Http/Controllers/api/GlobalSearchApiController.php index 9c67e40..97001d6 100644 --- a/app/Http/Controllers/api/GlobalSearchApiController.php +++ b/app/Http/Controllers/api/GlobalSearchApiController.php @@ -25,12 +25,14 @@ class GlobalSearchApiController extends Controller use BaseApiResponse; /** - * Massive Search across ALL data types + * Massive Search across ALL data types (With dynamic Date/Number Sorting) */ 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', 'page' => 'nullable|integer|min:1', ]); @@ -86,7 +88,7 @@ class GlobalSearchApiController extends Controller }); $results = $results->merge($opinions); - // 4. Search Arts (Explicitly separated for eager loading & performance) + // 4. Search Arts $arts = Art::with(['law.category']) ->where('title', 'LIKE', "%{$searchTerm}%") ->orWhere('text', 'LIKE', "%{$searchTerm}%") @@ -121,12 +123,41 @@ class GlobalSearchApiController extends Controller } } - // --- Execute Manual Pagination on the Combined Collection --- + // --- Apply Dynamic Memory Sorting --- + if ($request->has('sort_by')) { + $sortBy = $request->input('sort_by'); + $sortOrder = $request->input('sort', 'desc'); + + if ($sortBy === 'number') { + $sortCallback = function ($item) { + // Extract whatever number property this specific item uses + return (string) ($item['promulgation_number'] ?? $item['ruling_number'] ?? $item['opinion_number'] ?? $item['number'] ?? ''); + }; + + // Use Natural Sorting so "2" comes before "10" + if ($sortOrder === 'asc') { + $results = $results->sortBy($sortCallback, SORT_NATURAL)->values(); + } else { + $results = $results->sortByDesc($sortCallback, SORT_NATURAL)->values(); + } + } elseif ($sortBy === 'date') { + $sortCallback = function ($item) { + // Extract whatever date property this specific item uses (Note: Structural models will default to '' and group together) + return (string) ($item['approval_date'] ?? $item['ruling_date'] ?? $item['opinion_date'] ?? ''); + }; + + if ($sortOrder === 'asc') { + $results = $results->sortBy($sortCallback)->values(); + } else { + $results = $results->sortByDesc($sortCallback)->values(); + } + } + } + + // --- Execute Manual Pagination on the Sorted Collection --- $total = $results->count(); - // Slice the array for the exact page requested $paginatedItems = $results->forPage($page, $perPage)->values(); - // Build a proper Laravel paginator object so the response metadata remains identical $paginator = new LengthAwarePaginator( $paginatedItems, $total, @@ -236,7 +267,6 @@ class GlobalSearchApiController extends Controller $totalCount += $query->count(); - // Only append items if we haven't reached ~15 fast search items yet if ($results->count() < 15) { $items = $query->take($limitPerTable)->get()->map(function($item) use ($modelClass, $table) { $formatted = $this->formatStructure($item, $modelClass); @@ -249,7 +279,6 @@ class GlobalSearchApiController extends Controller } return $this->success([ - // Return top 15 matches max for dropdown 'items' => $results->take(15)->values(), 'count' => $totalCount ], 'Success', 'Global Fast Search Results'); @@ -314,7 +343,6 @@ class GlobalSearchApiController extends Controller private function formatArt($item, $searchTerm, $modelClass) { - // Use eager loaded relation if available to prevent N+1 queries, otherwise fallback to find $law = $item->law ?? Law::find($item->law_id); $isLocked = ! auth()->user()->isSubscriber() && (bool) $law?->is_locked; @@ -329,6 +357,7 @@ class GlobalSearchApiController extends Controller return [ 'id' => $item->id, 'title' => $item->title, + 'number' => $item->number, // Added for sorting capability 'text' => $isLocked ? null : $context, 'is_locked' => $isLocked, 'route' => array_values($this->route($modelClass, $item)), @@ -349,7 +378,7 @@ class GlobalSearchApiController extends Controller $payload = [ 'id' => $item->id, 'title' => $item->title, - 'number' => $item->number, + 'number' => $item->number, // Available for sorting capability 'is_locked' => $isLocked, 'route' => array_values($this->route($modelClass, $item)), 'law' => $law?->title ?? '', diff --git a/app/Http/Controllers/api/JudicialPrecedentApiController.php b/app/Http/Controllers/api/JudicialPrecedentApiController.php index 7925686..98732d5 100644 --- a/app/Http/Controllers/api/JudicialPrecedentApiController.php +++ b/app/Http/Controllers/api/JudicialPrecedentApiController.php @@ -28,6 +28,7 @@ class JudicialPrecedentApiController extends Controller { $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', ]); @@ -38,8 +39,16 @@ class JudicialPrecedentApiController extends Controller $query->where('category_id', $request->category_id); } + $sortBy = $request->input('sort_by', 'date'); $sortOrder = $request->input('sort', 'desc'); - $query->orderBy('ruling_date', $sortOrder); + $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); @@ -63,6 +72,8 @@ class JudicialPrecedentApiController extends Controller { $request->validate([ 'search' => 'nullable|string', + 'sort_by' => 'nullable|in:date,number', + 'sort' => 'nullable|in:asc,desc', 'per_page' => 'nullable|integer|min:1|max:100', ]); @@ -78,8 +89,18 @@ class JudicialPrecedentApiController extends Controller }); } + $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->orderBy('ruling_date', 'desc')->paginate($perPage); + $paginator = $query->paginate($perPage); $items = collect($paginator->items())->map(function ($item) { return $this->formatItem($item); @@ -110,7 +131,6 @@ class JudicialPrecedentApiController extends Controller } $count = $query->count(); - // Limit to 5 results for fast search dropdown $items = $query->orderBy('ruling_date', 'desc')->take(5)->get()->map(function ($item) { return $this->formatItem($item); }); diff --git a/app/Http/Controllers/api/LegalOpinionApiController.php b/app/Http/Controllers/api/LegalOpinionApiController.php index fab7d0c..a6ff5e6 100644 --- a/app/Http/Controllers/api/LegalOpinionApiController.php +++ b/app/Http/Controllers/api/LegalOpinionApiController.php @@ -28,6 +28,7 @@ class LegalOpinionApiController extends Controller { $request->validate([ 'category_id' => 'nullable|exists:legal_opinion_categories,id', + 'sort_by' => 'nullable|in:date,number', 'sort' => 'nullable|in:asc,desc', 'per_page' => 'nullable|integer|min:1|max:100', ]); @@ -38,8 +39,15 @@ class LegalOpinionApiController extends Controller $query->where('category_id', $request->category_id); } + $sortBy = $request->input('sort_by', 'date'); $sortOrder = $request->input('sort', 'desc'); - $query->orderBy('opinion_date', $sortOrder); + $sortColumn = $sortBy === 'number' ? 'opinion_number' : 'opinion_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); @@ -63,6 +71,8 @@ class LegalOpinionApiController extends Controller { $request->validate([ 'search' => 'nullable|string', + 'sort_by' => 'nullable|in:date,number', + 'sort' => 'nullable|in:asc,desc', 'per_page' => 'nullable|integer|min:1|max:100', ]); @@ -79,8 +89,18 @@ class LegalOpinionApiController extends Controller }); } + $sortBy = $request->input('sort_by', 'date'); + $sortOrder = $request->input('sort', 'desc'); + $sortColumn = $sortBy === 'number' ? 'opinion_number' : 'opinion_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->orderBy('opinion_date', 'desc')->paginate($perPage); + $paginator = $query->paginate($perPage); $items = collect($paginator->items())->map(function ($item) { return $this->formatItem($item); @@ -112,7 +132,6 @@ class LegalOpinionApiController extends Controller } $count = $query->count(); - // Limit to 5 results for fast search dropdown $items = $query->orderBy('opinion_date', 'desc')->take(5)->get()->map(function ($item) { return $this->formatItem($item); });