published() ->featured() ->orderByDesc('published_at') ->first(); $query = NewsArticle::with('author', 'category') ->published() ->orderByDesc('published_at'); if ($featured) { $query->where('id', '!=', $featured->id); } $articles = $query->paginate($perPage); $categories = NewsCategory::active()->withCount('publishedArticles')->ordered()->get(); $trending = NewsArticle::published() ->orderByDesc('views') ->limit(config('news.trending_limit', 5)) ->get(['id', 'title', 'slug', 'views', 'published_at']); $tags = NewsTag::has('articles')->orderBy('name')->get(); return view('news.index', [ 'featured' => $featured, 'articles' => $articles, 'categories' => $categories, 'trending' => $trending, 'tags' => $tags, ]); } // ----------------------------------------------------------------------- // Category page — /news/category/{slug} // ----------------------------------------------------------------------- public function category(Request $request, string $slug) { $category = NewsCategory::where('slug', $slug)->where('is_active', true)->firstOrFail(); $perPage = config('news.articles_per_page', 12); $articles = NewsArticle::with('author', 'category') ->published() ->byCategory($category->id) ->orderByDesc('published_at') ->paginate($perPage); $categories = NewsCategory::active()->withCount('publishedArticles')->ordered()->get(); return view('news.category', [ 'category' => $category, 'articles' => $articles, 'categories' => $categories, ]); } // ----------------------------------------------------------------------- // Tag page — /news/tag/{slug} // ----------------------------------------------------------------------- public function tag(Request $request, string $slug) { $tag = NewsTag::where('slug', $slug)->firstOrFail(); $perPage = config('news.articles_per_page', 12); $articles = NewsArticle::with('author', 'category') ->published() ->whereHas('tags', fn ($q) => $q->where('news_tags.slug', $slug)) ->orderByDesc('published_at') ->paginate($perPage); $categories = NewsCategory::active()->withCount('publishedArticles')->ordered()->get(); return view('news.tag', [ 'tag' => $tag, 'articles' => $articles, 'categories' => $categories, ]); } // ----------------------------------------------------------------------- // Article page — /news/{slug} // ----------------------------------------------------------------------- public function show(Request $request, string $slug) { $article = NewsArticle::with('author', 'category', 'tags') ->published() ->where('slug', $slug) ->firstOrFail(); // Track view (once per session / IP) $this->trackView($request, $article); // Related articles (same category, excluding current) $related = NewsArticle::with('author') ->published() ->when($article->category_id, fn ($q) => $q->where('category_id', $article->category_id)) ->where('id', '!=', $article->id) ->orderByDesc('published_at') ->limit(config('news.related_limit', 4)) ->get(); return view('news.show', [ 'article' => $article, 'related' => $related, ]); } // ----------------------------------------------------------------------- // Helpers // ----------------------------------------------------------------------- private function trackView(Request $request, NewsArticle $article): void { $ip = $request->ip(); $userId = Auth::id(); $session = 'news_view_' . $article->id; if ($request->session()->has($session)) { return; } NewsView::create([ 'article_id' => $article->id, 'user_id' => $userId, 'ip' => $ip, 'created_at' => now(), ]); $article->incrementViews(); $request->session()->put($session, true); } }