active() ->forAccessLevel() ->orderByDesc('is_pinned') ->orderByDesc('published_at'); if ($request->filled('type')) { $query->byContentType($request->type); } if ($request->filled('category')) { $query->whereHas('categories', function ($q) use ($request) { $q->where('article_categories.slug', $request->category); }); } if ($request->filled('tag')) { $query->whereHas('tags', function ($q) use ($request) { $q->where('article_tags.slug', $request->tag); }); } if ($request->filled('search')) { $search = $request->search; $query->where(function ($q) use ($search) { $q->where('title', 'like', "%{$search}%") ->orWhere('content', 'like', "%{$search}%"); }); } $articles = $query->paginate($request->integer('per_page', 12)); return ArticleCollectionResource::collection($articles); } public function show(string $slug) { $article = Article::with(['categories', 'tags', 'attachments']) ->active() ->forAccessLevel() ->where('slug', $slug) ->firstOrFail(); $article->incrementViewCount(); // Get related articles (same content_type, excluding current) $related = Article::with(['categories']) ->active() ->forAccessLevel() ->where('content_type', $article->content_type) ->where('id', '!=', $article->id) ->orderByDesc('published_at') ->limit(4) ->get(); return response()->json([ 'data' => new ArticleResource($article), 'related' => ArticleCollectionResource::collection($related), ]); } public function downloadAttachment(string $slug, int $attachmentId) { $article = Article::active() ->forAccessLevel() ->where('slug', $slug) ->firstOrFail(); $attachment = ArticleAttachment::where('article_id', $article->id) ->findOrFail($attachmentId); $attachment->incrementDownloadCount(); return DownloadFile::fromDisk( disk: 'public', path: $attachment->file_path, downloadName: $attachment->original_filename ); } }