Startseite und Heroes - Hero-Slideshow (11 Motive inkl. Febrü) mit Infokarte, Laufzeit-Linie, größeren Klickflächen, Goldrahmen und mobiler Karte unter dem Bild; Zeile über der Überschrift, neue Vertrauenspunkte - Hero-Stil auf Marken, Über uns und Kontakt übertragen, Breadcrumb entfernt - Gründer-Leiste, Vision-Box auf Ocker, Showroom-Display in Box mit „Größer ansehen“/Vollbild - Akzentfarbe #a8741f, größere Serif-Überschriften in der Interior-Welt - Marken: Bildpause mit Zitat; Vereine aktualisiert und verlinkt Inhalte - Lektorat aller angezeigten Texte, /about-Korrekturen, Rechtstexte (DDG, TDDDG, Hosting Hetzner) - Ablauf Maßanfertigung überall einheitlich, Magazin-Beitrag neu, alte Beiträge offline, deutsches Datum/Lesezeit, FAQ Projektausstattung, Otterpohl - Referenzen-Seite per CMS-Schalter ausblendbar CMS und Technik - Tote Sektionen, Felder, Seiten und Komponenten entfernt und archiviert (dev/2026-08-28/archiv-*) - Entwurf /entwurf, Farb-Schalter und /welcome entfernt; Test-Routen nur noch lokal - Seitenbaum-Beschreibungen geprüft, fehlende CMS-Felder ergänzt, Markenliste aus „Kernmarken“ - Admin-Bildvorschauen repariert, verwendete Bilder in die Mediathek übernommen - Seeder für das Deployment siehe dev/2026-08-28/entwicklungsplan.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
125 lines
4.5 KiB
PHP
125 lines
4.5 KiB
PHP
<?php
|
||
|
||
namespace Database\Seeders;
|
||
|
||
use App\Models\CmsArticle;
|
||
use App\Models\CmsBrand;
|
||
use FluxCms\Core\Models\CmsContent;
|
||
use FluxCms\Core\Models\CmsMedia;
|
||
use FluxCms\Core\Services\MediaConversionService;
|
||
use Illuminate\Database\Seeder;
|
||
use Illuminate\Support\Facades\Storage;
|
||
use Illuminate\Support\Str;
|
||
|
||
/**
|
||
* Übernimmt die Bilder, die Seeder unter public/img/assets/ abgelegt haben (Marken, Magazin,
|
||
* Slideshow …), in die Mediathek (07.10.2026) – damit sie im Bildauswahl-Dialog erscheinen.
|
||
*
|
||
* Name in der Mediathek ist der bisherige Pfad (z. B. `marken/cabinet/ankleide-solitaer-eiche-tano.webp`).
|
||
* Die Editoren speichern beim Auswählen genau diesen Namen, also bleiben alle Verweise gültig; der
|
||
* Admin (media_url()) zeigt das Bild aus der Mediathek. Die Datei wird kopiert, das Original unter
|
||
* public/img/assets/ bleibt liegen – die Website liefert weiter von dort (WebP-Fassungen).
|
||
*
|
||
* Übernommen wird nur, was Marken, Magazin oder CMS-Einträge (samt Standardtexten) tatsächlich
|
||
* verwenden. Bereits vorhandene Einträge werden übersprungen – mehrfaches Ausführen ist unbedenklich.
|
||
*
|
||
* php artisan db:seed --class=AssetMediaImportSeeder
|
||
*/
|
||
class AssetMediaImportSeeder extends Seeder
|
||
{
|
||
public function run(): void
|
||
{
|
||
$disk = config('flux-cms.media.disk', 'public');
|
||
$originals = config('flux-cms.media.originals_path', 'cms/media/originals');
|
||
$service = app(MediaConversionService::class);
|
||
$imported = 0;
|
||
|
||
foreach ($this->referencedAssets() as $value => $collection) {
|
||
if (CmsMedia::query()->where('filename', $value)->exists()) {
|
||
continue;
|
||
}
|
||
|
||
$source = public_path('img/assets/'.$value);
|
||
$extension = strtolower(pathinfo($source, PATHINFO_EXTENSION));
|
||
$path = $originals.'/'.Str::random(40).'.'.$extension;
|
||
|
||
Storage::disk($disk)->put($path, file_get_contents($source));
|
||
|
||
$isSvg = $extension === 'svg';
|
||
$dimensions = $isSvg ? false : @getimagesize($source);
|
||
|
||
$media = CmsMedia::create([
|
||
'filename' => $value,
|
||
'disk' => $disk,
|
||
'path' => $path,
|
||
'type' => 'image',
|
||
'mime_type' => $isSvg ? 'image/svg+xml' : (mime_content_type($source) ?: 'image/'.$extension),
|
||
'file_size' => filesize($source),
|
||
'original_width' => $dimensions ? $dimensions[0] : null,
|
||
'original_height' => $dimensions ? $dimensions[1] : null,
|
||
'collection' => $collection,
|
||
'conversions' => [],
|
||
]);
|
||
|
||
if (! $isSvg) {
|
||
$service->generateThumbnail($media);
|
||
}
|
||
|
||
$imported++;
|
||
}
|
||
|
||
$this->command?->info($imported === 0
|
||
? 'Mediathek: alle verwendeten Bilder sind bereits übernommen.'
|
||
: "Mediathek: {$imported} Bilder aus public/img/assets übernommen.");
|
||
}
|
||
|
||
/**
|
||
* Bildpfade, die Marken, Magazin oder CMS verwenden und die unter public/img/assets/ liegen,
|
||
* mit der Sammlung, in die sie kommen (die erste Quelle zählt: Marken, Magazin, Website).
|
||
*
|
||
* @return array<string, string>
|
||
*/
|
||
public function referencedAssets(): array
|
||
{
|
||
$values = [];
|
||
$collect = function (mixed $value, string $collection) use (&$collect, &$values): void {
|
||
if (is_array($value)) {
|
||
array_walk($value, fn ($item) => $collect($item, $collection));
|
||
|
||
return;
|
||
}
|
||
|
||
if (! is_string($value) || ! preg_match('/\.(jpe?g|png|webp|svg|gif)$/i', $value)) {
|
||
return;
|
||
}
|
||
|
||
$value = ltrim(trim($value), '/');
|
||
|
||
if (str_contains($value, '://') || str_starts_with($value, 'storage/') || str_starts_with($value, 'immobile/')) {
|
||
return;
|
||
}
|
||
|
||
if (is_file(public_path('img/assets/'.$value))) {
|
||
$values[$value] ??= $collection;
|
||
}
|
||
};
|
||
|
||
foreach (CmsBrand::query()->get() as $brand) {
|
||
$collect([$brand->logo, $brand->image, $brand->gallery, $brand->sections], 'Marken');
|
||
}
|
||
|
||
foreach (CmsArticle::query()->get() as $article) {
|
||
$collect($article->toArray(), 'Magazin');
|
||
}
|
||
|
||
foreach (CmsContent::query()->get() as $row) {
|
||
$collect($row->getTranslations('value'), 'Website');
|
||
}
|
||
|
||
$collect(trans('b2in.themes.'.config('app.theme', 'b2in'), [], 'de'), 'Website');
|
||
|
||
ksort($values);
|
||
|
||
return $values;
|
||
}
|
||
}
|