Files
michaelschiemer/src/Domain/Media/ImageSourceSetGenerator.php
Michael Schiemer 5050c7d73a docs: consolidate documentation into organized structure
- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
2025-10-05 11:05:04 +02:00

171 lines
5.5 KiB
PHP

<?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 = [];
// 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(),
);
}
}