b2in/app/Models/DisplayMedia.php
Kevin Adametz 6c6d683b9a Display CMS Optimierungen 29-05-2026
- Mediathek: Video-Vorschaubilder statt Icons (FFmpeg-Thumbnails + Backfill-Command), Kategorie "Sonstiges"
- B2in Media-Picker zeigt alle Medientypen, Typ wird automatisch erkannt; Thumbnail-Preview vor allen Medien-URL-Feldern
- B2in Marke/Footer: Footer ein/aus, Logo+Claim frei positionierbar (Ecken) mit Constraints, separate Anzeige-Schalter
- Angebote-Modul dynamisch: kein Slide-Typ mehr, einheitliches Detail-Layout mit ein-/ausblendbaren Bloecken, Logo/Brand pro Slide, Streichpreis-Option
- Player: leere Module stoppen Endlosschleife, dynamische Layout-Anpassung bei verstecktem Footer/Header
- Fix: Script-Ladereihenfolge (Livewire vor Flux), entfernte stale public/flux/flux.js, Modal-Crash beim Aktualisieren behoben

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 15:57:33 +00:00

161 lines
3.7 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class DisplayMedia extends Model
{
/** @use HasFactory<\Database\Factories\DisplayMediaFactory> */
use HasFactory;
protected $table = 'display_media';
protected $fillable = [
'filename',
'disk',
'path',
'external_url',
'source_type',
'type',
'mime_type',
'file_size',
'thumbnail_path',
'alt_text',
'title',
'collection',
'metadata',
'is_active',
];
protected function casts(): array
{
return [
'metadata' => 'array',
'file_size' => 'integer',
'is_active' => 'boolean',
];
}
// ========================================
// ACCESSORS
// ========================================
public function getUrl(): string
{
if ($this->isExternal()) {
return $this->external_url;
}
return Storage::disk($this->disk)->url($this->path);
}
public function getThumbnailUrl(): ?string
{
if ($this->thumbnail_path) {
return Storage::disk($this->disk)->url($this->thumbnail_path);
}
if ($this->isUpload() && $this->isImage()) {
return $this->getUrl();
}
return null;
}
// ========================================
// TYPE CHECKS
// ========================================
public function isUpload(): bool
{
return $this->source_type === 'upload';
}
public function isExternal(): bool
{
return $this->source_type === 'external';
}
public function isImage(): bool
{
return $this->type === 'image';
}
public function isVideo(): bool
{
return $this->type === 'video';
}
// ========================================
// DISPLAY HELPERS
// ========================================
public function getHumanFileSize(): string
{
if ($this->file_size === 0) {
return $this->isExternal() ? 'Extern' : '0 B';
}
$units = ['B', 'KB', 'MB', 'GB'];
$size = $this->file_size;
$unitIndex = 0;
while ($size >= 1024 && $unitIndex < count($units) - 1) {
$size /= 1024;
$unitIndex++;
}
return round($size, 1).' '.$units[$unitIndex];
}
public function getDisplayName(): string
{
return $this->title ?: $this->filename;
}
// ========================================
// SCOPES
// ========================================
public function scopeImages(Builder $query): Builder
{
return $query->where('type', 'image');
}
public function scopeVideos(Builder $query): Builder
{
return $query->where('type', 'video');
}
public function scopeUploads(Builder $query): Builder
{
return $query->where('source_type', 'upload');
}
public function scopeExternals(Builder $query): Builder
{
return $query->where('source_type', 'external');
}
public function scopeActive(Builder $query): Builder
{
return $query->where('is_active', true);
}
public function scopeInCollection(Builder $query, string $collection): Builder
{
return $query->where('collection', $collection);
}
public function scopeSearch(Builder $query, string $term): Builder
{
return $query->where(function (Builder $query) use ($term): void {
$query->where('filename', 'like', "%{$term}%")
->orWhere('title', 'like', "%{$term}%");
});
}
}