Files
fotospiel-app/database/seeders/DemoPhotosSeeder.php
Codex Agent 2f9a700e00
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled
Fix guest demo UX and enforce guest limits
2026-01-21 21:35:40 +01:00

198 lines
7.5 KiB
PHP

<?php
namespace Database\Seeders;
use App\Models\Emotion;
use App\Models\Event;
use App\Models\Photo;
use App\Models\PhotoLike;
use App\Models\Task;
use App\Models\Tenant;
use Carbon\Carbon;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class DemoPhotosSeeder extends Seeder
{
public function run(): void
{
$tenant = Tenant::where('slug', 'demo-tenant')->first();
if (! $tenant) {
$this->command->info('Demo tenant not found, skipping DemoPhotosSeeder');
return;
}
$photoDir = storage_path('app/public/photos');
if (! File::exists($photoDir)) {
$this->command->info('No demo photos available in storage/app/public/photos');
return;
}
$photoFiles = collect(File::files($photoDir))
->filter(fn ($file) => str_ends_with(strtolower($file->getFilename()), '.jpg'))
->values();
if ($photoFiles->isEmpty()) {
$this->command->info('No JPG demo photos found, skipping DemoPhotosSeeder');
return;
}
$emotions = Emotion::all();
if ($emotions->isEmpty()) {
$this->command->info('No emotions available, skipping DemoPhotosSeeder');
return;
}
$events = [
[
'model' => Event::with(['tasks', 'eventPackage', 'eventPackages', 'eventType'])
->where('slug', 'demo-wedding-2025')
->first(),
'guest_names' => [
'Anna Mueller', 'Max Schmidt', 'Lisa Weber', 'Tom Fischer', 'Sophie Bauer',
'Lukas Hoffmann', 'Emma Wagner', 'Jonas Klein', 'Mia Schwarz', 'Felix Becker',
'Lena Richter', 'Paul Lehmann', 'Julia Neumann', 'David Vogel', 'Sara Krueger',
'Tim Berger', 'Nina Wolf', 'Ben Schaefer', 'Laura Stein', 'Moritz Fuchs',
],
'like_range' => [4, 18],
],
[
'model' => Event::with(['tasks', 'eventPackage', 'eventPackages', 'eventType'])
->where('slug', 'demo-corporate-2025')
->first(),
'guest_names' => [
'Clara Schmidt', 'Jan Becker', 'Noah Winkler', 'Sina Albrecht', 'Kai Lenz',
'Tara Nguyen', 'Omar Hassan', 'Elias Roth', 'Greta Sommer', 'Leonard Busch',
'Verena Graf', 'Nico Adler', 'Johanna Kurz', 'Fabian Scholz', 'Mara Kranz',
'Yuki Tanaka', 'Mateo Ruiz', 'Amina Korb', 'Philipp Krüger', 'Selma Vogt',
],
'like_range' => [2, 12],
],
];
foreach ($events as $config) {
/** @var Event|null $event */
$event = $config['model'];
if (! $event) {
$this->command->warn('Demo event missing, skipping photo seeding.');
continue;
}
$taskIds = $event->tasks()->pluck('tasks.id')->all();
if ($taskIds === []) {
$eventTypeId = $event->event_type_id ?? optional($event->eventType)->id;
$taskIds = Task::where('event_type_id', $eventTypeId)->pluck('id')->all();
}
if ($taskIds === []) {
$this->command->warn(sprintf('No tasks assigned to %s. Photos will be seeded without task references.', $event->slug));
}
$guestNames = $config['guest_names'];
$photosToSeed = min($photoFiles->count(), count($guestNames), 20);
if ($photosToSeed === 0) {
continue;
}
$storage = Storage::disk('public');
$storage->makeDirectory("events/{$event->id}/gallery");
$storage->makeDirectory("events/{$event->id}/gallery/thumbs");
$photosSeeded = 0;
for ($i = 0; $i < $photosToSeed; $i++) {
$sourceFile = $photoFiles->get($i % $photoFiles->count());
$baseName = pathinfo($sourceFile->getFilename(), PATHINFO_FILENAME);
$guestName = $guestNames[$i];
$likes = rand($config['like_range'][0], $config['like_range'][1]);
$timestamp = Carbon::parse($event->date ?? now())
->addHours(rand(0, 36))
->addMinutes(rand(0, 59));
$filename = sprintf('%s-demo-%02d.jpg', $event->slug, $i + 1);
$destPath = "events/{$event->id}/gallery/{$filename}";
if (! $storage->exists($destPath)) {
$storage->put($destPath, File::get($sourceFile->getRealPath()));
}
$thumbFilename = sprintf('%s-demo-%02d_thumb.jpg', $event->slug, $i + 1);
$thumbDest = "events/{$event->id}/gallery/thumbs/{$thumbFilename}";
$existingThumb = "thumbnails/{$baseName}_thumb.jpg";
if ($storage->exists($existingThumb)) {
if (! $storage->exists($thumbDest)) {
$storage->copy($existingThumb, $thumbDest);
}
} else {
if (! $storage->exists($thumbDest)) {
$storage->put($thumbDest, File::get($sourceFile->getRealPath()));
}
}
$taskId = $taskIds ? $taskIds[array_rand($taskIds)] : null;
$emotionId = $emotions->random()->id;
$photoData = [
'task_id' => $taskId,
'emotion_id' => $emotionId,
'guest_name' => $guestName,
'thumbnail_path' => $thumbDest,
'likes_count' => $likes,
'is_featured' => $i === 0,
'metadata' => ['demo' => true],
'created_at' => $timestamp,
'updated_at' => $timestamp,
];
if (Schema::hasColumn('photos', 'status')) {
$photoData['status'] = 'approved';
}
$photo = Photo::updateOrCreate(
[
'tenant_id' => $tenant->id,
'event_id' => $event->id,
'file_path' => $destPath,
],
$photoData
);
PhotoLike::where('photo_id', $photo->id)->delete();
$maxLikes = min($likes, 10);
for ($like = 0; $like < $maxLikes; $like++) {
PhotoLike::create([
'photo_id' => $photo->id,
'guest_name' => 'GuestLike_'.Str::random(6),
'ip_address' => '10.0.'.rand(0, 254).'.'.rand(1, 254),
'created_at' => $timestamp->copy()->addMinutes($like * 3),
]);
}
$photosSeeded++;
}
$eventPackage = $event->eventPackage ?? $event->eventPackages()->orderByDesc('purchased_at')->first();
if ($eventPackage) {
$eventPackage->forceFill([
'used_photos' => max($eventPackage->used_photos ?? 0, $photosSeeded),
'used_guests' => max($eventPackage->used_guests ?? 0, count(array_unique($guestNames))),
])->save();
}
$this->command->info(sprintf('Seeded %d demo photos for %s', $photosSeeded, $event->slug));
}
}
}