*/ public function build(WebPortalContext $context, string $slug, ?int $year): array { $page = Paginator::resolveCurrentPage(); $build = fn (): array => $this->buildFresh($context, $slug, $year, $page); if ($page > self::MaxCachedPage) { return $build(); } return WebPageCache::remember( WebPageCache::key('category-archive', [$context->portal->value, $context->language, $slug, (string) $year, 'page', $page]), $build ); } /** * Jahre mit Meldungen einer Branche (inkl. Unterkategorien), absteigend * — auch für die Jahresleiste auf der Branchenseite. * * @param list $categoryIds * @param list $portalValues * @return array Jahr => Anzahl */ public static function yearsWithCounts(array $categoryIds, array $portalValues, string $language): array { $yearExpression = DB::connection()->getDriverName() === 'sqlite' ? "CAST(strftime('%Y', published_at) AS INTEGER)" : 'YEAR(published_at)'; return PressRelease::query() ->withoutGlobalScope(PortalScope::class) ->forPortalValues($portalValues) ->forLanguage($language) ->visibleInArchive() ->whereIn('category_id', $categoryIds) ->selectRaw($yearExpression.' as year, COUNT(*) as releases_count') ->groupBy('year') ->orderByDesc('year') ->pluck('releases_count', 'year') ->map(fn ($count): int => (int) $count) ->all(); } /** * @return array */ private function buildFresh(WebPortalContext $context, string $slug, ?int $year, int $page): array { $portalValues = $context->portalValues; $language = $context->language; $category = Category::query() ->whereIn('portal', $portalValues) ->where('is_active', true) ->whereHas('translations', fn (Builder $query) => $query->where('locale', $language)->where('slug', $slug)) ->with([ 'translations' => fn ($query) => $query->where('locale', $language), 'parent.translations' => fn ($query) => $query->where('locale', $language), ]) ->first(); if (! $category) { abort(404); } $categoryIds = Category::query() ->where('parent_id', $category->id) ->where('is_active', true) ->pluck('id') ->prepend($category->id) ->unique() ->values() ->all(); $years = self::yearsWithCounts($categoryIds, $portalValues, $language); if ($years === []) { abort(404); } // Ohne Jahr: das jüngste Jahr mit Meldungen (nicht das Kalenderjahr — // eine Branche ohne Meldung im laufenden Jahr hätte sonst eine leere Seite). $year ??= (int) array_key_first($years); if (! isset($years[$year])) { abort(404); } $releases = PressRelease::query() ->withoutGlobalScope(PortalScope::class) ->forPortalValues($portalValues) ->forLanguage($language) ->visibleInArchive() ->whereIn('category_id', $categoryIds) ->whereBetween('published_at', [ now()->setDate($year, 1, 1)->startOfDay(), now()->setDate($year, 12, 31)->endOfDay(), ]) ->with([ 'company:id,name,slug', 'category.translations' => fn ($query) => $query->where('locale', $language), ]) ->orderByDesc('published_at') ->paginate(self::PerPage, ['id', 'portal', 'language', 'slug', 'title', 'category_id', 'company_id', 'published_at', 'content_score'], 'page', $page); if ($page > 1 && $releases->isEmpty()) { abort(404); } return [ 'category' => $category, 'categoryAlternateSlugs' => $category->translations()->whereIn('locale', ['de', 'en'])->pluck('slug', 'locale')->all(), 'year' => $year, 'years' => $years, 'releases' => $releases, 'totalReleases' => array_sum($years), ]; } }