optimizations

This commit is contained in:
2026-03-28 19:15:39 +01:00
parent 0b25d9570a
commit cab4fbd83e
509 changed files with 1016804 additions and 1605 deletions

301
app/Models/NovaCard.php Normal file
View File

@@ -0,0 +1,301 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class NovaCard extends Model
{
use HasFactory, SoftDeletes;
public const FORMAT_SQUARE = 'square';
public const FORMAT_PORTRAIT = 'portrait';
public const FORMAT_STORY = 'story';
public const FORMAT_LANDSCAPE = 'landscape';
public const VISIBILITY_PUBLIC = 'public';
public const VISIBILITY_UNLISTED = 'unlisted';
public const VISIBILITY_PRIVATE = 'private';
public const STATUS_DRAFT = 'draft';
public const STATUS_PROCESSING = 'processing';
public const STATUS_PUBLISHED = 'published';
public const STATUS_HIDDEN = 'hidden';
public const STATUS_REJECTED = 'rejected';
public const MOD_PENDING = 'pending';
public const MOD_APPROVED = 'approved';
public const MOD_FLAGGED = 'flagged';
public const MOD_REJECTED = 'rejected';
protected $fillable = [
'uuid',
'user_id',
'category_id',
'title',
'slug',
'quote_text',
'quote_author',
'quote_source',
'description',
'format',
'project_json',
'schema_version',
'render_version',
'preview_path',
'preview_width',
'preview_height',
'background_type',
'background_image_id',
'original_card_id',
'root_card_id',
'template_id',
'visibility',
'status',
'moderation_status',
'featured',
'allow_download',
'allow_remix',
'views_count',
'shares_count',
'downloads_count',
'likes_count',
'favorites_count',
'saves_count',
'remixes_count',
'comments_count',
'challenge_entries_count',
'trending_score',
'featured_score',
'style_family',
'palette_family',
'density_score',
'editor_mode_last_used',
'allow_background_reuse',
'allow_export',
'original_creator_id',
'published_at',
'last_engaged_at',
'last_ranked_at',
'last_rendered_at',
];
protected $casts = [
'project_json' => 'array',
'schema_version' => 'integer',
'render_version' => 'integer',
'preview_width' => 'integer',
'preview_height' => 'integer',
'featured' => 'boolean',
'allow_download' => 'boolean',
'allow_remix' => 'boolean',
'views_count' => 'integer',
'shares_count' => 'integer',
'downloads_count' => 'integer',
'likes_count' => 'integer',
'favorites_count' => 'integer',
'saves_count' => 'integer',
'remixes_count' => 'integer',
'comments_count' => 'integer',
'challenge_entries_count' => 'integer',
'trending_score' => 'float',
'featured_score' => 'float',
'density_score' => 'integer',
'allow_background_reuse' => 'boolean',
'allow_export' => 'boolean',
'published_at' => 'datetime',
'last_engaged_at' => 'datetime',
'last_ranked_at' => 'datetime',
'last_rendered_at' => 'datetime',
];
protected static function booted(): void
{
static::creating(function (self $card): void {
if (! $card->uuid) {
$card->uuid = (string) Str::uuid();
}
});
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function category(): BelongsTo
{
return $this->belongsTo(NovaCardCategory::class, 'category_id');
}
public function template(): BelongsTo
{
return $this->belongsTo(NovaCardTemplate::class, 'template_id');
}
public function backgroundImage(): BelongsTo
{
return $this->belongsTo(NovaCardBackground::class, 'background_image_id');
}
public function originalCard(): BelongsTo
{
return $this->belongsTo(self::class, 'original_card_id');
}
public function rootCard(): BelongsTo
{
return $this->belongsTo(self::class, 'root_card_id');
}
public function originalCreator(): BelongsTo
{
return $this->belongsTo(User::class, 'original_creator_id');
}
public function remixes(): HasMany
{
return $this->hasMany(self::class, 'original_card_id');
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(NovaCardTag::class, 'nova_card_tag_relation', 'card_id', 'tag_id')
->withTimestamps();
}
public function reactions(): HasMany
{
return $this->hasMany(NovaCardReaction::class, 'card_id');
}
public function versions(): HasMany
{
return $this->hasMany(NovaCardVersion::class, 'card_id');
}
public function collectionItems(): HasMany
{
return $this->hasMany(NovaCardCollectionItem::class, 'card_id');
}
public function challengeEntries(): HasMany
{
return $this->hasMany(NovaCardChallengeEntry::class, 'card_id');
}
public function comments(): HasMany
{
return $this->hasMany(NovaCardComment::class, 'card_id');
}
public function scopePublished(Builder $query): Builder
{
return $query
->where('status', self::STATUS_PUBLISHED)
->where('moderation_status', '!=', self::MOD_REJECTED)
->whereIn('visibility', [self::VISIBILITY_PUBLIC, self::VISIBILITY_UNLISTED])
->whereNotNull('published_at');
}
public function scopePubliclyVisible(Builder $query): Builder
{
return $query
->published()
->where('visibility', self::VISIBILITY_PUBLIC)
->whereNotIn('moderation_status', [self::MOD_FLAGGED, self::MOD_REJECTED])
->where('status', self::STATUS_PUBLISHED);
}
public function scopeRising(Builder $query): Builder
{
return $query
->publiclyVisible()
->where('published_at', '>=', now()->subHours(96))
->where(function (Builder $q): void {
$q->where('saves_count', '>', 0)
->orWhere('remixes_count', '>', 0)
->orWhere('likes_count', '>', 1);
});
}
public function scopeFeaturedEditorial(Builder $query): Builder
{
return $query
->publiclyVisible()
->where(function (Builder $q): void {
$q->whereNotNull('featured_score')
->orWhere('featured', true);
});
}
public function previewUrl(): ?string
{
if (! $this->preview_path) {
return null;
}
return Storage::disk((string) config('nova_cards.storage.public_disk', 'public'))->url($this->preview_path);
}
public function ogPreviewUrl(): ?string
{
if (! $this->preview_path) {
return null;
}
$ogPath = preg_replace('/\.webp$/', '-og.jpg', $this->preview_path) ?: null;
if (! $ogPath) {
return $this->previewUrl();
}
return Storage::disk((string) config('nova_cards.storage.public_disk', 'public'))->url($ogPath);
}
public function publicUrl(): string
{
return route('cards.show', ['slug' => $this->slug, 'id' => $this->id]);
}
public function isOwnedBy(?User $user): bool
{
return $user !== null && (int) $user->id === (int) $this->user_id;
}
public function canBeViewedBy(?User $user): bool
{
if ($this->isOwnedBy($user)) {
return true;
}
if ($this->status !== self::STATUS_PUBLISHED) {
return false;
}
if ($this->moderation_status === self::MOD_REJECTED || $this->moderation_status === self::MOD_FLAGGED) {
return false;
}
return in_array($this->visibility, [self::VISIBILITY_PUBLIC, self::VISIBILITY_UNLISTED], true);
}
public function isRemix(): bool
{
return $this->original_card_id !== null;
}
public function canReceiveCommentsFrom(?User $user): bool
{
return $user !== null && $this->canBeViewedBy($user);
}
}