optimizations
This commit is contained in:
301
app/Models/NovaCard.php
Normal file
301
app/Models/NovaCard.php
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user