map(function ($category) { return [ 'id' => $category->id, 'name' => $category->name, ]; }); return $this->success($categories, 'Success', 'Bylaw Categories List'); } public function authorities() { $authorities = BylawLegalAuthority::all()->map(function ($authority) { return [ 'id' => $authority->id, 'name' => $authority->name, 'authority_type' => $authority->authority_type, ]; }); return $this->success($authorities, 'Success', 'Bylaw Authorities List'); } public function index(Request $request) { $request->validate([ 'category_id' => 'nullable|exists:bylaw_subject_categories,id', 'approval_authority_id' => 'nullable|exists:bylaw_legal_authorities,id', 'bylaw_type' => 'nullable|in:executive,delegated,independent', 'sort_by' => 'nullable|in:date,number', 'sort' => 'nullable|in:asc,desc', 'per_page' => 'nullable|integer|min:1|max:100', ]); $query = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) ->where('is_published', true); if ($request->filled('category_id')) { $query->where('category_id', $request->category_id); } if ($request->filled('approval_authority_id')) { $query->where('approval_authority_id', $request->approval_authority_id); } if ($request->filled('bylaw_type')) { $query->where('bylaw_type', $request->bylaw_type); } $sortBy = $request->input('sort_by', 'date'); $sortOrder = $request->input('sort', 'desc'); $sortColumn = $sortBy === 'number' ? 'promulgation_number' : 'approval_date'; if ($sortBy === 'number') { $query->orderByRaw("LENGTH({$sortColumn}) {$sortOrder}")->orderBy($sortColumn, $sortOrder); } else { $query->orderBy($sortColumn, $sortOrder); } $perPage = min(max((int) $request->input('per_page', 15), 10), 100); $paginator = $query->paginate($perPage); // واکشی بهینه گره‌ها از دیتابیس $documentIds = collect($paginator->items())->pluck('id')->toArray(); $allNodes = collect(); if (!empty($documentIds)) { $allNodes = DB::table('bylaw_nodes') ->whereIn('document_id', $documentIds) ->orderBy('sort_order', 'asc') ->get(); } $items = collect($paginator->items())->map(function ($item) use ($allNodes) { // حل مشکل فیلتر سخت‌گیرانه (Strict) در کالکشن‌ها $itemNodes = $allNodes->filter(function($node) use ($item) { return $node->document_id == $item->id; }); return $this->formatBylaw($item, $itemNodes); }); $responseData = [ 'items' => $items, 'current_page' => $paginator->currentPage(), 'last_page' => $paginator->lastPage(), 'per_page' => $paginator->perPage(), 'total' => $paginator->total(), ]; return $this->success($responseData, 'Success', 'Bylaws Fetched'); } public function single($id) { $bylaw = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) ->where('is_published', true) ->find($id); if (!$bylaw) { return $this->failed([], [ 'title' => 'خطا', 'message' => 'آیین‌نامه مورد نظر یافت نشد.' ], 404); } // واکشی خام گره‌ها برای توضیحات کلی $rawNodes = DB::table('bylaw_nodes') ->where('document_id', $bylaw->id) ->orderBy('sort_order', 'asc') ->get(); $formatted = $this->formatBylaw($bylaw, $rawNodes); if (!$formatted['is_locked']) { $eloquentNodes = BylawNode::with('type') ->where('document_id', $bylaw->id) ->orderBy('sort_order', 'asc') ->get(); $formatted['nodes'] = $eloquentNodes->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, ]; })->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([ 'search' => 'nullable|string', 'sort_by' => 'nullable|in:date,number', 'sort' => 'nullable|in:asc,desc', 'per_page' => 'nullable|integer|min:1|max:100', ]); $query = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) ->where('is_published', true); if ($request->filled('search')) { $searchTerm = $request->input('search'); $query->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}%") ->orWhere('title', 'LIKE', "%{$searchTerm}%"); }); }); } $sortBy = $request->input('sort_by', 'date'); $sortOrder = $request->input('sort', 'desc'); $sortColumn = $sortBy === 'number' ? 'promulgation_number' : 'approval_date'; if ($sortBy === 'number') { $query->orderByRaw("LENGTH({$sortColumn}) {$sortOrder}")->orderBy($sortColumn, $sortOrder); } else { $query->orderBy($sortColumn, $sortOrder); } $perPage = min(max((int) $request->input('per_page', 15), 10), 100); $paginator = $query->paginate($perPage); $documentIds = collect($paginator->items())->pluck('id')->toArray(); $allNodes = collect(); if (!empty($documentIds)) { $allNodes = DB::table('bylaw_nodes') ->whereIn('document_id', $documentIds) ->orderBy('sort_order', 'asc') ->get(); } $items = collect($paginator->items())->map(function ($item) use ($allNodes) { $itemNodes = $allNodes->filter(function($node) use ($item) { return $node->document_id == $item->id; }); return $this->formatBylaw($item, $itemNodes); }); $responseData = [ 'items' => $items, 'current_page' => $paginator->currentPage(), 'last_page' => $paginator->lastPage(), 'per_page' => $paginator->perPage(), 'total' => $paginator->total(), ]; return $this->success($responseData, 'Success', 'Search Results Fetched'); } public function fast_search(Request $request) { $searchTerm = $request->input('search'); $query = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) ->where('is_published', true); if (!empty($searchTerm)) { $query->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}%") ->orWhere('title', 'LIKE', "%{$searchTerm}%"); }); }); } $count = $query->count(); $items = $query->orderBy('approval_date', 'desc')->take(5)->get(); $documentIds = $items->pluck('id')->toArray(); $allNodes = collect(); if (!empty($documentIds)) { $allNodes = DB::table('bylaw_nodes') ->whereIn('document_id', $documentIds) ->orderBy('sort_order', 'asc') ->get(); } $formattedItems = $items->map(function ($item) use ($allNodes) { $itemNodes = $allNodes->filter(function($node) use ($item) { return $node->document_id == $item->id; }); return $this->formatBylaw($item, $itemNodes); }); return $this->success([ 'items' => $formattedItems, 'count' => $count ], 'Success', 'Fast Search Results'); } public function pager($id) { $current = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) ->where('is_published', true) ->find($id); if (!$current) { return $this->failed([], [ 'title' => 'خطا', 'message' => 'آیین‌نامه مورد نظر یافت نشد.' ], 404); } $previousItem = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) ->where('is_published', true) ->where(function ($query) use ($current) { if ($current->approval_date) { $query->where('approval_date', '<', $current->approval_date) ->orWhere(function ($subQuery) use ($current) { $subQuery->where('approval_date', '=', $current->approval_date) ->where('id', '<', $current->id); }); } else { $query->whereNull('approval_date')->where('id', '<', $current->id); } }) ->orderBy('approval_date', 'desc') ->orderBy('id', 'desc') ->first(); $nextItem = BylawDocument::with(['category', 'approvalAuthority', 'promulgationAuthority']) ->where('is_published', true) ->where(function ($query) use ($current) { if ($current->approval_date) { $query->where('approval_date', '>', $current->approval_date) ->orWhere(function ($subQuery) use ($current) { $subQuery->where('approval_date', '=', $current->approval_date) ->where('id', '>', $current->id); }); } else { $query->whereNotNull('approval_date') ->orWhere(function ($subQuery) use ($current) { $subQuery->whereNull('approval_date')->where('id', '>', $current->id); }); } }) ->orderBy('approval_date', 'asc') ->orderBy('id', 'asc') ->first(); $itemsToFormat = array_filter([$previousItem, $current, $nextItem]); $documentIds = collect($itemsToFormat)->pluck('id')->toArray(); $allNodes = collect(); if (!empty($documentIds)) { $allNodes = DB::table('bylaw_nodes') ->whereIn('document_id', $documentIds) ->orderBy('sort_order', 'asc') ->get(); } $results = array_values(array_map(function($item) use ($allNodes) { $itemNodes = $allNodes->filter(function($node) use ($item) { return $node->document_id == $item->id; }); return $this->formatBylaw($item, $itemNodes); }, $itemsToFormat)); return $this->success($results, 'Success', 'Pager Window Details'); } private function isLockedForCurrentUser($item): bool { return ! auth()->user()->isSubscriber() && (bool) ($item->is_locked ?? false); } /** * BULLETPROOF DESCRIPTION GENERATOR */ private function formatBylaw($item, $itemNodes = null) { $isLocked = $this->isLockedForCurrentUser($item); $description = $item->description; $cleanDesc = trim(str_replace([' ', '‌', "\r", "\n", "\t", ' '], '', strip_tags($description ?? ''))); // اگر فیلد اصلی description خالی بود (که در خزش فعلی خالی است)، به سراغ متن اصلی می‌رویم if (strlen($cleanDesc) < 5) { // شبکه اطمینان: اگر متغیر حاوی گره‌ها به هر دلیلی خالی بود، مستقیماً از دیتابیس می‌گیریم if ($itemNodes === null || (is_countable($itemNodes) && count($itemNodes) === 0)) { $itemNodes = DB::table('bylaw_nodes') ->where('document_id', $item->id) ->orderBy('sort_order', 'asc') ->get(); } $nodeContents = []; foreach ($itemNodes as $node) { // استخراج امن متن از آبجکت یا آرایه (برای جلوگیری از خطاهای احتمالی) $contentRaw = is_array($node) ? ($node['node_content'] ?? '') : ($node->node_content ?? ''); $content = trim(strip_tags($contentRaw)); if (!empty($content)) { $nodeContents[] = $content; } } $description = implode("\n\n", $nodeContents); } 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, 'official_gazette_number' => $item->official_gazette_number, 'official_gazette_date' => $item->official_gazette_date, 'execution_date' => $item->execution_date, '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 : $description, // متن کامل از node_content اینجا قرار می‌گیرد 'is_locked' => $isLocked, ]; } }