groupVariantsByFormat($image, $variantType); // Source-Tags für jedes Format generieren foreach (['avif', 'webp', 'jpeg'] as $format) { if (isset($variantsByFormat[$format])) { $sources[] = $this->generateSourceElement($variantsByFormat[$format], ImageFormat::from($format)); } } // Fallback img-Tag $fallbackImage = $this->getFallbackImage($variantsByFormat, $image); $attributes['src'] = $fallbackImage->getUrl(); $attributes['width'] = $fallbackImage->width; $attributes['height'] = $fallbackImage->height; if (! isset($attributes['alt'])) { $attributes['alt'] = ''; } $sources[] = $this->generateImgTag($attributes); return '' . implode('', $sources) . ''; } /** * Generiert einen einfachen source-Tag mit srcset * * @param array $variants Bildvarianten für ein bestimmtes Format * @param ImageFormat $format Das Bildformat * @return string HTML source-Element */ private function generateSourceElement(array $variants, ImageFormat $format): string { $srcset = []; $sizes = []; // Nach Größe sortieren (klein nach groß) usort($variants, function (ImageVariant $a, ImageVariant $b) { return $a->width <=> $b->width; }); foreach ($variants as $variant) { // URL und Breite für srcset $srcset[] = $variant->getUrl() . ' ' . $variant->width . 'w'; // Sizes basierend auf Breakpoints $size = ImageSize::from($variant->size); $breakpoint = $size->getBreakpoint(); if ($breakpoint !== null) { $sizes[] = "(max-width: {$breakpoint}px) {$variant->width}px"; } } // Größte Variante als Standardgröße hinzufügen $sizes[] = $variants[count($variants) - 1]->width . 'px'; return sprintf( '', $format->getMimeType(), implode(', ', $srcset), implode(', ', $sizes) ); } /** * Erzeugt einen img-Tag mit den angegebenen Attributen * * @param array $attributes HTML-Attribute für das img-Tag * @return string HTML img-Element */ private function generateImgTag(array $attributes): string { $htmlAttributes = []; foreach ($attributes as $name => $value) { $htmlAttributes[] = sprintf('%s="%s"', $name, htmlspecialchars((string)$value)); } return ''; } /** * Gruppiert Bildvarianten nach Format * * @param Image $image Das Originalbild * @param string $variantType Der Variantentyp * @return array Varianten gruppiert nach Format */ private function groupVariantsByFormat(Image $image, string $variantType): array { $variantsByFormat = []; // Check if variants are loaded to avoid uninitialized readonly property error if (! isset($image->variants)) { return []; } foreach ($image->variants as $variant) { if ($variant->variantType === $variantType) { $variantsByFormat[$variant->format][] = $variant; } } return $variantsByFormat; } /** * Wählt das beste Fallback-Bild aus * * @param array $variantsByFormat Varianten gruppiert nach Format * @param Image $image Das Originalbild * @return ImageVariant Die beste Fallback-Variante */ private function getFallbackImage(array $variantsByFormat, Image $image): ImageVariant { // Bevorzugen JPEG als Fallback if (! empty($variantsByFormat['jpeg'])) { // Mittlere Größe als Fallback verwenden $variants = $variantsByFormat['jpeg']; return $variants[min(1, count($variants) - 1)]; } // Alternativen, falls kein JPEG verfügbar foreach (['webp', 'avif'] as $format) { if (! empty($variantsByFormat[$format])) { return $variantsByFormat[$format][0]; } } // Wenn keine Varianten gefunden wurden, das Originalbild verwenden return new ImageVariant( imageId: (string) $image->ulid, variantType: 'original', size: 'original', format: pathinfo($image->filename, PATHINFO_EXTENSION), mimeType: $image->mimeType, fileSize: $image->fileSize, width: $image->width, height: $image->height, filename: $image->filename, path: $image->path->toString(), ); } }