Fixed api issue

This commit is contained in:
m.razaghi
2026-07-13 09:12:26 +03:30
parent 2d6ba2a70d
commit 39e85df27a

View File

@@ -7,7 +7,6 @@ use App\Models\Art;
use App\Models\Book; use App\Models\Book;
use App\Models\Branch; use App\Models\Branch;
use App\Models\BylawDocument; use App\Models\BylawDocument;
use App\Models\BylawNode;
use App\Models\Chapter; use App\Models\Chapter;
use App\Models\Division; use App\Models\Division;
use App\Models\Gate; use App\Models\Gate;
@@ -50,7 +49,7 @@ class GlobalSearchApiController extends Controller
$limitPerTable = 100; $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']) $arts = Art::with(['law.category', 'chapter', 'part', 'volum', 'book', 'section', 'gate'])
->where(function ($q) use ($searchTerm) { ->where(function ($q) use ($searchTerm) {
@@ -66,7 +65,7 @@ class GlobalSearchApiController extends Controller
$staticResults = $staticResults->concat($arts); $staticResults = $staticResults->concat($arts);
// ========================================================= // =========================================================
// 2. Search Laws and Structural Models // 2. Search Laws and Structural Models (Exact match to ArtController)
// ========================================================= // =========================================================
$structuralModels = [ $structuralModels = [
Law::class, Volum::class, Book::class, Division::class, 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']) $bylawsQuery = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority'])
->where('is_published', true) ->where('is_published', true)
@@ -103,22 +102,23 @@ class GlobalSearchApiController extends Controller
->orWhereIn('id', function($subQuery) use ($searchTerm) { ->orWhereIn('id', function($subQuery) use ($searchTerm) {
$subQuery->select('document_id') $subQuery->select('document_id')
->from('bylaw_nodes') ->from('bylaw_nodes')
->where('node_content', 'LIKE', "%{$searchTerm}%"); ->where('node_content', 'LIKE', "%{$searchTerm}%")
->orWhere('title', 'LIKE', "%{$searchTerm}%"); // Fixed: Title added!
}); });
})->take($limitPerTable)->get(); })->take($limitPerTable)->get();
// Safely bulk-fetch nodes for these bylaws // Safely bulk-fetch nodes using DB facade
$docIds = $bylawsQuery->pluck('id')->map(fn($id) => (int)$id)->toArray(); $docIds = $bylawsQuery->pluck('id')->toArray();
$nodesByDoc = collect(); $allNodes = collect();
if (!empty($docIds)) { if (!empty($docIds)) {
$nodesByDoc = BylawNode::whereIn('document_id', $docIds) $allNodes = DB::table('bylaw_nodes')
->whereIn('document_id', $docIds)
->orderBy('sort_order', 'asc') ->orderBy('sort_order', 'asc')
->get() ->get();
->groupBy(fn($node) => (int)$node->document_id);
} }
$bylaws = $bylawsQuery->map(function($item) use ($nodesByDoc) { $bylaws = $bylawsQuery->map(function($item) use ($allNodes) {
$itemNodes = $nodesByDoc->get((int)$item->id, collect()); $itemNodes = $allNodes->where('document_id', $item->id);
$formatted = $this->formatBylaw($item, $itemNodes); $formatted = $this->formatBylaw($item, $itemNodes);
$formatted['type'] = 'bylaw'; $formatted['type'] = 'bylaw';
return $formatted; 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']) $bylawsQuery = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority'])
->where('is_published', true) ->where('is_published', true)
->where(function ($q) use ($searchTerm) { ->where(function ($q) use ($searchTerm) {
@@ -276,24 +276,26 @@ class GlobalSearchApiController extends Controller
->orWhereIn('id', function($subQuery) use ($searchTerm) { ->orWhereIn('id', function($subQuery) use ($searchTerm) {
$subQuery->select('document_id') $subQuery->select('document_id')
->from('bylaw_nodes') ->from('bylaw_nodes')
->where('node_content', 'LIKE', "%{$searchTerm}%"); ->where('node_content', 'LIKE', "%{$searchTerm}%")
->orWhere('title', 'LIKE', "%{$searchTerm}%");
}); });
}); });
$totalCount += $bylawsQuery->count(); $totalCount += $bylawsQuery->count();
$bylawsRaw = $bylawsQuery->take($limitPerTable)->get(); $bylawsRaw = $bylawsQuery->take($limitPerTable)->get();
$docIds = $bylawsRaw->pluck('id')->map(fn($id) => (int)$id)->toArray(); // Safely bulk-fetch nodes using DB facade
$nodesByDoc = collect(); $docIds = $bylawsRaw->pluck('id')->toArray();
$allNodes = collect();
if (!empty($docIds)) { if (!empty($docIds)) {
$nodesByDoc = BylawNode::whereIn('document_id', $docIds) $allNodes = DB::table('bylaw_nodes')
->whereIn('document_id', $docIds)
->orderBy('sort_order', 'asc') ->orderBy('sort_order', 'asc')
->get() ->get();
->groupBy(fn($node) => (int)$node->document_id);
} }
$bylaws = $bylawsRaw->map(function($item) use ($nodesByDoc) { $bylaws = $bylawsRaw->map(function($item) use ($allNodes) {
$itemNodes = $nodesByDoc->get((int)$item->id, collect()); $itemNodes = $allNodes->where('document_id', $item->id);
$formatted = $this->formatBylaw($item, $itemNodes); $formatted = $this->formatBylaw($item, $itemNodes);
$formatted['type'] = 'bylaw'; $formatted['type'] = 'bylaw';
return $formatted; 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) private function formatBylaw($item, $itemNodes = null)
{ {
$isLocked = $this->isLockedForCurrentUserGeneric($item); $isLocked = $this->isLockedForCurrentUserGeneric($item);
$description = $item->description;
if ($itemNodes === null) { // Check if description is practically empty (less than 5 chars = garbage HTML)
$itemNodes = BylawNode::where('document_id', $item->id)->orderBy('sort_order', 'asc')->get(); $cleanDesc = trim(str_replace([' ', '‌', "\r", "\n", "\t", ' '], '', strip_tags($description ?? '')));
if (strlen($cleanDesc) < 5) {
// 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();
}
} }
// Extract content and encode safely (fixes JSON encoding bugs) $nodeContents = [];
$nodeContentAsDescription = $itemNodes->pluck('node_content') if ($itemNodes) {
->map(function ($text) { foreach ($itemNodes as $node) {
$safeText = trim((string)$text); // EXPLICITLY GET node_content
return mb_convert_encoding($safeText, 'UTF-8', 'UTF-8'); $content = trim(strip_tags($node->node_content ?? ''));
}) if (!empty($content)) {
->filter(function ($text) { $nodeContents[] = $content;
return $text !== ''; }
}) }
->implode("\n\n"); }
// If the database nodes were actually empty, fallback to the description column $description = implode("\n\n", $nodeContents);
if (empty($nodeContentAsDescription)) {
$nodeContentAsDescription = mb_convert_encoding(trim((string)$item->description), 'UTF-8', 'UTF-8');
} }
return [ return [
@@ -381,7 +395,7 @@ class GlobalSearchApiController extends Controller
'approval_authority_name' => optional($item->approvalAuthority)->name, 'approval_authority_name' => optional($item->approvalAuthority)->name,
'approval_date' => $item->approval_date, 'approval_date' => $item->approval_date,
'promulgation_number' => $item->promulgation_number, 'promulgation_number' => $item->promulgation_number,
'description' => $isLocked ? null : $nodeContentAsDescription, 'description' => $isLocked ? null : $description,
'is_locked' => $isLocked, '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) private function formatArt($item, $searchTerm, $modelClass)
{ {
$law = $item->law ?? Law::find($item->law_id); $law = $item->law ?? Law::find($item->law_id);
@@ -440,7 +457,7 @@ class GlobalSearchApiController extends Controller
'is_locked' => $isLocked, 'is_locked' => $isLocked,
'route' => array_values($this->route($modelClass, $item)), 'route' => array_values($this->route($modelClass, $item)),
'category' => optional($law?->category)->name, 'category' => optional($law?->category)->name,
'law' => $law?->title ?? '', // STRICLY STRING 'law' => $law?->title ?? '',
'count_art' => $law?->arts?->count() ?? 0, 'count_art' => $law?->arts?->count() ?? 0,
'count_volums' => $law?->volums?->count() ?? 0, 'count_volums' => $law?->volums?->count() ?? 0,
'count_volum' => $law?->volums?->count() ?? 0, 'count_volum' => $law?->volums?->count() ?? 0,
@@ -460,7 +477,7 @@ class GlobalSearchApiController extends Controller
'number' => $item->number, 'number' => $item->number,
'is_locked' => $isLocked, 'is_locked' => $isLocked,
'route' => array_values($this->route($modelClass, $item)), 'route' => array_values($this->route($modelClass, $item)),
'law' => $law?->title ?? '', // STRICTLY STRING 'law' => $law?->title ?? '',
]; ];
if ((new $modelClass)->getTable() == 'laws') { if ((new $modelClass)->getTable() == 'laws') {