*/ protected $fillable = [ 'title', 'description', 'file_path', 'file_name', 'file_size', 'mime_type', 'width', 'height', 'position', 'is_featured', 'alt_text', 'credit', 'source_url', 'metadata', ]; /** * The attributes that should be cast. * * @var array */ protected $casts = [ 'width' => 'integer', 'height' => 'integer', 'file_size' => 'integer', 'position' => 'integer', 'is_featured' => 'boolean', 'metadata' => 'array', ]; /** * Get the parent photoable model. */ public function photoable(): MorphTo { return $this->morphTo(); } /** * Get the URL for the photo. */ public function getUrlAttribute(): string { return asset('storage/' . $this->file_path); } /** * Get the thumbnail URL for the photo. */ public function getThumbnailUrlAttribute(): string { $pathInfo = pathinfo($this->file_path); $thumbnailPath = $pathInfo['dirname'] . '/' . $pathInfo['filename'] . '_thumb.' . $pathInfo['extension']; return asset('storage/' . $thumbnailPath); } /** * Scope a query to only include featured photos. */ public function scopeFeatured($query) { return $query->where('is_featured', true); } /** * Scope a query to order by position. */ public function scopeOrdered($query) { return $query->orderBy('position'); } /** * Set this photo as featured and unset others. */ public function setAsFeatured(): bool { if (!$this->photoable) { return false; } // Begin transaction DB::beginTransaction(); try { // Unset all other photos as featured $this->photoable->photos() ->where('id', '!=', $this->id) ->update(['is_featured' => false]); // Set this photo as featured $this->is_featured = true; $this->save(); DB::commit(); return true; } catch (\Exception $e) { DB::rollBack(); return false; } } /** * Update the position of this photo. */ public function updatePosition(int $position): bool { $this->position = $position; return $this->save(); } }