From 51c4570f975f59f8467077c195c6aff15ef1f708 Mon Sep 17 00:00:00 2001 From: "m.razaghi" Date: Sun, 12 Jul 2026 16:49:12 +0330 Subject: [PATCH] Fixed api issue --- .../api/GlobalSearchApiController.php | 209 +++++++----------- 1 file changed, 82 insertions(+), 127 deletions(-) diff --git a/app/Http/Controllers/api/GlobalSearchApiController.php b/app/Http/Controllers/api/GlobalSearchApiController.php index 6f1c470..0ba222c 100644 --- a/app/Http/Controllers/api/GlobalSearchApiController.php +++ b/app/Http/Controllers/api/GlobalSearchApiController.php @@ -42,20 +42,19 @@ class GlobalSearchApiController extends Controller $perPage = min(max((int) $request->input('per_page', 15), 10), 100); $page = (int) $request->input('page', 1); - $staticResults = collect(); // For Art & Structure (IGNORES SORTING) - $sortableResults = collect(); // For Bylaws, Precedents, Opinions (SORTS BASED ON REQUEST) + $staticResults = collect(); // Arts and Structures (Always at the top) + $sortableResults = collect(); // Bylaws, Opinions, Precedents (Sorted if requested) if (!empty($searchTerm)) { $limitPerTable = 100; // ========================================================= - // 1. Search Arts FIRST (Exact match to ArtController) + // 1. Search Arts (Exact match to ArtController) // ========================================================= $arts = Art::with(['law.category', 'chapter', 'part', 'volum', 'book', 'section', 'gate']) ->where(function ($q) use ($searchTerm) { $q->where('title', 'LIKE', "%{$searchTerm}%") - ->orWhere('text', 'LIKE', "%{$searchTerm}%") - ->orWhere('number', 'LIKE', "%{$searchTerm}%"); + ->orWhere('text', 'LIKE', "%{$searchTerm}%"); }) ->take($limitPerTable)->get()->map(function($item) use ($searchTerm) { $formatted = $this->formatArt($item, $searchTerm, Art::class); @@ -143,7 +142,7 @@ class GlobalSearchApiController extends Controller $sortableResults = $sortableResults->concat($opinions); } - // --- Apply Sorting ONLY to $sortableResults (Arts and Structures are ignored!) --- + // --- Apply Dynamic Memory Sorting ONLY to Legal Docs --- if ($request->has('sort_by') && $request->input('sort_by') != null) { $sortBy = $request->input('sort_by'); $sortOrder = $request->input('sort', 'desc'); @@ -171,7 +170,7 @@ class GlobalSearchApiController extends Controller } } - // Combine Results: Static (Arts/Structures) ALWAYS at the top, followed by Sorted Docs + // Combine Results: Arts and Structures ALWAYS pin to the top! $results = $staticResults->concat($sortableResults); // --- Execute Manual Pagination on the final Collection --- @@ -210,12 +209,11 @@ class GlobalSearchApiController extends Controller if (!empty($searchTerm)) { - // 1. Search Arts FIRST for Fast Search + // 1. Search Arts FIRST $artQuery = Art::with(['law', 'law.category', 'chapter', 'part', 'volum', 'book', 'section', 'gate']) ->where(function ($q) use ($searchTerm) { $q->where('title', 'LIKE', "%{$searchTerm}%") - ->orWhere('text', 'LIKE', "%{$searchTerm}%") - ->orWhere('number', 'LIKE', "%{$searchTerm}%"); + ->orWhere('text', 'LIKE', "%{$searchTerm}%"); }); $totalCount += $artQuery->count(); $arts = $artQuery->take($limitPerTable)->get()->map(function($item) use ($searchTerm) { @@ -225,60 +223,7 @@ class GlobalSearchApiController extends Controller }); $results = $results->concat($arts); - // 2. Bylaw Documents - $bylawQuery = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) - ->where('is_published', true) - ->where(function ($q) use ($searchTerm) { - $q->where('title', 'LIKE', "%{$searchTerm}%") - ->orWhere('description', 'LIKE', "%{$searchTerm}%") - ->orWhere('promulgation_number', 'LIKE', "%{$searchTerm}%") - ->orWhere('official_gazette_number', 'LIKE', "%{$searchTerm}%") - ->orWhereIn('id', function($subQuery) use ($searchTerm) { - $subQuery->select('document_id') - ->from('bylaw_nodes') - ->where('node_content', 'LIKE', "%{$searchTerm}%"); - }); - }); - $totalCount += $bylawQuery->count(); - $bylaws = $bylawQuery->take($limitPerTable)->get()->map(function($item) { - $formatted = $this->formatBylaw($item); - $formatted['type'] = 'bylaw'; - return $formatted; - }); - $results = $results->concat($bylaws); - - // 3. Judicial Precedents - $judicialQuery = JudicialPrecedent::with('category') - ->where(function ($q) use ($searchTerm) { - $q->where('ruling_number', 'LIKE', "%{$searchTerm}%") - ->orWhere('subject', 'LIKE', "%{$searchTerm}%") - ->orWhere('full_text', 'LIKE', "%{$searchTerm}%"); - }); - $totalCount += $judicialQuery->count(); - $judicials = $judicialQuery->take($limitPerTable)->get()->map(function($item) { - $formatted = $this->formatJudicial($item); - $formatted['type'] = 'judicial_precedent'; - return $formatted; - }); - $results = $results->concat($judicials); - - // 4. Legal Opinions - $opinionQuery = LegalOpinion::with('category') - ->where(function ($q) use ($searchTerm) { - $q->where('opinion_number', 'LIKE', "%{$searchTerm}%") - ->orWhere('subject', 'LIKE', "%{$searchTerm}%") - ->orWhere('full_text', 'LIKE', "%{$searchTerm}%") - ->orWhere('issuing_authority', 'LIKE', "%{$searchTerm}%"); - }); - $totalCount += $opinionQuery->count(); - $opinions = $opinionQuery->take($limitPerTable)->get()->map(function($item) { - $formatted = $this->formatOpinion($item); - $formatted['type'] = 'legal_opinion'; - return $formatted; - }); - $results = $results->concat($opinions); - - // 5. Structural Models + // 2. Structural Models $structuralModels = [ Law::class, Volum::class, Book::class, Division::class, Section::class, Chapter::class, Part::class, Gate::class, @@ -303,6 +248,59 @@ class GlobalSearchApiController extends Controller $results = $results->concat($items); } } + + // 3. Bylaw Documents + $bylawQuery = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) + ->where('is_published', true) + ->where(function ($q) use ($searchTerm) { + $q->where('title', 'LIKE', "%{$searchTerm}%") + ->orWhere('description', 'LIKE', "%{$searchTerm}%") + ->orWhere('promulgation_number', 'LIKE', "%{$searchTerm}%") + ->orWhere('official_gazette_number', 'LIKE', "%{$searchTerm}%") + ->orWhereIn('id', function($subQuery) use ($searchTerm) { + $subQuery->select('document_id') + ->from('bylaw_nodes') + ->where('node_content', 'LIKE', "%{$searchTerm}%"); + }); + }); + $totalCount += $bylawQuery->count(); + $bylaws = $bylawQuery->take($limitPerTable)->get()->map(function($item) { + $formatted = $this->formatBylaw($item); + $formatted['type'] = 'bylaw'; + return $formatted; + }); + $results = $results->concat($bylaws); + + // 4. Judicial Precedents + $judicialQuery = JudicialPrecedent::with('category') + ->where(function ($q) use ($searchTerm) { + $q->where('ruling_number', 'LIKE', "%{$searchTerm}%") + ->orWhere('subject', 'LIKE', "%{$searchTerm}%") + ->orWhere('full_text', 'LIKE', "%{$searchTerm}%"); + }); + $totalCount += $judicialQuery->count(); + $judicials = $judicialQuery->take($limitPerTable)->get()->map(function($item) { + $formatted = $this->formatJudicial($item); + $formatted['type'] = 'judicial_precedent'; + return $formatted; + }); + $results = $results->concat($judicials); + + // 5. Legal Opinions + $opinionQuery = LegalOpinion::with('category') + ->where(function ($q) use ($searchTerm) { + $q->where('opinion_number', 'LIKE', "%{$searchTerm}%") + ->orWhere('subject', 'LIKE', "%{$searchTerm}%") + ->orWhere('full_text', 'LIKE', "%{$searchTerm}%") + ->orWhere('issuing_authority', 'LIKE', "%{$searchTerm}%"); + }); + $totalCount += $opinionQuery->count(); + $opinions = $opinionQuery->take($limitPerTable)->get()->map(function($item) { + $formatted = $this->formatOpinion($item); + $formatted['type'] = 'legal_opinion'; + return $formatted; + }); + $results = $results->concat($opinions); } return $this->success([ @@ -382,72 +380,36 @@ class GlobalSearchApiController extends Controller } /** - * Exact 1:1 match with ArtController format mapping + * Exact 1:1 match with ArtController::searchModel mapping + * All relations (law, chapter, etc) are strictly Strings to prevent App Crashes! */ - private function formatArt($q, $searchTerm, $modelClass) + private function formatArt($item, $searchTerm, $modelClass) { - $law = $q->law ?? Law::find($q->law_id); + $law = $item->law ?? Law::find($item->law_id); $isLocked = ! auth()->user()->isSubscriber() && (bool) ($law?->is_locked ?? false); - $text = $q->text ?? ''; + $text = $item->text ?? ''; $searchQuoted = preg_quote($searchTerm, '/'); - - // Exact regex match logic from ArtController - if (preg_match('/\b(.{0,50})\b('.$searchQuoted.')\b(.{0,50})\b/i', $text, $matches)) { + if (!empty($searchTerm) && preg_match('/\b(.{0,50})\b('.$searchQuoted.')\b(.{0,50})\b/i', $text, $matches)) { $context = trim($matches[1] ?? '').' '.($matches[2] ?? '').' '.trim($matches[3] ?? ''); } else { $context = $text; } return [ - 'id' => $q->id, - 'title' => $q->title, - 'number' => $q->number, - 'text' => $isLocked ? null : $context, - 'is_locked' => $isLocked, - 'route' => array_values($this->route($modelClass, $q)), - 'category' => optional($law?->category)->name, - 'law' => $law?->title, - 'count_art' => $law?->arts?->count() ?? 0, - 'count_volum' => $law?->volums?->count() ?? 0, - 'price' => $law?->price, - 'image' => $law?->image, - - 'chapter' => $q->chapter != null ? [ - 'id' => $q->chapter->id, - 'title' => $q->chapter->title, - 'number' => $q->chapter->number, - ] : null, - 'part' => $q->part != null ? [ - 'id' => $q->part->id, - 'title' => $q->part->title, - 'number' => $q->part->number, - ] : null, - 'volum' => $q->volum != null ? [ - 'id' => $q->volum->id, - 'title' => $q->volum->title, - 'number' => $q->volum->number, - ] : null, - 'law_object' => $q->law != null ? [ - 'id' => $q->law->id, - 'title' => $q->law->title, - 'number' => $q->law->number, - ] : null, - 'book' => $q->book != null ? [ - 'id' => $q->book->id, - 'title' => $q->book->title, - 'number' => $q->book->number, - ] : null, - 'section' => $q->section != null ? [ - 'id' => $q->section->id, - 'title' => $q->section->title, - 'number' => $q->section->number, - ] : null, - 'gate' => $q->gate != null ? [ - 'id' => $q->gate->id, - 'title' => $q->gate->title, - 'number' => $q->gate->number, - ] : null, + 'id' => $item->id, + 'title' => $item->title, + 'number' => $item->number, + 'text' => $isLocked ? null : $context, + 'is_locked' => $isLocked, + 'route' => array_values($this->route($modelClass, $item)), + 'category' => optional($law?->category)->name, + 'law' => $law?->title ?? '', // STRICLY STRING! Prevents Android App Crash! + 'count_art' => $law?->arts?->count() ?? 0, + 'count_volums' => $law?->volums?->count() ?? 0, // WITH S + 'count_volum' => $law?->volums?->count() ?? 0, // WITHOUT S (Fallback) + 'price' => $law?->price, + 'image' => $law?->image, ]; } @@ -462,11 +424,7 @@ class GlobalSearchApiController extends Controller 'number' => $item->number, 'is_locked' => $isLocked, 'route' => array_values($this->route($modelClass, $item)), - 'law' => $law ? [ - 'id' => $law->id, - 'title' => $law->title, - 'number' => $law->number, - ] : null, + 'law' => $law?->title ?? '', // STRICTLY STRING ]; if ((new $modelClass)->getTable() == 'laws') { @@ -501,6 +459,3 @@ class GlobalSearchApiController extends Controller return []; } } - - - -- 2.49.1