Wire admin studio SSR and search infrastructure

This commit is contained in:
2026-05-01 11:46:06 +02:00
parent 257b0dbef6
commit 18cea8b0f0
329 changed files with 197465 additions and 2741 deletions

View File

@@ -28,6 +28,19 @@ class Artwork extends Model
{
use HasFactory, SoftDeletes, Searchable;
/**
* Override Scout's bootSearchable to skip the ModelObserver (which fires MakeSearchable
* on every save). We still register SearchableScope and Builder macros so that
* scout:import and Builder::searchable() continue to work.
* All indexing is managed explicitly via IndexArtworkJob.
*/
public static function bootSearchable(): void
{
static::addGlobalScope(new \Laravel\Scout\SearchableScope);
(new static)->registerSearchableMacros();
// ModelObserver intentionally omitted — indexing is handled by IndexArtworkJob.
}
public const PUBLISHED_AS_USER = 'user';
public const PUBLISHED_AS_GROUP = 'group';
@@ -254,6 +267,11 @@ class Artwork extends Model
return $this->hasMany(WorldSubmission::class)->orderByDesc('reviewed_at')->orderByDesc('created_at');
}
public function worldRewardGrants(): HasMany
{
return $this->hasMany(WorldRewardGrant::class)->orderByDesc('granted_at')->orderByDesc('id');
}
public function isPublishedByGroup(): bool
{
return $this->publishedAsType() === self::PUBLISHED_AS_GROUP;
@@ -409,6 +427,14 @@ class Artwork extends Model
$stat = $this->stats;
$awardStat = $this->awardStat;
$publishedSortAt = $this->published_at ?? $this->created_at;
$sortedCategories = $this->categories->sortBy(
fn ($category) => sprintf(
'%010d|%s|%010d',
(int) ($category->sort_order ?? 999999999),
strtolower((string) ($category->name ?? '')),
(int) ($category->id ?? 0)
)
)->values();
// Orientation derived from pixel dimensions
$orientation = 'square';
@@ -425,8 +451,22 @@ class Artwork extends Model
? $this->width . 'x' . $this->height
: '';
// Primary category slug (first attached category)
$primaryCategory = $this->categories->first();
// Primary category slug follows the same sort_order-first semantics used by page presenters.
$primaryCategory = $sortedCategories->first();
$categorySlugs = $sortedCategories
->pluck('slug')
->filter()
->map(static fn ($slug) => (string) $slug)
->unique()
->values()
->all();
$contentTypeSlugs = $sortedCategories
->map(static fn ($category) => $category->contentType?->slug)
->filter()
->map(static fn ($slug) => (string) $slug)
->unique()
->values()
->all();
$category = $primaryCategory?->slug ?? '';
$content_type = $primaryCategory?->contentType?->slug ?? '';
@@ -442,7 +482,9 @@ class Artwork extends Model
'author_name' => $this->group?->name ?? $this->user?->name ?? 'Skinbase',
'published_as_type' => $this->publishedAsType(),
'category' => $category,
'categories' => $categorySlugs,
'content_type' => $content_type,
'content_types' => $contentTypeSlugs,
'tags' => $tags,
'ai_clip_tags' => collect((array) ($this->clip_tags_json ?? []))
->map(static fn ($row) => is_array($row) ? (string) ($row['tag'] ?? '') : '')