tenant_id !== (int) $event->tenant_id) { abort(403, 'Unauthorized'); } $photos = Photo::query() ->with('mediaAsset') ->where('event_id', $event->id) ->where('status', 'approved') ->orderBy('created_at') ->get(); if ($photos->isEmpty()) { abort(404, 'No approved photos available for this event.'); } $zip = new ZipArchive; $tempPath = tempnam(sys_get_temp_dir(), 'fotospiel-photos-'); if ($tempPath === false || $zip->open($tempPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { abort(500, 'Unable to generate archive.'); } foreach ($photos as $photo) { $filename = $this->buildFilename($event, $photo); $asset = $photo->mediaAsset ?? EventMediaAsset::query() ->where('photo_id', $photo->id) ->where('variant', 'original') ->first(); $added = false; if ($asset && $asset->path) { $added = $this->addDiskFileToArchive($zip, $asset->disk ?? config('filesystems.default'), $asset->path, $filename); } if (! $added && $photo->file_path) { $added = $this->addDiskFileToArchive($zip, config('filesystems.default'), $photo->file_path, $filename); } if (! $added) { Log::warning('Skipping photo in archive build', [ 'event_id' => $event->id, 'photo_id' => $photo->id, ]); } } $zip->close(); $downloadName = sprintf('fotospiel-event-%s-photos.zip', $event->slug ?? $event->id); return response()->streamDownload(function () use ($tempPath) { $stream = fopen($tempPath, 'rb'); if (! $stream) { throw new FileNotFoundException('Archive could not be opened.'); } fpassthru($stream); fclose($stream); unlink($tempPath); }, $downloadName, [ 'Content-Type' => 'application/zip', ]); } private function buildFilename(Event $event, Photo $photo): string { $timestamp = $photo->created_at?->format('Ymd_His') ?? now()->format('Ymd_His'); $extension = pathinfo($photo->file_path ?? '', PATHINFO_EXTENSION) ?: 'jpg'; return sprintf('%s-photo-%d.%s', $timestamp, $photo->id, $extension); } private function addDiskFileToArchive(ZipArchive $zip, ?string $diskName, string $path, string $filename): bool { $disk = $diskName ? Storage::disk($diskName) : Storage::disk(config('filesystems.default')); try { if (method_exists($disk, 'path')) { $absolute = $disk->path($path); if (is_file($absolute)) { return $zip->addFile($absolute, $filename); } } $stream = $disk->readStream($path); if ($stream) { $contents = stream_get_contents($stream); fclose($stream); if ($contents !== false) { return $zip->addFromString($filename, $contents); } } } catch (\Throwable $e) { Log::warning('Failed to add file to archive', [ 'disk' => $diskName, 'path' => $path, 'error' => $e->getMessage(), ]); } return false; } }