diff --git a/app/Http/Controllers/api/GlobalSearchApiController.php b/app/Http/Controllers/api/GlobalSearchApiController.php index 662086b..66b868d 100644 --- a/app/Http/Controllers/api/GlobalSearchApiController.php +++ b/app/Http/Controllers/api/GlobalSearchApiController.php @@ -26,7 +26,7 @@ class GlobalSearchApiController extends Controller use BaseApiResponse; /** - * Massive Search across ALL data types (With dynamic Date/Number Sorting) + * Massive Search across ALL data types (With dynamic Date/Number Sorting & Deep Art Search) */ public function search(Request $request) { @@ -47,8 +47,8 @@ class GlobalSearchApiController extends Controller if (!empty($searchTerm)) { $limitPerTable = 100; // Performance safeguard - // 1. Search Bylaw Documents (Including node_content subquery) - $bylaws = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) + // 1. Search Bylaw Documents + $bylaws = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority', 'nodes']) ->where('is_published', true) ->where(function ($q) use ($searchTerm) { $q->where('title', 'LIKE', "%{$searchTerm}%") @@ -94,10 +94,18 @@ class GlobalSearchApiController extends Controller }); $results = $results->merge($opinions); - // 4. Search Arts - $arts = Art::with(['law.category']) - ->where('title', 'LIKE', "%{$searchTerm}%") - ->orWhere('text', 'LIKE', "%{$searchTerm}%") + // 4. Search Arts (Deep Relational Search & Eager Loading) + $arts = Art::with(['law.category', 'chapter', 'part', 'volum', 'book', 'section', 'gate', 'division', 'branch']) + ->where(function ($q) use ($searchTerm) { + $q->where('title', 'LIKE', "%{$searchTerm}%") + ->orWhere('text', 'LIKE', "%{$searchTerm}%") + ->orWhereHas('law', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }) + ->orWhereHas('volum', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }) + ->orWhereHas('book', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }) + ->orWhereHas('section', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }) + ->orWhereHas('chapter', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }) + ->orWhereHas('part', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }); + }) ->take($limitPerTable)->get()->map(function($item) use ($searchTerm) { $formatted = $this->formatArt($item, $searchTerm, Art::class); $formatted['type'] = 'art'; @@ -194,7 +202,7 @@ class GlobalSearchApiController extends Controller if (!empty($searchTerm)) { // 1. Bylaw Documents - $bylawQuery = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) + $bylawQuery = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority', 'nodes']) ->where('is_published', true) ->where(function ($q) use ($searchTerm) { $q->where('title', 'LIKE', "%{$searchTerm}%") @@ -247,9 +255,17 @@ class GlobalSearchApiController extends Controller $results = $results->merge($opinions); // 4. Search Arts - $artQuery = Art::with(['law.category']) - ->where('title', 'LIKE', "%{$searchTerm}%") - ->orWhere('text', 'LIKE', "%{$searchTerm}%"); + $artQuery = Art::with(['law.category', 'chapter', 'part', 'volum', 'book', 'section', 'gate', 'division', 'branch']) + ->where(function ($q) use ($searchTerm) { + $q->where('title', 'LIKE', "%{$searchTerm}%") + ->orWhere('text', 'LIKE', "%{$searchTerm}%") + ->orWhereHas('law', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }) + ->orWhereHas('volum', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }) + ->orWhereHas('book', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }) + ->orWhereHas('section', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }) + ->orWhereHas('chapter', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }) + ->orWhereHas('part', function ($sub) use ($searchTerm) { $sub->where('title', 'LIKE', "%{$searchTerm}%"); }); + }); $totalCount += $artQuery->count(); $arts = $artQuery->take($limitPerTable)->get()->map(function($item) use ($searchTerm) { $formatted = $this->formatArt($item, $searchTerm, Art::class); @@ -305,14 +321,8 @@ class GlobalSearchApiController extends Controller $isLocked = $this->isLockedForCurrentUserGeneric($item); $description = $item->description; - - // Direct Query Fallback: If description is empty, fetch node_content manually - if (empty($description)) { - $description = BylawNode::where('document_id', $item->id) - ->orderBy('sort_order', 'asc') - ->pluck('node_content') - ->filter() - ->implode("\n\n"); + if (empty($description) && $item->relationLoaded('nodes')) { + $description = $item->nodes->pluck('node_content')->filter()->implode("\n\n"); } return [ @@ -380,11 +390,48 @@ class GlobalSearchApiController extends Controller 'is_locked' => $isLocked, 'route' => array_values($this->route($modelClass, $item)), 'category' => optional($law->category)->name, - 'law' => $law?->title, + 'count_art' => $law?->arts?->count() ?? 0, 'count_volums'=> $law?->volums?->count() ?? 0, 'price' => $law?->price, 'image' => $law?->image, + + // Relational Objects + 'law' => $item->law != null ? [ + 'id' => $item->law->id, + 'title' => $item->law->title, + 'number' => $item->law->number, + ] : null, + 'chapter' => $item->chapter != null ? [ + 'id' => $item->chapter->id, + 'title' => $item->chapter->title, + 'number' => $item->chapter->number, + ] : null, + 'part' => $item->part != null ? [ + 'id' => $item->part->id, + 'title' => $item->part->title, + 'number' => $item->part->number, + ] : null, + 'volum' => $item->volum != null ? [ + 'id' => $item->volum->id, + 'title' => $item->volum->title, + 'number' => $item->volum->number, + ] : null, + 'book' => $item->book != null ? [ + 'id' => $item->book->id, + 'title' => $item->book->title, + 'number' => $item->book->number, + ] : null, + 'section' => $item->section != null ? [ + 'id' => $item->section->id, + 'title' => $item->section->title, + 'number' => $item->section->number, + ] : null, + 'gate' => $item->gate != null ? [ + 'id' => $item->gate->id, + 'title' => $item->gate->title, + 'number' => $item->gate->number, + ] : null, ]; } @@ -399,7 +446,12 @@ class GlobalSearchApiController extends Controller 'number' => $item->number, 'is_locked' => $isLocked, 'route' => array_values($this->route($modelClass, $item)), - 'law' => $law?->title ?? '', + // Keep law payload explicitly as an object to prevent DTO parsing errors + 'law' => $law ? [ + 'id' => $law->id, + 'title' => $law->title, + 'number' => $law->number, + ] : null, ]; if ((new $modelClass)->getTable() == 'laws') { @@ -412,17 +464,18 @@ class GlobalSearchApiController extends Controller private function route($modelClass, $query) { + // Now safely uses eager loaded relations if they exist! if ((new $modelClass)->getTable() == 'art') { $route = array_filter([ - Law::find($query->law_id)?->title, - Volum::find($query->volum_id)?->title, - Book::find($query->book_id)?->title, - Division::find($query->division_id)?->title, - Section::find($query->section_id)?->title, - Chapter::find($query->chapter_id)?->title, - Part::find($query->part_id)?->title, - Gate::find($query->gate_id)?->title, - Branch::find($query->branch_id)?->title, + $query->law->title ?? Law::find($query->law_id)?->title, + $query->volum->title ?? Volum::find($query->volum_id)?->title, + $query->book->title ?? Book::find($query->book_id)?->title, + $query->division->title ?? Division::find($query->division_id)?->title, + $query->section->title ?? Section::find($query->section_id)?->title, + $query->chapter->title ?? Chapter::find($query->chapter_id)?->title, + $query->part->title ?? Part::find($query->part_id)?->title, + $query->gate->title ?? Gate::find($query->gate_id)?->title, + $query->branch->title ?? Branch::find($query->branch_id)?->title, ]); return array_values($route); }