From 39e85df27a8e0d64ae92e5ad95c147ea487e6418 Mon Sep 17 00:00:00 2001 From: "m.razaghi" Date: Mon, 13 Jul 2026 09:12:26 +0330 Subject: [PATCH] Fixed api issue --- .../api/GlobalSearchApiController.php | 101 ++++++++++-------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/app/Http/Controllers/api/GlobalSearchApiController.php b/app/Http/Controllers/api/GlobalSearchApiController.php index ad156f9..f4afe07 100644 --- a/app/Http/Controllers/api/GlobalSearchApiController.php +++ b/app/Http/Controllers/api/GlobalSearchApiController.php @@ -7,7 +7,6 @@ use App\Models\Art; use App\Models\Book; use App\Models\Branch; use App\Models\BylawDocument; -use App\Models\BylawNode; use App\Models\Chapter; use App\Models\Division; use App\Models\Gate; @@ -50,7 +49,7 @@ class GlobalSearchApiController extends Controller $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) { @@ -66,7 +65,7 @@ class GlobalSearchApiController extends Controller $staticResults = $staticResults->concat($arts); // ========================================================= - // 2. Search Laws and Structural Models + // 2. Search Laws and Structural Models (Exact match to ArtController) // ========================================================= $structuralModels = [ Law::class, Volum::class, Book::class, Division::class, @@ -91,7 +90,7 @@ class GlobalSearchApiController extends Controller } // ========================================================= - // 3. Search Bylaw Documents (With fixed node_content fetching) + // 3. Search Bylaw Documents (With 100% Bulletproof Node Fetching) // ========================================================= $bylawsQuery = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) ->where('is_published', true) @@ -103,22 +102,23 @@ class GlobalSearchApiController extends Controller ->orWhereIn('id', function($subQuery) use ($searchTerm) { $subQuery->select('document_id') ->from('bylaw_nodes') - ->where('node_content', 'LIKE', "%{$searchTerm}%"); + ->where('node_content', 'LIKE', "%{$searchTerm}%") + ->orWhere('title', 'LIKE', "%{$searchTerm}%"); // Fixed: Title added! }); })->take($limitPerTable)->get(); - // Safely bulk-fetch nodes for these bylaws - $docIds = $bylawsQuery->pluck('id')->map(fn($id) => (int)$id)->toArray(); - $nodesByDoc = collect(); + // Safely bulk-fetch nodes using DB facade + $docIds = $bylawsQuery->pluck('id')->toArray(); + $allNodes = collect(); if (!empty($docIds)) { - $nodesByDoc = BylawNode::whereIn('document_id', $docIds) + $allNodes = DB::table('bylaw_nodes') + ->whereIn('document_id', $docIds) ->orderBy('sort_order', 'asc') - ->get() - ->groupBy(fn($node) => (int)$node->document_id); + ->get(); } - $bylaws = $bylawsQuery->map(function($item) use ($nodesByDoc) { - $itemNodes = $nodesByDoc->get((int)$item->id, collect()); + $bylaws = $bylawsQuery->map(function($item) use ($allNodes) { + $itemNodes = $allNodes->where('document_id', $item->id); $formatted = $this->formatBylaw($item, $itemNodes); $formatted['type'] = 'bylaw'; return $formatted; @@ -265,7 +265,7 @@ class GlobalSearchApiController extends Controller } } - // 3. Bylaw Documents + // 3. Bylaw Documents (With 100% Bulletproof Node Fetching) $bylawsQuery = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) ->where('is_published', true) ->where(function ($q) use ($searchTerm) { @@ -276,24 +276,26 @@ class GlobalSearchApiController extends Controller ->orWhereIn('id', function($subQuery) use ($searchTerm) { $subQuery->select('document_id') ->from('bylaw_nodes') - ->where('node_content', 'LIKE', "%{$searchTerm}%"); + ->where('node_content', 'LIKE', "%{$searchTerm}%") + ->orWhere('title', 'LIKE', "%{$searchTerm}%"); }); }); $totalCount += $bylawsQuery->count(); $bylawsRaw = $bylawsQuery->take($limitPerTable)->get(); - $docIds = $bylawsRaw->pluck('id')->map(fn($id) => (int)$id)->toArray(); - $nodesByDoc = collect(); + // Safely bulk-fetch nodes using DB facade + $docIds = $bylawsRaw->pluck('id')->toArray(); + $allNodes = collect(); if (!empty($docIds)) { - $nodesByDoc = BylawNode::whereIn('document_id', $docIds) + $allNodes = DB::table('bylaw_nodes') + ->whereIn('document_id', $docIds) ->orderBy('sort_order', 'asc') - ->get() - ->groupBy(fn($node) => (int)$node->document_id); + ->get(); } - $bylaws = $bylawsRaw->map(function($item) use ($nodesByDoc) { - $itemNodes = $nodesByDoc->get((int)$item->id, collect()); + $bylaws = $bylawsRaw->map(function($item) use ($allNodes) { + $itemNodes = $allNodes->where('document_id', $item->id); $formatted = $this->formatBylaw($item, $itemNodes); $formatted['type'] = 'bylaw'; return $formatted; @@ -348,30 +350,42 @@ class GlobalSearchApiController extends Controller } /** - * UPDATED: Uses the exact encoding and fallback logic tested in BylawApiController + * BULLETPROOF DESCRIPTION GENERATOR (Matches BylawApiController Exactly) */ private function formatBylaw($item, $itemNodes = null) { $isLocked = $this->isLockedForCurrentUserGeneric($item); + $description = $item->description; - if ($itemNodes === null) { - $itemNodes = BylawNode::where('document_id', $item->id)->orderBy('sort_order', 'asc')->get(); - } + // Check if description is practically empty (less than 5 chars = garbage HTML) + $cleanDesc = trim(str_replace([' ', '‌', "\r", "\n", "\t", ' '], '', strip_tags($description ?? ''))); - // Extract content and encode safely (fixes JSON encoding bugs) - $nodeContentAsDescription = $itemNodes->pluck('node_content') - ->map(function ($text) { - $safeText = trim((string)$text); - return mb_convert_encoding($safeText, 'UTF-8', 'UTF-8'); - }) - ->filter(function ($text) { - return $text !== ''; - }) - ->implode("\n\n"); + if (strlen($cleanDesc) < 5) { - // If the database nodes were actually empty, fallback to the description column - if (empty($nodeContentAsDescription)) { - $nodeContentAsDescription = mb_convert_encoding(trim((string)$item->description), 'UTF-8', 'UTF-8'); + // Absolute Safety Net: If somehow $itemNodes is still null or empty array, FORCE a direct query. + if ($itemNodes === null || (is_object($itemNodes) && $itemNodes->isEmpty())) { + // Verify against DB just in case + $dbNodes = DB::table('bylaw_nodes')->where('document_id', $item->id)->count(); + if ($dbNodes > 0) { + $itemNodes = DB::table('bylaw_nodes') + ->where('document_id', $item->id) + ->orderBy('sort_order', 'asc') + ->get(); + } + } + + $nodeContents = []; + if ($itemNodes) { + foreach ($itemNodes as $node) { + // EXPLICITLY GET node_content + $content = trim(strip_tags($node->node_content ?? '')); + if (!empty($content)) { + $nodeContents[] = $content; + } + } + } + + $description = implode("\n\n", $nodeContents); } return [ @@ -381,7 +395,7 @@ class GlobalSearchApiController extends Controller 'approval_authority_name' => optional($item->approvalAuthority)->name, 'approval_date' => $item->approval_date, 'promulgation_number' => $item->promulgation_number, - 'description' => $isLocked ? null : $nodeContentAsDescription, + 'description' => $isLocked ? null : $description, 'is_locked' => $isLocked, ]; } @@ -418,6 +432,9 @@ class GlobalSearchApiController extends Controller ]; } + /** + * Exact 1:1 match with ArtController format mapping + */ private function formatArt($item, $searchTerm, $modelClass) { $law = $item->law ?? Law::find($item->law_id); @@ -440,7 +457,7 @@ class GlobalSearchApiController extends Controller 'is_locked' => $isLocked, 'route' => array_values($this->route($modelClass, $item)), 'category' => optional($law?->category)->name, - 'law' => $law?->title ?? '', // STRICLY STRING + 'law' => $law?->title ?? '', 'count_art' => $law?->arts?->count() ?? 0, 'count_volums' => $law?->volums?->count() ?? 0, 'count_volum' => $law?->volums?->count() ?? 0, @@ -460,7 +477,7 @@ class GlobalSearchApiController extends Controller 'number' => $item->number, 'is_locked' => $isLocked, 'route' => array_values($this->route($modelClass, $item)), - 'law' => $law?->title ?? '', // STRICTLY STRING + 'law' => $law?->title ?? '', ]; if ((new $modelClass)->getTable() == 'laws') {