Wer einen großen, variantenreichen Produktkatalog nach Pimcore migriert, kommt am Classification Store kaum vorbei. Stellen Sie sich tausende Produkte vor, jedes mit Dutzenden technischen Attributen, die sich je nach Produktfamilie unterscheiden, und Werten, die für Audit und Compliance nachvollziehbar bleiben müssen. Einfache Felder auf einem Pimcore-DataObject halten diese Struktur nicht, ohne die Klasse in eine unübersichtliche Wand aus meist leeren Attributen zu verwandeln.
Gerade im DACH-Raum, wo mehrsprachige Kataloge und regulierte Branchen die Regel sind, entscheidet das Datenmodell über den Erfolg einer Pimcore-Migration. Dieser Beitrag ist ein praxisnaher Bericht: die Entscheidungen, die sich bewährt haben, die Code-Muster, auf die wir gesetzt haben, und eine Sache, die wir heute anders machen würden. Er setzt voraus, dass Sie Pimcore kennen und vor der Frage stehen, wie Sie echte Produktdaten modellieren und migrieren.
Wann Classification Store besser ist als Bricks und einfache Attribute
Pimcore bietet drei Wege, Attribute an ein DataObject zu hängen. Einfache Klassenfelder sind am schnellsten abfragbar, liegen aber auf der Klasse, sodass jede Produktfamilie denselben Feldsatz trägt. Object Bricks erlauben optionale Feldblöcke, was hilft, wenn nur manche Produkte eine bestimmte Gruppe brauchen. Der Classification Store geht weiter: Er speichert Werte in einer eigenen Schlüssel-Wert-Struktur, die in Gruppen organisiert ist, und welche Gruppen gelten, kann pro Objekt variieren.
Der Classification Store rechtfertigt seine Komplexität, wenn der Attributsatz wirklich dynamisch ist. Wir greifen in drei Situationen dazu:
- Der Attributsatz unterscheidet sich stark zwischen Produktfamilien und würde die Klasse sonst mit Feldern aufblähen, die für die meisten Produkte leer sind.
- Die Liste der Attribute ändert sich häufig und sollte von Datenverantwortlichen pflegbar sein, statt in einem Code-Deployment ausgeliefert zu werden.
- Redakteure brauchen Attribute gruppiert und sortiert in der Oberfläche, ohne dass ein Entwickler jedes Mal die Klassendefinition anfasst.
Der Haken: Werte im Classification Store sind keine vollwertigen Klassenfelder. Sie können eine Produktliste nicht so einfach nach einem Store-Schlüssel filtern wie nach einer normalen Spalte, und jeder Lesezugriff läuft über die Store-API. Ist ein Attribut zentral für Abfragen und bei jedem Produkt vorhanden, gehört es als einfaches Feld auf die Klasse. Der Classification Store ist für den langen Schwanz der familienspezifischen, redaktionell gepflegten Attribute gedacht.
Wir planen und bauen Pimcore-Datenmodelle, Migrationen und Integrationen von Anfang bis Ende.
Pimcore Entwicklung in Wien →Gruppen und Schlüssel für variantenreiche Daten modellieren
Die zwei Kernstrukturen im Classification Store sind Gruppen und Schlüssel. Ein Schlüssel ist eine einzelne Attributdefinition: Name, Datentyp und ob er lokalisiert ist. Eine Gruppe ist eine benannte Sammlung von Schlüsseln. Sie weisen einem Objekt Gruppen zu, und das Objekt hält dann Werte für die Schlüssel dieser Gruppen. Für variantenreiche Daten haben wir uns auf eine Gruppe pro Produktfamilie festgelegt, plus eine gemeinsame Gruppe für Attribute, die jede Familie teilt.
Das hielt die Redakteure fokussiert. Ein Produkt einer Familie sah nur die Gruppen, die galten, statt an hunderten irrelevanten Schlüsseln vorbeizuscrollen. Es machte auch das Import-Mapping eindeutig, weil jedes Quellattribut auf genau eine Gruppe und einen Schlüssel abgebildet wurde.
<?php
use Pimcore\Model\DataObject\Product;
$product = Product::getById($id);
$store = $product->getAttributes(); // Classificationstore instance
// Group and key ids come from the Classification Store definition.
$store->setLocalizedKeyValue($groupId, $keyId, '316L stainless steel', 'en');
$product->save();Werte werden über numerische Gruppen- und Schlüssel-IDs adressiert, nicht über Namen. Lösen Sie diese IDs einmal aus der Definition auf und cachen Sie das Mapping. Sie pro Zeile während eines großen Imports nachzuschlagen, ist ein häufiger und vermeidbarer Performance-Fehler.
Migrationsansatz: stagen, validieren, idempotent importieren
Eine Migration in ein reguliertes PIM ist kein einmaliges Skript. Wir fahren sie in drei Durchläufen: die Rohdaten der Quelle in ein Zwischenschema stagen, gegen das Zielmodell validieren, dann mit einem idempotenten Writer importieren. Jeder Durchlauf ist für sich wiederholbar, was zählt, sobald der erste Volllauf Datenprobleme aufdeckt, die Sie an der Quelle beheben müssen.
- Staging: die Quell-Exporte unverändert in ein Staging-Schema laden, mit erhaltenem Quell-Primärschlüssel. Noch keine Transformation, damit Sie jeden Wert zu seinem Ursprung zurückverfolgen können.
- Validierung: jede Zeile gegen das Zielmodell prüfen (Pflichtattribute vorhanden, Enum-Werte bekannt, Einheiten parsebar) und einen Report schreiben. Es wird nichts importiert, bis dieser Report sauber genug zum Freigeben ist.
- Import: per stabilem externen Schlüssel upserten, damit ein erneuter Lauf nie Duplikate erzeugt.
Idempotenz ist der Teil, den Teams überspringen und dann bereuen. Produktimporte werden erneut gefahren: ein Fix an der Quelle, eine Mapping-Änderung, ein Teilabbruch mitten im Lauf. Schlüsselt der Writer auf eine externe ID und aktualisiert an Ort und Stelle, ist ein erneuter Lauf sicher. Legt er blind Objekte an, verdoppelt ein erneuter Lauf Ihren Katalog.
<?php
use Pimcore\Model\DataObject\Product;
use Pimcore\Model\Element\Service;
$existing = Product::getByExternalId($externalId, 1);
$product = $existing instanceof Product ? $existing : new Product();
if (!$existing) {
$product->setExternalId($externalId);
$product->setParent($familyFolder);
$product->setKey(Service::getValidKey($sku, 'object'));
}
$product->setPublished(false); // reviewed publish happens in a later pass
applyClassificationValues($product, $row);
$product->save();Wir hielten Objekte unveröffentlicht, bis ein Prüfer freigab, sodass ein fehlerhafter Import nie den Live-Kanal erreichte. In einem regulierten Umfeld ist dieses Freigabe-Gate nicht optional, und es von Tag eins in den Import einzubauen ist günstiger, als es nach dem Go-live nachzurüsten.
Nicht sicher, ob Ihr Katalog überhaupt Pimcore braucht oder wie Sie PIM und DAM zusammen strukturieren? Genau damit fangen wir an.
PIM- und DAM-Beratung →Integration über die REST- und Data-Hub-API
Sobald die Daten drin waren, mussten nachgelagerte Systeme sie lesen. Pimcore bietet zwei zentrale Integrationsflächen: die Web-Service-REST-API und den GraphQL-Endpunkt des Data Hub. Für leselastige Integrationen, die gezielt einzelne Felder ziehen, war der GraphQL-Endpunkt die bessere Wahl, weil ein Konsument genau die Attribute anfragt, die er braucht, und nichts weiter.
query Product {
getProduct(id: 417) {
id
sku
material
sterilizationMethod
}
}Dieselbe Query läuft über einfaches HTTP gegen den Data-Hub-Endpunkt, was die Anbindung an jeden JSON-fähigen Konsumenten leicht macht.
{
"query": "query { getProduct(id: 417) { id sku material } }"
}Zwei Dinge, die Sie einplanen sollten. Der Data Hub stellt nur die Felder bereit, die Sie in seiner Konfiguration aktivieren, ein neues Attribut ist für Konsumenten also unsichtbar, bis der Endpunkt aktualisiert wird. Und Classification-Store-Schlüssel erscheinen in GraphQL unter den Namen, die Sie konfigurieren, sodass der API-Vertrag und die Store-Definition synchron bleiben müssen. Wir haben die Data-Hub-Feldliste aus demselben Mapping erzeugt, das den Import steuerte, damit beide nicht auseinanderdriften.
Fallstricke, auf die wir gestoßen sind
Performance bei großen Schlüsselmengen. Classification-Store-Lesezugriffe sind bei einer Handvoll Schlüsseln günstig und werden teuer, wenn ein Objekt hunderte trägt. Eine Produktliste zu laden und dabei jeden Store-Wert anzufassen, zog weit mehr Zeilen als erwartet. Die Lösung war, nur die Gruppen zu lesen, die eine Ansicht braucht, und das Gruppen- und Schlüssel-ID-Mapping zu cachen, statt Namen bei jedem Zugriff aufzulösen.
Lokalisierung von Attributwerten. Jeder Schlüssel ist entweder lokalisiert oder nicht, und diese Einstellung falsch zu setzen, ist mühsam rückgängig zu machen. Ein nicht lokalisiert gespeicherter Wert lässt sich später nicht einfach ohne Datenmigration pro Sprache aufteilen. Wir haben für alles, was ein Mensch schreibt, etwa Labels und Beschreibungen, auf lokalisiert gesetzt und für reine Messwerte und Enums auf nicht lokalisiert. Diese Entscheidung pro Schlüssel vorab zu treffen, ersparte uns eine zweite Migration.
Versionierungs-Rauschen beim Massenimport. Jedes Speichern erzeugt eine Version. Ein erster Import von tausenden Objekten, jedes mindestens einmal gespeichert, erzeugte eine Versionshistorie, die die inhaltlich relevanten redaktionellen Änderungen begrub und die Datenbank aufblähte. Für den Massenimport haben wir die Versionierung auf dem Importpfad deaktiviert und vor der Übergabe an die Redaktion wieder eingeschaltet.
<?php
use Pimcore\Model\Version;
// Silence version + notification noise during a bulk import.
Version::disable();
try {
foreach ($rows as $row) {
importProduct($row);
}
} finally {
Version::enable();
}Was wir anders machen würden
Eine ehrliche Korrektur. Wir haben einige Grenzfall-Attribute als Classification-Store-Schlüssel modelliert, die im Nachhinein als einfache Felder auf die Klasse gehört hätten. Sie kamen in fast jedem Listen-Filter vor, und diese Lesezugriffe über den Store zu leiten, kostete Query-Performance, die wir dann umbauen mussten. Wird ein Attribut oft abgefragt und ist bei nahezu allen Produkten vorhanden, wiegt der Komfort redaktionell gepflegter Schlüssel den Lesekostennachteil nicht auf. Beim nächsten Mal würden wir diese Grenze früher ziehen und den Store für den echten familienspezifischen langen Schwanz behalten.
Planen Sie eine Pimcore-Migration oder ein Classification-Store-Modell? Wir helfen Teams, das Datenmodell vor dem Import richtig aufzusetzen, damit die Migration nur einmal läuft. Erzählen Sie uns von Ihrem Katalog und den Systemen, die er versorgen muss.
Über eine Pimcore-Migration sprechen →