chore: complete update
This commit is contained in:
163
src/Domain/Media/ImageSourceSetGenerator.php
Normal file
163
src/Domain/Media/ImageSourceSetGenerator.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Media;
|
||||
|
||||
/**
|
||||
* Erzeugt HTML-Source-Sets für responsive Bilder
|
||||
*/
|
||||
final readonly class ImageSourceSetGenerator
|
||||
{
|
||||
/**
|
||||
* Generiert ein vollständiges Picture-Element mit Source-Tags für verschiedene Formate und Größen
|
||||
*
|
||||
* @param Image $image Das Originalbild mit seinen Varianten
|
||||
* @param string $variantType Der Variantentyp (thumbnail, gallery, hero)
|
||||
* @param array $attributes Zusätzliche Attribute für das img-Tag (alt, class, etc.)
|
||||
* @return string HTML Picture-Element
|
||||
*/
|
||||
public function generatePictureElement(Image $image, string $variantType = 'gallery', array $attributes = []): string
|
||||
{
|
||||
$sources = [];
|
||||
|
||||
// Varianten nach Format gruppieren
|
||||
$variantsByFormat = $this->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 '<picture>' . implode('', $sources) . '</picture>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
'<source type="%s" srcset="%s" sizes="%s">',
|
||||
$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 '<img ' . implode(' ', $htmlAttributes) . '>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
|
||||
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 (isset($variantsByFormat['jpeg']) && !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 (isset($variantsByFormat[$format]) && !empty($variantsByFormat[$format])) {
|
||||
return $variantsByFormat[$format][0];
|
||||
}
|
||||
}
|
||||
|
||||
// Wenn keine Varianten gefunden wurden, das Originalbild verwenden
|
||||
return new ImageVariant(
|
||||
imageId: $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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user