Add Artwork model convenience methods for featured thumbnails
This commit is contained in:
@@ -1,19 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\ArtworkContributor;
|
||||
use App\Models\Group;
|
||||
use App\Services\ThumbnailService;
|
||||
use App\Support\ArtworkFeaturedImagePath;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use App\Services\ThumbnailService;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Laravel\Scout\SearchableScope;
|
||||
|
||||
/**
|
||||
* App\Models\Artwork
|
||||
@@ -26,7 +28,7 @@ use Laravel\Scout\Searchable;
|
||||
*/
|
||||
class Artwork extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes, Searchable;
|
||||
use HasFactory, Searchable, SoftDeletes;
|
||||
|
||||
/**
|
||||
* Override Scout's bootSearchable to skip the ModelObserver (which fires MakeSearchable
|
||||
@@ -36,16 +38,19 @@ class Artwork extends Model
|
||||
*/
|
||||
public static function bootSearchable(): void
|
||||
{
|
||||
static::addGlobalScope(new \Laravel\Scout\SearchableScope);
|
||||
static::addGlobalScope(new SearchableScope);
|
||||
(new static)->registerSearchableMacros();
|
||||
// ModelObserver intentionally omitted — indexing is handled by IndexArtworkJob.
|
||||
}
|
||||
|
||||
public const PUBLISHED_AS_USER = 'user';
|
||||
|
||||
public const PUBLISHED_AS_GROUP = 'group';
|
||||
|
||||
public const VISIBILITY_PUBLIC = 'public';
|
||||
|
||||
public const VISIBILITY_UNLISTED = 'unlisted';
|
||||
|
||||
public const VISIBILITY_PRIVATE = 'private';
|
||||
|
||||
protected $table = 'artworks';
|
||||
@@ -187,6 +192,7 @@ class Artwork extends Model
|
||||
}
|
||||
|
||||
$sizeKey = array_key_exists($size, self::THUMB_SIZES) ? $size : 'md';
|
||||
|
||||
return ThumbnailService::fromHash($this->hash, $this->thumb_ext, $sizeKey);
|
||||
}
|
||||
|
||||
@@ -213,9 +219,14 @@ class Artwork extends Model
|
||||
public function getThumbnailUrlAttribute(): ?string
|
||||
{
|
||||
$url = $this->getThumbUrlAttribute();
|
||||
if (!empty($url)) return $url;
|
||||
if (! empty($url)) {
|
||||
return $url;
|
||||
}
|
||||
$thumb = $this->getThumbAttribute();
|
||||
if (!empty($thumb)) return $thumb;
|
||||
if (! empty($thumb)) {
|
||||
return $thumb;
|
||||
}
|
||||
|
||||
return '/images/placeholder.jpg';
|
||||
}
|
||||
|
||||
@@ -224,13 +235,90 @@ class Artwork extends Model
|
||||
*/
|
||||
public function getThumbSrcsetAttribute(): ?string
|
||||
{
|
||||
if (empty($this->hash) || empty($this->thumb_ext)) return null;
|
||||
if (empty($this->hash) || empty($this->thumb_ext)) {
|
||||
return null;
|
||||
}
|
||||
$sm = $this->thumbUrl('sm');
|
||||
$md = $this->thumbUrl('md');
|
||||
if (!$sm || !$md) return null;
|
||||
if (! $sm || ! $md) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $sm.' 320w, '.$md.' 600w';
|
||||
}
|
||||
|
||||
public function featuredThumbnailObjectPath(string $variant = 'desktop'): ?string
|
||||
{
|
||||
if (empty($this->hash)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return app(ArtworkFeaturedImagePath::class)->objectPath($this, $variant);
|
||||
}
|
||||
|
||||
public function featuredThumbnailUrl(string $variant = 'desktop'): string
|
||||
{
|
||||
$helper = app(ArtworkFeaturedImagePath::class);
|
||||
|
||||
foreach ($helper->preferredVariantOrder($variant) as $candidate) {
|
||||
if ($this->hasFeaturedThumbnail($candidate)) {
|
||||
return $helper->url($this, $candidate);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->thumbUrl('xl')
|
||||
?? $this->thumbUrl('lg')
|
||||
?? 'https://files.skinbase.org/default/missing_xl.webp';
|
||||
}
|
||||
|
||||
public function hasFeaturedThumbnail(?string $variant = null): bool
|
||||
{
|
||||
if (empty($this->hash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$helper = app(ArtworkFeaturedImagePath::class);
|
||||
$variants = $variant !== null ? [$helper->normalizeVariant($variant)] : $helper->variantNames();
|
||||
$disk = Storage::disk((string) config('uploads.object_storage.disk', 's3'));
|
||||
|
||||
foreach ($variants as $variantName) {
|
||||
if ($disk->exists($helper->objectPath($this, $variantName))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasAllFeaturedThumbnails(): bool
|
||||
{
|
||||
$helper = app(ArtworkFeaturedImagePath::class);
|
||||
|
||||
foreach ($helper->variantNames() as $variant) {
|
||||
if (! $this->hasFeaturedThumbnail($variant)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function featuredImageAltText(): string
|
||||
{
|
||||
$title = trim((string) ($this->title ?? ''));
|
||||
$author = trim((string) ($this->user?->name ?? ''));
|
||||
|
||||
if ($title !== '' && $author !== '') {
|
||||
return sprintf('%s by %s', $title, $author);
|
||||
}
|
||||
|
||||
if ($title !== '') {
|
||||
return $title;
|
||||
}
|
||||
|
||||
return 'Featured artwork';
|
||||
}
|
||||
|
||||
// Relations
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
@@ -544,6 +632,7 @@ class Artwork extends Model
|
||||
{
|
||||
// Compose approved() so behavior is consistent and composable
|
||||
$table = $this->getTable();
|
||||
|
||||
return $query->approved()->where("{$table}.is_public", true);
|
||||
}
|
||||
|
||||
@@ -551,6 +640,7 @@ class Artwork extends Model
|
||||
{
|
||||
// Respect soft deletes and mark approved content
|
||||
$table = $this->getTable();
|
||||
|
||||
return $query->whereNull("{$table}.deleted_at")->where("{$table}.is_approved", true);
|
||||
}
|
||||
|
||||
@@ -558,6 +648,7 @@ class Artwork extends Model
|
||||
{
|
||||
// Respect soft deletes and only include published items up to now
|
||||
$table = $this->getTable();
|
||||
|
||||
return $query->whereNull("{$table}.deleted_at")
|
||||
->whereNotNull("{$table}.published_at")
|
||||
->where("{$table}.published_at", '<=', now());
|
||||
@@ -583,7 +674,7 @@ class Artwork extends Model
|
||||
|
||||
return $query
|
||||
->whereRaw('COALESCE('.$table.'.is_mature, 0) = 0')
|
||||
->whereRaw("COALESCE(" . $table . ".maturity_status, 'clear') != ?", ['suspected']);
|
||||
->whereRaw('COALESCE('.$table.".maturity_status, 'clear') != ?", ['suspected']);
|
||||
}
|
||||
|
||||
public function scopeWithoutMissingThumbnails(Builder $query): Builder
|
||||
|
||||
Reference in New Issue
Block a user