Wire admin studio SSR and search infrastructure
This commit is contained in:
@@ -20,6 +20,8 @@ use Illuminate\Pagination\AbstractCursorPaginator;
|
||||
|
||||
class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
{
|
||||
private const CACHE_VERSION = 'v4';
|
||||
|
||||
/**
|
||||
* Meilisearch sort-field arrays per sort alias.
|
||||
* First element is primary sort; subsequent elements are tie-breakers.
|
||||
@@ -28,18 +30,18 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
// ── Nova sort aliases ─────────────────────────────────────────────────
|
||||
// trending_score_24h only covers artworks ≤ 7 days old; use 7d score
|
||||
// and favorites_count as fallbacks so older artworks don't all tie at 0.
|
||||
'trending' => ['trending_score_24h:desc', 'trending_score_7d:desc', 'favorites_count:desc', 'created_at:desc'],
|
||||
'trending' => ['trending_score_24h:desc', 'trending_score_7d:desc', 'favorites_count:desc', 'published_at_ts:desc'],
|
||||
// "New & Hot": 30-day trending window surfaces recently-active artworks.
|
||||
'fresh' => ['trending_score_7d:desc', 'favorites_count:desc', 'created_at:desc'],
|
||||
'fresh' => ['published_at_ts:desc', 'trending_score_7d:desc', 'favorites_count: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'],
|
||||
'favorited' => ['favorites_count:desc', 'trending_score_24h:desc', 'published_at_ts:desc'],
|
||||
'downloaded' => ['downloads_count:desc', 'trending_score_24h:desc', 'published_at_ts:desc'],
|
||||
'oldest' => ['published_at_ts:asc'],
|
||||
// ── Legacy aliases (backward compat) ──────────────────────────────────
|
||||
'latest' => ['created_at:desc'],
|
||||
'popular' => ['views:desc', 'favorites_count:desc'],
|
||||
'liked' => ['likes:desc', 'favorites_count:desc'],
|
||||
'downloads' => ['downloads:desc', 'downloads_count:desc'],
|
||||
'latest' => ['published_at_ts:desc'],
|
||||
'popular' => ['views:desc', 'favorites_count:desc', 'published_at_ts:desc'],
|
||||
'liked' => ['likes:desc', 'favorites_count:desc', 'published_at_ts:desc'],
|
||||
'downloads' => ['downloads:desc', 'downloads_count:desc', 'published_at_ts:desc'],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -66,6 +68,7 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
private const SORT_OPTIONS = [
|
||||
['value' => 'trending', 'label' => '🔥 Trending'],
|
||||
['value' => 'fresh', 'label' => '🆕 Fresh'],
|
||||
['value' => 'latest', 'label' => '🕐 Latest'],
|
||||
['value' => 'top-rated', 'label' => '⭐ Top Rated'],
|
||||
['value' => 'favorited', 'label' => '❤️ Most Favorited'],
|
||||
['value' => 'downloaded', 'label' => '⬇ Most Downloaded'],
|
||||
@@ -88,11 +91,11 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
$ttl = self::SORT_TTL_MAP[$sort] ?? 300;
|
||||
|
||||
$artworks = Cache::remember(
|
||||
"browse.all.catalog-visible.v2.{$sort}.{$page}",
|
||||
"browse.all.catalog-visible." . self::CACHE_VERSION . ".{$sort}.{$page}",
|
||||
$ttl,
|
||||
fn () => $this->search->searchWithThumbnailPreference([
|
||||
'filter' => 'is_public = true AND is_approved = true',
|
||||
'sort' => self::SORT_MAP[$sort] ?? ['created_at:desc'],
|
||||
'sort' => self::SORT_MAP[$sort] ?? ['published_at_ts:desc'],
|
||||
], $perPage, false, $page)
|
||||
);
|
||||
$artworks->getCollection()->transform(fn ($a) => $this->presentArtwork($a));
|
||||
@@ -150,11 +153,11 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
$normalizedPath = trim((string) $path, '/');
|
||||
if ($normalizedPath === '') {
|
||||
$artworks = Cache::remember(
|
||||
"gallery.ct.catalog-visible.v2.{$contentSlug}.{$sort}.{$page}",
|
||||
"gallery.ct.catalog-visible." . self::CACHE_VERSION . ".{$contentSlug}.{$sort}.{$page}",
|
||||
$ttl,
|
||||
fn () => $this->search->searchWithThumbnailPreference([
|
||||
'filter' => 'is_public = true AND is_approved = true AND content_type = "' . $contentSlug . '"',
|
||||
'sort' => self::SORT_MAP[$sort] ?? ['created_at:desc'],
|
||||
'filter' => 'is_public = true AND is_approved = true AND ' . $this->contentTypeFilterClause($contentSlug),
|
||||
'sort' => self::SORT_MAP[$sort] ?? ['published_at_ts:desc'],
|
||||
], $perPage, false, $page)
|
||||
);
|
||||
$artworks->getCollection()->transform(fn ($a) => $this->presentArtwork($a));
|
||||
@@ -192,16 +195,14 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
}
|
||||
|
||||
$categorySlugs = $this->categoryFilterSlugs($category);
|
||||
$categoryFilter = collect($categorySlugs)
|
||||
->map(fn (string $slug) => 'category = "' . addslashes($slug) . '"')
|
||||
->implode(' OR ');
|
||||
$filterExpression = $this->categoryPageFilterExpression($contentSlug, $categorySlugs);
|
||||
|
||||
$artworks = Cache::remember(
|
||||
'gallery.cat.catalog-visible.v2.' . md5($contentSlug . '|' . implode('|', $categorySlugs)) . ".{$sort}.{$page}",
|
||||
'gallery.cat.catalog-visible.' . self::CACHE_VERSION . '.' . md5($contentSlug . '|' . implode('|', $categorySlugs)) . ".{$sort}.{$page}",
|
||||
$ttl,
|
||||
fn () => $this->search->searchWithThumbnailPreference([
|
||||
'filter' => 'is_public = true AND is_approved = true AND (' . $categoryFilter . ')',
|
||||
'sort' => self::SORT_MAP[$sort] ?? ['created_at:desc'],
|
||||
'filter' => $filterExpression,
|
||||
'sort' => self::SORT_MAP[$sort] ?? ['published_at_ts:desc'],
|
||||
], $perPage, false, $page)
|
||||
);
|
||||
$artworks->getCollection()->transform(fn ($a) => $this->presentArtwork($a));
|
||||
@@ -369,6 +370,31 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
return array_values(array_unique($slugs));
|
||||
}
|
||||
|
||||
private function categoryFilterClause(string $categorySlug): string
|
||||
{
|
||||
$quoted = addslashes($categorySlug);
|
||||
|
||||
return '(category = "' . $quoted . '" OR categories = "' . $quoted . '")';
|
||||
}
|
||||
|
||||
private function categoryPageFilterExpression(string $contentTypeSlug, array $categorySlugs): string
|
||||
{
|
||||
$categoryFilter = collect($categorySlugs)
|
||||
->map(fn (string $slug) => $this->categoryFilterClause($slug))
|
||||
->implode(' OR ');
|
||||
|
||||
return 'is_public = true AND is_approved = true AND '
|
||||
. $this->contentTypeFilterClause($contentTypeSlug)
|
||||
. ' AND (' . $categoryFilter . ')';
|
||||
}
|
||||
|
||||
private function contentTypeFilterClause(string $contentTypeSlug): string
|
||||
{
|
||||
$quoted = addslashes($contentTypeSlug);
|
||||
|
||||
return '(content_type = "' . $quoted . '" OR content_types = "' . $quoted . '")';
|
||||
}
|
||||
|
||||
private function resolvePerPage(Request $request): int
|
||||
{
|
||||
$limit = (int) $request->query('limit', 0);
|
||||
@@ -393,7 +419,7 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
private function mainCategories(): Collection
|
||||
{
|
||||
return $this->contentTypeResolver
|
||||
->publicContentTypes()
|
||||
->toolbarContentTypes()
|
||||
->map(function (ContentType $type) {
|
||||
return (object) [
|
||||
'id' => $type->id,
|
||||
|
||||
Reference in New Issue
Block a user