From 3b9fcd76dfdf3254eed1ef42e342e96637bc1b4b Mon Sep 17 00:00:00 2001 From: "m.razaghi" Date: Fri, 10 Jul 2026 23:22:56 +0330 Subject: [PATCH] updated apis --- .../Controllers/api/BylawApiController.php | 91 +++++++++++++++++-- .../api/GlobalSearchApiController.php | 32 ++++--- 2 files changed, 102 insertions(+), 21 deletions(-) diff --git a/app/Http/Controllers/api/BylawApiController.php b/app/Http/Controllers/api/BylawApiController.php index 1217e0c..e7aaf28 100644 --- a/app/Http/Controllers/api/BylawApiController.php +++ b/app/Http/Controllers/api/BylawApiController.php @@ -6,6 +6,8 @@ use App\Http\Controllers\Controller; use App\Models\BylawDocument; use App\Models\BylawLegalAuthority; use App\Models\BylawSubjectCategory; +use App\Models\BylawNode; +use App\Models\BylawCodificationLink; use App\Traits\BaseApiResponse; use Illuminate\Http\Request; @@ -49,7 +51,7 @@ class BylawApiController extends Controller 'per_page' => 'nullable|integer|min:1|max:100', ]); - $query = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) + $query = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority', 'nodes']) ->where('is_published', true); if ($request->filled('category_id')) { @@ -92,6 +94,63 @@ class BylawApiController extends Controller return $this->success($responseData, 'Success', 'Bylaws Fetched'); } + public function single($id) + { + $bylaw = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority', 'nodes']) + ->where('is_published', true) + ->find($id); + + if (!$bylaw) { + return $this->failed([], [ + 'title' => 'خطا', + 'message' => 'آیین‌نامه مورد نظر یافت نشد.' + ], 404); + } + + $formatted = $this->formatBylaw($bylaw); + + if (!$formatted['is_locked']) { + $nodes = BylawNode::with('type') + ->where('document_id', $bylaw->id) + ->orderBy('sort_order', 'asc') + ->get() + ->map(function ($node) { + return [ + 'id' => $node->id, + 'parent_id' => $node->parent_id, + 'node_type' => optional($node->type)->name, + 'node_identifier' => $node->node_identifier, + 'title' => $node->title, + 'node_content' => $node->node_content, + 'sort_order' => $node->sort_order, + 'valid_from' => $node->valid_from, + 'valid_to' => $node->valid_to, + 'node_status' => $node->node_status, + ]; + }); + + $formatted['nodes'] = $nodes->toArray(); + + $links = BylawCodificationLink::where('source_entity_type', 'bylaw_doc') + ->where('source_entity_id', $bylaw->id) + ->get() + ->map(function ($link) { + return [ + 'id' => $link->id, + 'target_entity_type' => $link->target_entity_type, + 'target_entity_id' => $link->target_entity_id, + 'link_type' => $link->link_type, + 'effective_date' => $link->effective_date, + 'justification' => $link->justification, + ]; + }); + + $formatted['links'] = $links->toArray(); + } + + return $this->success($formatted, 'Success', 'Bylaw Document Details Fetched'); + } + public function search(Request $request) { $request->validate([ @@ -101,7 +160,7 @@ class BylawApiController extends Controller 'per_page' => 'nullable|integer|min:1|max:100', ]); - $query = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) + $query = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority', 'nodes']) ->where('is_published', true); if ($request->filled('search')) { @@ -111,7 +170,10 @@ class BylawApiController extends Controller $q->where('title', 'LIKE', "%{$searchTerm}%") ->orWhere('description', 'LIKE', "%{$searchTerm}%") ->orWhere('promulgation_number', 'LIKE', "%{$searchTerm}%") - ->orWhere('official_gazette_number', 'LIKE', "%{$searchTerm}%"); + ->orWhere('official_gazette_number', 'LIKE', "%{$searchTerm}%") + ->orWhereHas('nodes', function ($nodeQuery) use ($searchTerm) { + $nodeQuery->where('node_content', 'LIKE', "%{$searchTerm}%"); + }); }); } @@ -147,7 +209,7 @@ class BylawApiController extends Controller { $searchTerm = $request->input('search'); - $query = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) + $query = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority', 'nodes']) ->where('is_published', true); if (!empty($searchTerm)) { @@ -155,7 +217,10 @@ class BylawApiController extends Controller $q->where('title', 'LIKE', "%{$searchTerm}%") ->orWhere('description', 'LIKE', "%{$searchTerm}%") ->orWhere('promulgation_number', 'LIKE', "%{$searchTerm}%") - ->orWhere('official_gazette_number', 'LIKE', "%{$searchTerm}%"); + ->orWhere('official_gazette_number', 'LIKE', "%{$searchTerm}%") + ->orWhereHas('nodes', function ($nodeQuery) use ($searchTerm) { + $nodeQuery->where('node_content', 'LIKE', "%{$searchTerm}%"); + }); }); } @@ -172,7 +237,7 @@ class BylawApiController extends Controller public function pager($id) { - $current = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) + $current = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority', 'nodes']) ->where('is_published', true) ->find($id); @@ -183,7 +248,7 @@ class BylawApiController extends Controller ], 404); } - $previousItem = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) + $previousItem = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority', 'nodes']) ->where('is_published', true) ->where(function ($query) use ($current) { if ($current->approval_date) { @@ -200,7 +265,7 @@ class BylawApiController extends Controller ->orderBy('id', 'desc') ->first(); - $nextItem = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) + $nextItem = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority', 'nodes']) ->where('is_published', true) ->where(function ($query) use ($current) { if ($current->approval_date) { @@ -238,13 +303,21 @@ class BylawApiController extends Controller { $isLocked = $this->isLockedForCurrentUser($item); + // Map description: If description is null, combine all node_content into one full text! + $description = $item->description; + if (empty($description) && $item->relationLoaded('nodes')) { + $description = $item->nodes->pluck('node_content')->filter()->implode("\n\n"); + } + return [ 'id' => $item->id, 'title' => $item->title, 'category_id' => $item->category_id, 'category_name' => optional($item->category)->name, + 'approval_authority_id' => $item->approval_authority_id, 'approval_authority_name' => optional($item->approvalAuthority)->name, 'approval_date' => $item->approval_date, + 'promulgation_authority_id' => $item->promulgation_authority_id, 'promulgation_authority_name'=> optional($item->promulgationAuthority)->name, 'promulgation_date' => $item->promulgation_date, 'promulgation_number' => $item->promulgation_number, @@ -254,7 +327,7 @@ class BylawApiController extends Controller 'bylaw_type' => $item->bylaw_type, 'global_status' => $item->global_status, 'pdf_file_url' => $isLocked ? null : ($item->pdf_file_path ? asset('storage/' . $item->pdf_file_path) : null), - 'description' => $isLocked ? null : $item->description, + 'description' => $isLocked ? null : $description, 'is_locked' => $isLocked, ]; } diff --git a/app/Http/Controllers/api/GlobalSearchApiController.php b/app/Http/Controllers/api/GlobalSearchApiController.php index 97001d6..4fd122e 100644 --- a/app/Http/Controllers/api/GlobalSearchApiController.php +++ b/app/Http/Controllers/api/GlobalSearchApiController.php @@ -46,14 +46,17 @@ class GlobalSearchApiController extends Controller if (!empty($searchTerm)) { $limitPerTable = 100; // Performance safeguard - // 1. Search Bylaw Documents - $bylaws = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) + // 1. Search Bylaw Documents (Including deeper node_content search) + $bylaws = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority', 'nodes']) ->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}%"); + ->orWhere('official_gazette_number', 'LIKE', "%{$searchTerm}%") + ->orWhereHas('nodes', function ($nodeQuery) use ($searchTerm) { + $nodeQuery->where('node_content', 'LIKE', "%{$searchTerm}%"); + }); })->take($limitPerTable)->get()->map(function($item) { $formatted = $this->formatBylaw($item); $formatted['type'] = 'bylaw'; @@ -130,11 +133,9 @@ class GlobalSearchApiController extends Controller 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 { @@ -142,7 +143,6 @@ class GlobalSearchApiController extends Controller } } 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'] ?? ''); }; @@ -186,19 +186,21 @@ class GlobalSearchApiController extends Controller $results = collect(); $totalCount = 0; - // Take a max of 3 from each table so the quick-search array doesn't become too heavy $limitPerTable = 3; 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}%") ->orWhere('description', 'LIKE', "%{$searchTerm}%") ->orWhere('promulgation_number', 'LIKE', "%{$searchTerm}%") - ->orWhere('official_gazette_number', 'LIKE', "%{$searchTerm}%"); + ->orWhere('official_gazette_number', 'LIKE', "%{$searchTerm}%") + ->orWhereHas('nodes', function ($nodeQuery) use ($searchTerm) { + $nodeQuery->where('node_content', 'LIKE', "%{$searchTerm}%"); + }); }); $totalCount += $bylawQuery->count(); $bylaws = $bylawQuery->take($limitPerTable)->get()->map(function($item) { @@ -297,6 +299,12 @@ class GlobalSearchApiController extends Controller { $isLocked = $this->isLockedForCurrentUserGeneric($item); + // Fallback description to node_content if empty + $description = $item->description; + if (empty($description) && $item->relationLoaded('nodes')) { + $description = $item->nodes->pluck('node_content')->filter()->implode("\n\n"); + } + return [ 'id' => $item->id, 'title' => $item->title, @@ -304,7 +312,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 : $item->description, + 'description' => $isLocked ? null : $description, 'is_locked' => $isLocked, ]; } @@ -357,7 +365,7 @@ class GlobalSearchApiController extends Controller return [ 'id' => $item->id, 'title' => $item->title, - 'number' => $item->number, // Added for sorting capability + 'number' => $item->number, 'text' => $isLocked ? null : $context, 'is_locked' => $isLocked, 'route' => array_values($this->route($modelClass, $item)), @@ -378,7 +386,7 @@ class GlobalSearchApiController extends Controller $payload = [ 'id' => $item->id, 'title' => $item->title, - 'number' => $item->number, // Available for sorting capability + 'number' => $item->number, 'is_locked' => $isLocked, 'route' => array_values($this->route($modelClass, $item)), 'law' => $law?->title ?? '',