feat: add reusable gallery carousel and ranking feed infrastructure
This commit is contained in:
@@ -113,6 +113,80 @@ final class ArtworkSearchService
|
||||
});
|
||||
}
|
||||
|
||||
// ── Category / Content-Type page sorts ────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Meilisearch sort fields per alias.
|
||||
* Used by categoryPageSort() and contentTypePageSort().
|
||||
*/
|
||||
private const CATEGORY_SORT_FIELDS = [
|
||||
'trending' => ['trending_score_24h:desc', 'created_at:desc'],
|
||||
'fresh' => ['created_at:desc'],
|
||||
'top-rated' => ['awards_received_count:desc', 'favorites_count:desc'],
|
||||
'favorited' => ['favorites_count:desc', 'trending_score_24h:desc'],
|
||||
'downloaded' => ['downloads_count:desc', 'trending_score_24h:desc'],
|
||||
'oldest' => ['created_at:asc'],
|
||||
];
|
||||
|
||||
/** Cache TTL (seconds) per sort alias for category pages. */
|
||||
private const CATEGORY_SORT_TTL = [
|
||||
'trending' => 300, // 5 min
|
||||
'fresh' => 120, // 2 min
|
||||
'top-rated' => 600, // 10 min
|
||||
'favorited' => 300,
|
||||
'downloaded' => 300,
|
||||
'oldest' => 600,
|
||||
];
|
||||
|
||||
/**
|
||||
* Artworks for a single category page, sorted via Meilisearch.
|
||||
* Default sort: trending (trending_score_24h:desc).
|
||||
*
|
||||
* Cache key pattern: category.{slug}.{sort}.{page}
|
||||
* TTL varies by sort (see spec: 5/2/10 min).
|
||||
*/
|
||||
public function categoryPageSort(string $categorySlug, string $sort = 'trending', int $perPage = 24): LengthAwarePaginator
|
||||
{
|
||||
$sort = array_key_exists($sort, self::CATEGORY_SORT_FIELDS) ? $sort : 'trending';
|
||||
$page = (int) request()->get('page', 1);
|
||||
$ttl = self::CATEGORY_SORT_TTL[$sort] ?? self::CACHE_TTL;
|
||||
$cacheKey = "category.{$categorySlug}.{$sort}.{$page}";
|
||||
|
||||
return Cache::remember($cacheKey, $ttl, function () use ($categorySlug, $sort, $perPage) {
|
||||
return Artwork::search('')
|
||||
->options([
|
||||
'filter' => self::BASE_FILTER . ' AND category = "' . addslashes($categorySlug) . '"',
|
||||
'sort' => self::CATEGORY_SORT_FIELDS[$sort],
|
||||
])
|
||||
->paginate($perPage);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Artworks for a content-type root page, sorted via Meilisearch.
|
||||
* Default sort: trending.
|
||||
*
|
||||
* Cache key pattern: content_type.{slug}.{sort}.{page}
|
||||
*/
|
||||
public function contentTypePageSort(string $contentTypeSlug, string $sort = 'trending', int $perPage = 24): LengthAwarePaginator
|
||||
{
|
||||
$sort = array_key_exists($sort, self::CATEGORY_SORT_FIELDS) ? $sort : 'trending';
|
||||
$page = (int) request()->get('page', 1);
|
||||
$ttl = self::CATEGORY_SORT_TTL[$sort] ?? self::CACHE_TTL;
|
||||
$cacheKey = "content_type.{$contentTypeSlug}.{$sort}.{$page}";
|
||||
|
||||
return Cache::remember($cacheKey, $ttl, function () use ($contentTypeSlug, $sort, $perPage) {
|
||||
return Artwork::search('')
|
||||
->options([
|
||||
'filter' => self::BASE_FILTER . ' AND content_type = "' . addslashes($contentTypeSlug) . '"',
|
||||
'sort' => self::CATEGORY_SORT_FIELDS[$sort],
|
||||
])
|
||||
->paginate($perPage);
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Related artworks: same tags, different artwork, ranked by views + likes.
|
||||
* Limit 12.
|
||||
|
||||
Reference in New Issue
Block a user