format('Y-m-d') . ".{$limit}"; return Cache::remember($cacheKey, $ttl, fn (): Collection => $this->selectSpotlight($limit)); } /** * Return high-quality older artworks for feed blending ("curated" pool). * Excludes artworks newer than $olderThanDays to keep them out of the * "fresh" section yet available for blending. * * Cached per (limit, olderThanDays) tuple and rotated daily. */ public function getCurated(int $limit = 12, int $olderThanDays = 7): Collection { if (! EarlyGrowth::enabled()) { return collect(); } $ttl = (int) config('early_growth.cache_ttl.spotlight', 3600); $cacheKey = 'egs.curated.' . now()->format('Y-m-d') . ".{$limit}.{$olderThanDays}"; return Cache::remember($cacheKey, $ttl, fn (): Collection => $this->selectCurated($limit, $olderThanDays)); } // ─── Private selection logic ────────────────────────────────────────────── /** * Select spotlight artworks. * Uses a date-based seed for deterministic daily rotation. * Fetches 3× the needed count and selects the top-ranked subset. */ private function selectSpotlight(int $limit): Collection { $seed = (int) now()->format('Ymd'); // Artworks published > 7 days ago with meaningful ranking score return Artwork::query() ->public() ->published() ->with([ 'user:id,name,username', 'user.profile:user_id,avatar_hash', 'categories:id,name,slug,content_type_id,parent_id,sort_order', ]) ->leftJoin('artwork_stats as _ast', '_ast.artwork_id', '=', 'artworks.id') ->select('artworks.*') ->where('artworks.published_at', '<=', now()->subDays(7)) // Blend ranking quality with daily-seeded randomness so spotlight varies ->orderByRaw("COALESCE(_ast.ranking_score, 0) * 0.6 + RAND({$seed}) * 0.4 DESC") ->limit($limit * 3) ->get() ->sortByDesc(fn ($a) => optional($a->artworkStats)->ranking_score ?? 0) ->take($limit) ->values(); } /** * Select curated older artworks for feed blending. */ private function selectCurated(int $limit, int $olderThanDays): Collection { $seed = (int) now()->format('Ymd'); return Artwork::query() ->public() ->published() ->with([ 'user:id,name,username', 'user.profile:user_id,avatar_hash', 'categories:id,name,slug,content_type_id,parent_id,sort_order', ]) ->leftJoin('artwork_stats as _ast2', '_ast2.artwork_id', '=', 'artworks.id') ->select('artworks.*') ->where('artworks.published_at', '<=', now()->subDays($olderThanDays)) ->orderByRaw("COALESCE(_ast2.ranking_score, 0) * 0.7 + RAND({$seed}) * 0.3 DESC") ->limit($limit) ->get() ->values(); } }