-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Beschreibung
Automatisches Setzen von Variantenbildern basierend auf:
- Eigenschaftsbilder (z.B. Farboption "Rot" hat ein Bild → wird Varianten-Cover)
- Parent-Produktbilder (optional alle Bilder des Hauptartikels an Variante anhängen)
Use Case
Beispiel: Jacke mit Farbvarianten
Hauptartikel "Premium Leather Jacket":
- Bild 1: Produktfoto Vorderseite
- Bild 2: Produktfoto Rückseite
- Bild 3: Detailaufnahme
Optionen:
- Farbe "Schwarz": Hat Eigenschaftsbild
black-swatch.jpg - Farbe "Braun": Hat Eigenschaftsbild
brown-swatch.jpg - Größe "XL": Kein Bild
Variante "XL / Schwarz":
- Cover (Hauptbild):
black-swatch.jpg(von Eigenschaft "Schwarz") - Weitere Bilder (optional): Bild 1, 2, 3 vom Parent
Ergebnis im Frontend:
- Kunde sieht schwarze Jacke als Hauptbild
- Kann durch Parent-Bilder browsen (Vorder-/Rückseite, Details)
Anforderungen
1. Eigenschaftsbild als Cover setzen
Funktionalität
- Prüfe alle Optionen der Variante (z.B.
[XL, Schwarz]) - Falls Option ein
mediahat → setze als Varianten-Cover - Falls mehrere Optionen Bilder haben → Priorität konfigurierbar
Priorität bei mehreren Bildern
Option A: Erste gefundene Option (Empfohlen)
Variante: [Größe: XL (mit Bild), Farbe: Schwarz (mit Bild)]
→ Cover: Bild von "XL" (da zuerst in Options-Array)
Option B: Konfigurierbare Priorität
Plugin-Config: "Farbe > Größe > Material"
Variante: [Größe: XL (mit Bild), Farbe: Schwarz (mit Bild)]
→ Cover: Bild von "Schwarz" (da Farbe höher priorisiert)
Empfehlung: Option B mit Standard-Priorität nach PropertyGroup-Namen
Edge Cases
- Keine Option hat Bild: Behalte bestehendes Cover (oder Parent-Cover)
- Option ohne Media-Objekt: Überspringe, prüfe nächste Option
- Variante hat bereits Cover: Überschreibe nur wenn
--force-imagesgesetzt
2. Parent-Bilder anhängen (Optional)
Funktionalität
- Kopiere alle
mediades Parent-Produkts zur Variante - Reihenfolge beibehalten (Position aus Parent übernehmen)
- Eigenschaftsbild bleibt Cover (Position 0)
- Parent-Bilder ab Position 1
Beispiel
Parent "jacket-001":
media: [
{id: img1, position: 0, isCover: true}, // Vorderseite
{id: img2, position: 1, isCover: false}, // Rückseite
{id: img3, position: 2, isCover: false} // Detail
]
Variante mit Eigenschaftsbild "Schwarz":
media: [
{id: img_schwarz, position: 0, isCover: true}, // Eigenschaftsbild
{id: img1, position: 1, isCover: false}, // Parent Bild 1
{id: img2, position: 2, isCover: false}, // Parent Bild 2
{id: img3, position: 3, isCover: false} // Parent Bild 3
]
Duplikat-Vermeidung
- Prüfe ob Bild bereits bei Variante existiert (via
mediaId) - Verhindere doppelte Bilder
3. Konfigurationsoptionen
CLI Flags
# Nur Eigenschaftsbilder als Cover setzen
bin/console wsc:variant:update --product-numbers="jacket-001" --update-images
# Eigenschaftsbilder + Parent-Bilder anhängen
bin/console wsc:variant:update --product-numbers="jacket-001" --update-images --inherit-parent-images
# Nur Parent-Bilder anhängen (ohne Eigenschaftsbilder)
bin/console wsc:variant:update --product-numbers="jacket-001" --inherit-parent-images
# Bestehende Bilder überschreiben
bin/console wsc:variant:update --product-numbers="jacket-001" --update-images --force-imagesPlugin-Konfiguration (Admin)
<config>
<!-- Feature-Toggle -->
<input-field name="updateImages" type="bool">
<label>Eigenschaftsbilder als Cover setzen</label>
<defaultValue>false</defaultValue>
</input-field>
<input-field name="inheritParentImages" type="bool">
<label>Parent-Bilder an Varianten anhängen</label>
<defaultValue>false</defaultValue>
</input-field>
<input-field name="forceImageUpdate" type="bool">
<label>Bestehende Bilder überschreiben</label>
<defaultValue>false</defaultValue>
</input-field>
<!-- Priorität bei mehreren Eigenschaftsbildern -->
<input-field name="imagePropertyPriority" type="text">
<label>Eigenschafts-Priorität (komma-separiert)</label>
<placeholder>Farbe,Material,Größe</placeholder>
<helpText>PropertyGroup-Namen in Prioritäts-Reihenfolge</helpText>
</input-field>
</config>Admin Interface (Issue #4)
- Checkboxen analog zu
--name-only,--number-only - "Bilder aktualisieren": Setzt Eigenschaftsbilder als Cover
- "Parent-Bilder übernehmen": Hängt Parent-Bilder an
4. Technische Umsetzung
Neue Service-Methode
class VariantUpdateService
{
public function updateVariantImages(
ProductEntity $variant,
ProductEntity $parent,
bool $inheritParentImages = false,
bool $force = false
): void {
// 1. Eigenschaftsbild als Cover setzen
$optionImage = $this->findPriorityOptionImage($variant);
if ($optionImage) {
$this->setVariantCover($variant, $optionImage, $force);
}
// 2. Parent-Bilder anhängen (optional)
if ($inheritParentImages) {
$this->inheritParentImages($variant, $parent);
}
}
private function findPriorityOptionImage(ProductEntity $variant): ?MediaEntity
{
$options = $variant->getOptions();
$priority = $this->getPriorityList(); // Aus Config
// Sortiere Optionen nach Priorität
foreach ($priority as $propertyGroupName) {
foreach ($options as $option) {
if ($option->getGroup()->getName() === $propertyGroupName
&& $option->getMedia() !== null) {
return $option->getMedia();
}
}
}
// Fallback: Erste Option mit Bild
foreach ($options as $option) {
if ($option->getMedia() !== null) {
return $option->getMedia();
}
}
return null;
}
private function setVariantCover(
ProductEntity $variant,
MediaEntity $media,
bool $force
): void {
// Prüfe ob Variante bereits Cover hat
if (!$force && $variant->getCover() !== null) {
$this->logger->info('Variant already has cover, skipping', [
'variant_id' => $variant->getId(),
]);
return;
}
// Erstelle ProductMedia-Eintrag
$productMedia = [
'productId' => $variant->getId(),
'mediaId' => $media->getId(),
'position' => 0,
];
$this->productMediaRepository->create([$productMedia], $this->context);
// Setze als Cover
$this->productRepository->update([[
'id' => $variant->getId(),
'coverId' => $productMedia['id'], // Nach create verfügbar
]], $this->context);
}
private function inheritParentImages(
ProductEntity $variant,
ProductEntity $parent
): void {
$parentMedia = $parent->getMedia();
if ($parentMedia === null || $parentMedia->count() === 0) {
return;
}
$existingMediaIds = $variant->getMedia()
?->map(fn($pm) => $pm->getMediaId())
?? [];
$newMedia = [];
$position = 1; // Start ab 1 (0 ist Cover)
foreach ($parentMedia as $parentProductMedia) {
// Duplikat-Check
if (in_array($parentProductMedia->getMediaId(), $existingMediaIds)) {
continue;
}
$newMedia[] = [
'productId' => $variant->getId(),
'mediaId' => $parentProductMedia->getMediaId(),
'position' => $position++,
];
}
if (!empty($newMedia)) {
$this->productMediaRepository->create($newMedia, $this->context);
}
}
}DAL Associations erweitern
$criteria->addAssociation('options.group');
$criteria->addAssociation('options.media'); // NEU
$criteria->addAssociation('media'); // Varianten-Bilder
$criteria->addAssociation('cover'); // Varianten-Cover
$criteria->addAssociation('parent.media'); // Parent-Bilder für InheritRepository-Injection
public function __construct(
private readonly EntityRepository $productRepository,
private readonly EntityRepository $productMediaRepository, // NEU
private readonly LoggerInterface $logger
) {}5. Logging & Dry-Run
Dry-Run Output
Variante: jacket-001-xl-schwarz
✓ Name: Premium Leather Jacket XL Schwarz
✓ Nummer: jacket-001-xl-schwarz
📷 Cover würde gesetzt: black-swatch.jpg (von Option "Schwarz")
📷 3 Parent-Bilder würden angehängt
Log-Einträge
$this->logger->info('Variant image updated', [
'variant_id' => $variant->getId(),
'cover_media_id' => $media->getId(),
'source_option' => $option->getName(),
'inherited_images_count' => count($newMedia),
]);6. Edge Cases & Fehlerbehandlung
| Szenario | Verhalten |
|---|---|
| Option ohne Media-Objekt | Überspringe, prüfe nächste |
| Alle Optionen ohne Bild | Behalte bestehendes Cover |
| Parent ohne Bilder | Inherit-Feature macht nichts |
| Variante hat bereits identisches Bild | Duplikat vermeiden |
| Media-Entity nicht gefunden | Logge Warnung, überspringe |
| Variante hat 10 eigene Bilder | Hänge Parent-Bilder trotzdem an (Position 11+) |
7. Performance-Überlegungen
Problem
Bei 10.000 Varianten mit je 5 Parent-Bildern:
- 50.000 ProductMedia-Inserts
- Potenziell hohe DB-Last
Lösung: Batch-Inserts
// Sammle alle ProductMedia für Batch
$allProductMedia = [];
foreach ($variants as $variant) {
$newMedia = $this->prepareInheritedImages($variant, $parent);
$allProductMedia = array_merge($allProductMedia, $newMedia);
// Insert alle 500 Einträge
if (count($allProductMedia) >= 500) {
$this->productMediaRepository->create($allProductMedia, $this->context);
$allProductMedia = [];
}
}
// Rest inserieren
if (!empty($allProductMedia)) {
$this->productMediaRepository->create($allProductMedia, $this->context);
}Akzeptanzkriterien
- Eigenschaftsbilder werden als Varianten-Cover gesetzt
- Priorität bei mehreren Bildern konfigurierbar
- Parent-Bilder können optional angehängt werden
- Keine Duplikat-Bilder
- Dry-Run zeigt Bild-Änderungen an
- CLI-Flags
--update-imagesund--inherit-parent-imagesfunktionieren - Admin-Konfiguration für Bild-Features
- Logging für Audit-Trail
- Performance-Test: 1.000 Varianten mit 5 Bildern < 2 Min
Testfälle
Unit Tests
public function testFindPriorityOptionImage(): void
{
// Test Priority: Farbe > Größe
// Variante mit [Größe (mit Bild), Farbe (mit Bild)]
// Erwartet: Farb-Bild wird gewählt
}
public function testInheritParentImagesNoDuplicates(): void
{
// Variante hat bereits Bild X
// Parent hat Bilder X, Y, Z
// Erwartet: Nur Y, Z werden angehängt
}Integration Tests
public function testUpdateVariantImages(): void
{
$parent = $this->createProductWithImages(['img1', 'img2']);
$variant = $this->createVariantWithColorOption($parent, 'Schwarz', 'black.jpg');
$service->updateVariantImages($variant, $parent, inheritParentImages: true);
// Assert: Cover ist black.jpg
// Assert: Variante hat 3 Bilder (black.jpg + img1 + img2)
}Abhängigkeiten
- Sollte nach Code Quality Verbesserungen und Shopware Best Practices #8 (Service-Extraktion) implementiert werden
- Kann parallel zu Message Queue mit automatischer Batch-Anpassung implementieren #5 (Message Queue) entwickelt werden
- Integration in Admin Interface mit Plugin-Konfiguration und Twig-Templates #4 (Admin Interface) als zusätzliche Checkboxen
- Integration in Neuer CLI Command für alle Produkte: wsc:variant:update-all #6 (CLI update-all) als globale Flags
Dokumentation
README-Erweiterung
Beispiel: Bild-Management
# Eigenschaftsbilder als Cover setzen
bin/console wsc:variant:update --product-numbers="jacket-001" --update-images
# Eigenschaftsbilder + Parent-Bilder übernehmen
bin/console wsc:variant:update \
--product-numbers="jacket-001" \
--update-images \
--inherit-parent-images
# Für alle Produkte
bin/console wsc:variant:update-all \
--update-images \
--inherit-parent-images \
--forcePrioritäts-Konfiguration
Einstellungen → Plugins → WSC Variant Updater
→ "Eigenschafts-Priorität": Farbe,Material,Größe
Dies bedeutet: Bei Variante mit Optionen [Größe: XL (mit Bild), Farbe: Schwarz (mit Bild)]
wird das Bild von "Schwarz" als Cover gewählt, da "Farbe" höhere Priorität hat.
Metadata
Metadata
Assignees
Labels
No labels