statt organisch wuchern lassen
In den meisten Agenturprojekten wachsen Attribut-Sets unkontrolliert: Jeder neue Produkttyp bekommt sein eigenes Set, Namenskonventionen fehlen, und nach zwei Jahren verwaltet niemand mehr, welche der achtzig Sets tatsächlich noch produktiv genutzt werden. Wer Attribut-Sets stattdessen vor der Erstellung an Produkttypen und Geschäftsanforderungen ausrichtet, sie programmatisch über Data Patches erzeugt und über einen klaren Governance-Prozess kontrolliert, hält die EAV-Struktur wartbar, performant und für neue Entwickler nachvollziehbar.
Inhaltsverzeichnis
- 1. Wildwuchs: Wie Attribut-Sets in Agenturprojekten unkontrolliert wachsen
- 2. Attribut-Sets strategisch planen: Produkttypen und Anforderungen zuerst
- 3. Attribut-Gruppen und sort_order innerhalb eines Sets
- 4. Klonen vs. Neuanlage: Die richtige Basis wählen
- 5. Programmatische Erstellung mit Data Patch und EavSetup
- 6. Attribut-Sets zuweisen: programmatisch und per Import
- 7. Governance-Prozess: Berechtigungen und Namenskonventionen
- 8. Performance- und Indexierungs-Auswirkungen
- 9. Sicher konsolidieren: ungenutzte Attribut-Sets im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Wildwuchs: Wie Attribut-Sets in Agenturprojekten unkontrolliert wachsen
In fast jedem gewachsenen Magento-Projekt findet sich dasselbe Bild: eine dreistellige Zahl an Attribut-Sets, von denen niemand mehr genau erklären kann, wofür die Hälfte gedacht war. Der Grund ist selten technisch, sondern organisatorisch. Ein neuer Produkttyp kommt hinzu, ein Merchandiser klickt im Admin auf "Neues Set anlegen", vergibt einen Namen wie "Set 2" oder "Test Winterkollektion" und speichert. Niemand prüft, ob ein bestehendes Set mit einer zusätzlichen Attribut-Gruppe ausgereicht hätte. Nach einem Jahr Agenturbetrieb mit mehreren Redakteuren, Praktikanten und externen Dienstleistern kommen so leicht fünfzig bis hundert Attribut-Sets zusammen, von denen ein Großteil nie wieder verwendet wird.
Das eigentliche Problem ist nicht die Anzahl an sich, sondern der fehlende Plan dahinter. Jedes Attribut-Set in Magento 2 ist eine Kombination aus Entity-Type, Attribut-Gruppen und einer Liste zugeordneter Attribute mit individueller Sortierung. Ohne eine bewusste Struktur bildet sich diese Kombination zufällig, abhängig davon, wer gerade Zugriff auf den Admin-Bereich hatte. Das Ergebnis: doppelte Sets mit fast identischem Inhalt, Sets ohne ein einziges zugeordnetes Produkt und Sets, deren Name nichts über den Zweck verrät. Jede dieser Situationen kostet später Zeit, sei es beim Onboarding neuer Entwickler, beim Produktimport oder bei der Fehlersuche, warum ein Attribut im Frontend nicht angezeigt wird.
2. Attribut-Sets strategisch planen: Produkttypen und Anforderungen zuerst
Ein tragfähiger Ansatz beginnt vor dem ersten Klick im Admin: Welche Produkttypen gibt es im Katalog wirklich, und welche davon unterscheiden sich fachlich so stark, dass sie eigene Attribute brauchen? Ein Bekleidungshändler mit einfachen und konfigurierbaren Produkten braucht typischerweise deutlich weniger eigenständige Attribut-Sets als vermutet, wenn man Varianten über Attribut-Gruppen statt über komplett neue Sets abbildet. Die Faustregel: ein neues Set lohnt sich, wenn sich die Menge der relevanten Attribute signifikant unterscheidet, nicht wenn nur ein einzelnes Zusatzattribut fehlt. Für Letzteres reicht es, das Attribut dem bestehenden Set hinzuzufügen.
In der Praxis hat sich ein kurzer Planungsschritt vor jedem neuen Modul bewährt: eine Tabelle mit Produkttyp, den fachlich zwingenden Attributen und der Frage, ob ein bestehendes Set diese Attribute bereits vollständig oder fast vollständig abdeckt. Erst wenn diese Prüfung negativ ausfällt, folgt die Erstellung eines neuen Attribut-Sets. Diese Denkweise verhindert die häufigste Wurzelursache des Wildwuchses: die Annahme, dass jede neue Produktkategorie automatisch ein eigenes Set braucht, obwohl Kategorie und Attribut-Set in Magento 2 vollkommen unabhängige Konzepte sind. Kategorien steuern die Navigation, Attribut-Sets steuern, welche Felder ein Produkt im Admin-Formular und potenziell im Frontend zeigt.
3. Attribut-Gruppen und sort_order innerhalb eines Sets
Innerhalb eines Attribut-Sets sorgen Attribut-Gruppen für die Struktur, die im Produkt-Editor als Tabs oder Abschnitte sichtbar wird. Jede Gruppe hat einen eigenen sort_order, der die Reihenfolge der Tabs bestimmt, und jedes Attribut innerhalb einer Gruppe hat ebenfalls einen eigenen sort_order, der die Reihenfolge der Felder innerhalb des Tabs festlegt. Diese doppelte Sortierung wird in der Praxis oft übersehen, weil der Standard-Import und die Standard-Attribute mit sinnvollen Werten vorbelegt sind. Sobald aber eigene Gruppen hinzukommen, etwa "Technische Daten" oder "SEO-Attribute", muss die Sortierung bewusst gesetzt werden, sonst landen neue Felder ans Ende der Liste und werden von Redakteuren übersehen.
Sinnvoll strukturierte Gruppen reduzieren zudem den Bedarf an komplett neuen Attribut-Sets: Statt für jeden Produkttyp ein eigenes Set mit denselben Basisattributen anzulegen, kann eine zusätzliche, optionale Gruppe im bestehenden Set die abweichenden Felder aufnehmen. Das folgende Beispiel zeigt, wie eine neue Gruppe samt Sortierung über EavSetup in einer Data Patch angelegt wird, inklusive der Zuordnung eines bestehenden Attributs mit eigenem sort_order innerhalb der Gruppe.
<?php
declare(strict_types=1);
namespace Mironsoft\AttributeSetGuard\Setup\Patch\Data;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Catalog\Model\Product;
/**
* Adds a dedicated "Technische Daten" attribute group with explicit sort_order
* to the existing "Default" attribute set instead of creating a new set.
*/
final class AddTechnicalDataGroup implements DataPatchInterface
{
public function __construct(
private readonly ModuleDataSetupInterface $moduleDataSetup,
private readonly EavSetupFactory $eavSetupFactory,
) {
}
/**
* Creates the group and assigns an existing attribute with its own sort_order.
*
* @return void
*/
public function apply(): void
{
$this->moduleDataSetup->getConnection()->startSetup();
/** @var EavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$entityTypeId = $eavSetup->getEntityTypeId(Product::ENTITY);
$attributeSetId = $eavSetup->getAttributeSetId($entityTypeId, 'Default');
// Group sort_order 25 places the new tab after "Advanced Pricing"
$groupId = $eavSetup->addAttributeGroup(
$entityTypeId,
$attributeSetId,
'Technische Daten',
25
);
// Attribute sort_order 10 is the first field inside the new group
$eavSetup->addAttributeToGroup(
$entityTypeId,
$attributeSetId,
$groupId,
'material',
10
);
$this->moduleDataSetup->getConnection()->endSetup();
}
/**
* @return array<int, string>
*/
public static function getDependencies(): array
{
return [];
}
/**
* @return array<int, string>
*/
public function getAliases(): array
{
return [];
}
}
4. Klonen vs. Neuanlage: Die richtige Basis wählen
Wenn ein neuer Produkttyp fachlich tatsächlich ein eigenes Attribut-Set rechtfertigt, stellt sich die nächste Frage: von Grund auf neu anlegen oder ein bestehendes Set klonen? Magento 2 bietet dafür im Admin-Formular unter "Neues Attribut-Set" das Feld "Basierend auf", das intern die Methode initFromSkeleton() der Klasse Magento\Eav\Model\Entity\Attribute\Set nutzt. Diese Methode kopiert sämtliche Attribut-Gruppen samt ihrer Sortierung sowie alle zugeordneten Attribute des gewählten Skeleton-Sets in das neue Set. Das ist in den allermeisten Fällen die richtige Wahl, weil ein neuer Produkttyp selten komplett andere Basisattribute braucht als der Rest des Katalogs, etwa Name, Beschreibung, Preis oder SEO-Felder.
Neuanlage von Grund auf ist nur dann sinnvoll, wenn ein Set bewusst minimal gehalten werden soll, etwa für einen sehr schlanken Import von Ersatzteil- oder Zubehördaten ohne die üblichen Marketing-Attribute. In der Praxis führt das Klonen zu deutlich konsistenteren Attribut-Sets, weil Basisstruktur und Sortierung über alle Sets hinweg gleich bleiben und nur die abweichenden Attribute ergänzt werden müssen. Das folgende Beispiel klont ein bestehendes Set programmatisch, was sich anbietet, wenn dieser Schritt reproduzierbar über mehrere Umgebungen laufen soll, statt einmalig im Admin ausgeführt zu werden.
<?php
declare(strict_types=1);
namespace Mironsoft\AttributeSetGuard\Setup\Patch\Data;
use Magento\Eav\Api\AttributeSetRepositoryInterface;
use Magento\Eav\Api\Data\AttributeSetInterfaceFactory;
use Magento\Eav\Model\Entity\Attribute\Set;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Catalog\Model\Product;
/**
* Clones the "Default" attribute set into a new "Konfigurierbar - Bekleidung" set
* using initFromSkeleton so groups, sort_order and attributes stay consistent.
*/
final class CloneClothingAttributeSet implements DataPatchInterface
{
public function __construct(
private readonly ModuleDataSetupInterface $moduleDataSetup,
private readonly EavSetupFactory $eavSetupFactory,
private readonly AttributeSetRepositoryInterface $attributeSetRepository,
private readonly AttributeSetInterfaceFactory $attributeSetFactory,
) {
}
/**
* Clones the skeleton set and persists the new set via the repository.
*
* @return void
*/
public function apply(): void
{
$this->moduleDataSetup->getConnection()->startSetup();
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$entityTypeId = (int) $eavSetup->getEntityTypeId(Product::ENTITY);
$skeletonId = (int) $eavSetup->getAttributeSetId($entityTypeId, 'Default');
/** @var Set $newSet */
$newSet = $this->attributeSetFactory->create();
$newSet->setEntityTypeId($entityTypeId);
$newSet->setAttributeSetName('Konfigurierbar - Bekleidung');
// Copies groups, sort_order and attribute assignments from the skeleton set
$newSet->validate();
$newSet->initFromSkeleton($skeletonId);
$this->attributeSetRepository->save($newSet);
$this->moduleDataSetup->getConnection()->endSetup();
}
/**
* @return array<int, string>
*/
public static function getDependencies(): array
{
return [];
}
/**
* @return array<int, string>
*/
public function getAliases(): array
{
return [];
}
}
5. Programmatische Erstellung mit Data Patch und EavSetup
Sobald feststeht, dass ein neues Attribut-Set tatsächlich von Grund auf entstehen soll, gehört diese Erstellung in eine versionierte Data Patch, nicht in einen manuellen Admin-Klick. Der Unterschied ist entscheidend für Reproduzierbarkeit: Eine Data Patch läuft bei jedem setup:upgrade in jeder Umgebung exakt einmal, ist Teil des Code-Reviews und lässt sich über die patch_list-Tabelle nachvollziehen. Ein im Admin manuell angelegtes Set existiert dagegen nur in der Datenbank, in der es erstellt wurde, was bei Staging, Produktion und lokalen Entwicklungsumgebungen zu abweichenden Ständen führt, wenn niemand die manuelle Anlage dokumentiert.
Magento stellt für diese Aufgabe die Klasse Magento\Eav\Setup\EavSetup bereit, die über eine EavSetupFactory injiziert wird. Die Methode addAttributeSet() erzeugt das Set, addAttributeGroup() die zugehörigen Gruppen und addAttributeToGroup() ordnet vorhandene Attribute den Gruppen zu. Wichtig dabei: addAttributeSet() erzeugt intern bereits eine Standardgruppe, sodass eigene Aufrufe von addAttributeGroup() nur für zusätzliche Gruppen nötig sind. Die folgende Data Patch zeigt die vollständige Erstellung eines neuen Attribut-Sets für digitale Produkte, inklusive einer eigenen Gruppe und der Zuordnung mehrerer Attribute mit expliziter Sortierung.
<?php
declare(strict_types=1);
namespace Mironsoft\AttributeSetGuard\Setup\Patch\Data;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Catalog\Model\Product;
/**
* Creates a fresh "Digital - Lizenzprodukt" attribute set from scratch
* with its own group and explicit attribute sort_order.
*/
final class CreateDigitalProductAttributeSet implements DataPatchInterface
{
/**
* @param ModuleDataSetupInterface $moduleDataSetup Setup connection wrapper
* @param EavSetupFactory $eavSetupFactory Factory for the EAV setup helper
*/
public function __construct(
private readonly ModuleDataSetupInterface $moduleDataSetup,
private readonly EavSetupFactory $eavSetupFactory,
) {
}
/**
* Creates the attribute set, a dedicated group and assigns attributes.
*
* @return void
*/
public function apply(): void
{
$this->moduleDataSetup->getConnection()->startSetup();
/** @var EavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$entityTypeId = $eavSetup->getEntityTypeId(Product::ENTITY);
// Cloning the "Default" set as skeleton keeps base attributes consistent
$skeletonId = $eavSetup->getAttributeSetId($entityTypeId, 'Default');
$attributeSetId = $eavSetup->addAttributeSet(
$entityTypeId,
'Digital - Lizenzprodukt',
null,
$skeletonId
);
$groupId = $eavSetup->addAttributeGroup(
$entityTypeId,
$attributeSetId,
'Lizenzdaten',
30
);
$eavSetup->addAttributeToGroup($entityTypeId, $attributeSetId, $groupId, 'license_type', 10);
$eavSetup->addAttributeToGroup($entityTypeId, $attributeSetId, $groupId, 'license_duration', 20);
$eavSetup->addAttributeToGroup($entityTypeId, $attributeSetId, $groupId, 'download_limit', 30);
$this->moduleDataSetup->getConnection()->endSetup();
}
/**
* @return array<int, string>
*/
public static function getDependencies(): array
{
return [];
}
/**
* @return array<int, string>
*/
public function getAliases(): array
{
return [];
}
}
6. Attribut-Sets zuweisen: programmatisch und per Import
Ein neues Attribut-Set nützt wenig, solange keine Produkte darauf verweisen. Programmatisch geschieht die Zuweisung über ProductRepositoryInterface: Ein Produkt wird geladen, setAttributeSetId() gesetzt und über save() persistiert. Wichtig ist dabei, dass ein Wechsel des Attribut-Sets auf einem bestehenden Produkt Attributwerte nicht automatisch löscht, sie werden lediglich im neuen Set nicht mehr angezeigt, solange das Attribut dort nicht zugeordnet ist. Für Massenänderungen an vielen Produkten empfiehlt sich statt einer Schleife über save() die Nutzung der Produkt-Massenaktionen im Admin-Grid oder ein eigener Batch-Prozess mit expliziter Fehlerbehandlung pro Produkt, damit ein einzelner ungültiger Datensatz nicht den gesamten Lauf abbricht.
Beim CSV-Import ist die Zuordnung über die Spalte attribute_set_code vorgesehen, die den Namen des Attribut-Sets als Text erwartet, nicht die numerische ID. Der Import-Mechanismus in Magento\CatalogImportExport\Model\Import\Product validiert diesen Wert gegen die Liste existierender Sets und bricht die betroffene Zeile mit einem Validierungsfehler ab, wenn der Name nicht exakt existiert. Genau hier zeigt sich, warum konsistente Namensgebung kein kosmetisches Detail ist: Tippfehler oder mehrere Sets mit fast identischem Namen wie "Bekleidung" und "Bekleidung " mit Leerzeichen führen zu stillen Fehlzuordnungen oder abgebrochenen Importzeilen, deren Ursache im Fehlerprotokoll nicht sofort ersichtlich ist.
<?php
declare(strict_types=1);
namespace Mironsoft\AttributeSetGuard\Console;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Eav\Api\AttributeSetRepositoryInterface;
use Magento\Eav\Api\Data\AttributeSetSearchResultsInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Reassigns a batch of products to a target attribute set by SKU list,
* failing individually per SKU instead of aborting the whole run.
*/
final class ReassignAttributeSetCommand extends Command
{
public function __construct(
private readonly ProductRepositoryInterface $productRepository,
private readonly AttributeSetRepositoryInterface $attributeSetRepository,
private readonly SearchCriteriaBuilder $searchCriteriaBuilder,
) {
parent::__construct('mironsoft:attribute-set:reassign');
}
/**
* Configures the command name, arguments and description.
*
* @return void
*/
protected function configure(): void
{
$this->setDescription('Reassigns products to a target attribute set by SKU');
$this->addArgument('attribute-set-name', InputArgument::REQUIRED);
$this->addArgument('skus', InputArgument::IS_ARRAY | InputArgument::REQUIRED);
}
/**
* @param InputInterface $input Command input
* @param OutputInterface $output Command output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$setName = (string) $input->getArgument('attribute-set-name');
$criteria = $this->searchCriteriaBuilder
->addFilter('attribute_set_name', $setName)
->create();
/** @var AttributeSetSearchResultsInterface $result */
$result = $this->attributeSetRepository->getList($criteria);
$items = $result->getItems();
if (count($items) === 0) {
$output->writeln(sprintf('<error>Attribute set "%s" not found</error>', $setName));
return Command::FAILURE;
}
$targetSetId = current($items)->getAttributeSetId();
foreach ((array) $input->getArgument('skus') as $sku) {
try {
$product = $this->productRepository->get((string) $sku);
$product->setAttributeSetId((int) $targetSetId);
$this->productRepository->save($product);
$output->writeln(sprintf('[OK] %s moved to set %s', $sku, $setName));
} catch (\Throwable $exception) {
$output->writeln(sprintf('[FAIL] %s: %s', $sku, $exception->getMessage()));
}
}
return Command::SUCCESS;
}
}
7. Governance-Prozess: Berechtigungen und Namenskonventionen
Technische Lösungen wie Data Patches verhindern Wildwuchs nur, wenn sie auch tatsächlich genutzt werden. Der zweite, mindestens ebenso wichtige Baustein ist ein Governance-Prozess: Wer im Team darf ein neues Attribut-Set überhaupt anlegen? In den meisten Agenturprojekten ist die Antwort implizit "jeder mit Admin-Zugriff", was in der Praxis zu genau dem Wildwuchs führt, der eingangs beschrieben wurde. Ein wirksamer Ansatz beschränkt das native ACL-Recht für die Attribut-Set-Verwaltung im Admin über die Benutzerrollen-Verwaltung auf einen kleinen Kreis, typischerweise die Backend-Entwicklung und einen benannten Verantwortlichen auf Kundenseite, während Redakteure und Merchandiser weiterhin Produkte bearbeiten, aber keine neuen Sets erzeugen können.
Ergänzend dazu gehört eine schriftlich festgehaltene Namenskonvention zum Pflichtprogramm, etwa nach dem Schema Produkttyp, Kategorie, optionale Variante, also "Konfigurierbar - Bekleidung" oder "Einfach - Elektronik - Zubehör". Ein solches Schema macht den Zweck eines Attribut-Sets sofort erkennbar, ohne dass jemand die zugeordneten Attribute einzeln durchsehen muss, und verhindert die im Import beschriebenen Kollisionen durch fast identische Namen. In Projekten mit mehreren Beteiligten hat es sich bewährt, diese Konvention nicht nur zu dokumentieren, sondern auch technisch über ein Observer- oder Plugin-Pattern auf dem Speichervorgang des Attribut-Set-Controllers zu erzwingen: Ein Name, der nicht dem definierten Muster entspricht, wird mit einer verständlichen Fehlermeldung abgelehnt, statt stillschweigend gespeichert zu werden. So bleibt die Konvention auch dann wirksam, wenn die schriftliche Dokumentation in Vergessenheit gerät.
8. Performance- und Indexierungs-Auswirkungen
Eine häufig unterschätzte Folge zu vieler Attribut-Sets betrifft die Admin-Performance, nicht primär das Frontend. Das Dropdown zur Auswahl des Attribut-Sets auf der "Neues Produkt"-Seite rendert bei mehreren hundert Einträgen spürbar langsamer, weil die Liste ungefiltert und unsortiert als HTML-Select aufgebaut wird. Gravierender ist der Effekt auf die interne Konfigurations-Auflösung: Magento\Eav\Model\Config::getEntityAttributes() wird pro Kombination aus Entity-Type und Attribut-Set aufgerufen und das Ergebnis separat gecacht. Je mehr unterschiedliche Sets existieren, desto mehr einzelne Cache-Einträge müssen nach einer Cache-Leerung neu aufgebaut werden, was sich beim ersten Laden jeder Produktseite im Admin nach einem Deploy als spürbare Verzögerung bemerkbar macht.
Für die Katalog-Indexer selbst ist die reine Anzahl an Attribut-Sets ein sekundärer Faktor, da die relevanten Indexer wie "Product EAV" primär attributbezogen und nicht set-bezogen arbeiten. Der eigentliche Performance-Hebel liegt in der Konsequenz aus unkontrolliertem Wachstum: Wenn viele Sets nahezu identische Attribute in leicht unterschiedlicher Kombination enthalten, wächst die Zahl der eindeutigen Attribut-Konfigurationen, die das System vorhalten und cachen muss, ohne dass ein fachlicher Mehrwert entsteht. Für Importe gilt ähnliches: Eine Validierung gegen mehrere hundert Attribut-Sets pro Zeile dauert zwar dank indexierter Namenssuche nicht merklich länger, aber die Fehleranfälligkeit durch Tippfehler oder Verwechslungen steigt proportional zur Anzahl ähnlich benannter Sets.
9. Sicher konsolidieren: ungenutzte Attribut-Sets im Vergleich
Bevor ein Attribut-Set gelöscht wird, muss zweifelsfrei feststehen, dass kein Produkt mehr darauf verweist. Eine einfache Prüfabfrage gruppiert die Tabelle catalog_product_entity nach attribute_set_id und vergleicht das Ergebnis mit der Liste aller Sets aus eav_attribute_set. Sets, die in dieser Gegenüberstellung mit null zugeordneten Produkten auftauchen, sind Kandidaten für die Bereinigung, sollten aber vor dem Löschen zusätzlich gegen Entwurfsprodukte, deaktivierte Produkte und Multi-Store-Zuordnungen geprüft werden, da ein Produkt trotz "Disabled"-Status weiterhin ein Attribut-Set referenziert. Erst nach dieser doppelten Prüfung sollte die Löschung über AttributeSetRepositoryInterface::deleteById() erfolgen, da diese Methode die referenzielle Integrität der zugehörigen Gruppen und Attribut-Zuordnungen korrekt auflöst, während ein direkter Eingriff in die Datenbank verwaiste Zeilen in eav_attribute_group und eav_entity_attribute hinterlassen kann.
Für ein produktives Audit-Werkzeug lohnt sich ein eigenes Console Command, das genau diese Gegenüberstellung automatisiert ausführt und tabellarisch ausgibt, ergänzt um eine System.xml-Konfiguration, über die ein Schwellenwert für die Mindestanzahl an Produkten pro Attribut-Set hinterlegt werden kann. So lässt sich der Audit-Lauf regelmäßig per Cron ausführen und meldet automatisch Sets, die unterhalb des konfigurierten Schwellenwerts liegen, statt dass jemand die Prüfung manuell und unregelmäßig anstößt. Der Schwellenwert selbst wird über ScopeConfigInterface aus einem eigenen system.xml-Feld gelesen, sodass Kunde oder Projektleitung ihn ohne Code-Änderung anpassen können.
<?php
declare(strict_types=1);
namespace Mironsoft\AttributeSetGuard\Console;
use Magento\Eav\Api\AttributeSetRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ResourceConnection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\Table;
/**
* Audits all attribute sets and reports those below the configured
* minimum product threshold, read from system.xml via ScopeConfigInterface.
*/
final class AuditAttributeSetsCommand extends Command
{
private const XML_PATH_MIN_PRODUCTS = 'mironsoft_attributesetguard/general/min_products_threshold';
public function __construct(
private readonly AttributeSetRepositoryInterface $attributeSetRepository,
private readonly SearchCriteriaBuilder $searchCriteriaBuilder,
private readonly ResourceConnection $resourceConnection,
private readonly ScopeConfigInterface $scopeConfig,
) {
parent::__construct('mironsoft:attribute-set:audit');
}
/**
* Configures the command name and description.
*
* @return void
*/
protected function configure(): void
{
$this->setDescription('Lists attribute sets below the configured minimum product count');
}
/**
* Compares eav_attribute_set against catalog_product_entity and prints
* every set whose product count is below the configured threshold.
*
* @param InputInterface $input Command input
* @param OutputInterface $output Command output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$threshold = (int) $this->scopeConfig->getValue(self::XML_PATH_MIN_PRODUCTS);
$connection = $this->resourceConnection->getConnection();
$productCounts = $connection->fetchPairs(
$connection->select()
->from($this->resourceConnection->getTableName('catalog_product_entity'), ['attribute_set_id'])
->columns(['product_count' => 'COUNT(*)'])
->group('attribute_set_id')
);
$criteria = $this->searchCriteriaBuilder->create();
$rows = [];
foreach ($this->attributeSetRepository->getList($criteria)->getItems() as $attributeSet) {
$setId = (int) $attributeSet->getAttributeSetId();
$count = (int) ($productCounts[$setId] ?? 0);
if ($count < $threshold) {
$rows[] = [$setId, $attributeSet->getAttributeSetName(), $count];
}
}
$table = new Table($output);
$table->setHeaders(['Set ID', 'Name', 'Products'])->setRows($rows);
$table->render();
return Command::SUCCESS;
}
}
| Aufgabe | Unsicher / Riskant | Empfohlenes Muster | Vorteil |
|---|---|---|---|
| Set löschen | DELETE FROM eav_attribute_set |
AttributeSetRepositoryInterface::deleteById() |
Referenzielle Integrität bleibt erhalten, keine verwaisten Zeilen |
| Neues Set anlegen | Admin-UI ad hoc, undokumentiert |
Data Patch mit EavSetup, versioniert |
Reproduzierbar in jeder Umgebung, Code-Review möglich |
| Namensgebung | "Set 2", "Test", "NEU final" |
Produkttyp - Kategorie - Variante |
Zweck sofort erkennbar, keine Kollisionen im Import |
| Set klonen | Attribute manuell nachbauen |
initFromSkeleton() auf Basis-Set |
Gruppen und sort_order werden korrekt übernommen |
| Zuweisung im Import | attribute_set_id fest verdrahtet |
attribute_set_code, vorab validiert |
Import bricht kontrolliert ab statt falsches Set zuzuweisen |
Der Wert dieser Tabelle liegt nicht in Einzeltipps, sondern in der Konsequenz über das gesamte Projekt hinweg. Ein Team, das für die Hälfte der Vorgänge das empfohlene Muster nutzt und bei der anderen Hälfte auf den ad-hoc-Weg zurückfällt, hat am Ende trotzdem denselben Wildwuchs, nur langsamer. Erst die durchgängige Anwendung, unterstützt durch ein Audit-Werkzeug, das Abweichungen sichtbar macht, hält die Anzahl der Attribut-Sets dauerhaft im Rahmen der tatsächlichen fachlichen Anforderungen.
Mironsoft
Magento 2 Architektur, EAV-Governance und Hyvä-Frontend
Attribut-Sets außer Kontrolle geraten?
Wir analysieren bestehende Attribut-Sets, konsolidieren ungenutzte Strukturen und bauen Data Patches und Audit-Werkzeuge, die Wildwuchs in eurem Katalog dauerhaft verhindern.
Bestandsaudit
Analyse aller Attribut-Sets, Identifikation ungenutzter und doppelter Strukturen
Migration & Konsolidierung
Sichere Zusammenführung über Data Patches statt riskanter manueller Eingriffe
Governance-Setup
ACL-Rechte, Namenskonventionen und Audit-Commands für nachhaltige Kontrolle
10. Zusammenfassung
Attribut-Sets sind kein technisches Randthema, sondern ein zentraler Baustein der Katalogarchitektur, der ohne Planung schnell aus dem Ruder läuft. Der Schlüssel liegt darin, Attribut-Sets an tatsächlichen Unterschieden bei Produkttypen auszurichten, statt sie für jede kleine Abweichung neu zu erzeugen. Attribut-Gruppen mit sauber gesetztem sort_order lösen die meisten Anforderungen, für die reflexhaft ein neues Set angelegt würde. Wo ein neues Set wirklich nötig ist, gehört die Erstellung in eine versionierte Data Patch mit EavSetup, idealerweise auf Basis eines geklonten Skeleton-Sets über initFromSkeleton.
Genauso wichtig wie die Technik ist der organisatorische Rahmen: klare Berechtigungen, wer neue Attribut-Sets anlegen darf, eine verbindliche Namenskonvention und ein Audit-Werkzeug, das regelmäßig ungenutzte Sets sichtbar macht, bevor sie sich unbemerkt ansammeln. Wer diese beiden Ebenen, technische Erzeugung und organisatorische Kontrolle, konsequent zusammen denkt, verhindert genau den Wildwuchs, der in ungeplanten Agenturprojekten nach wenigen Jahren zur Regel wird.
Attribut-Sets in Magento 2, das Wichtigste auf einen Blick
Planung vor Erstellung
Neues Set nur bei fachlich signifikant abweichenden Attributen, sonst reicht eine zusätzliche Attribut-Gruppe im bestehenden Set.
Programmatische Erstellung
Data Patch mit EavSetup und initFromSkeleton statt Admin-Ad-hoc-Klick. Versioniert, reproduzierbar, code-reviewbar.
Governance & Namenskonvention
ACL-Recht auf wenige Rollen beschränken. Feste Namenskonvention wie Produkttyp - Kategorie - Variante durchsetzen.
Performance & Konsolidierung
Regelmäßiges Audit gegen catalog_product_entity, Löschung ausschließlich über AttributeSetRepositoryInterface.
11. FAQ: Attribut-Sets in Magento 2 verwalten
1Was ist ein Attribut-Set in Magento 2 genau?
2Warum wuchern Attribut-Sets in Agenturprojekten?
3Wie plane ich Attribut-Sets sinnvoll?
4Unterschied Attribut-Gruppen und Attribut-Sets?
5Wann klonen statt neu erstellen?
6Wie erstelle ich Sets per Data Patch?
7Zuweisung per Import?
8Wer darf neue Sets anlegen?
9Wirken sich viele Sets auf Performance aus?
10Ungenutztes Set sicher entfernen?
Mironsoft
Magento 2 Architektur, EAV-Governance und Hyvä-Frontend
Ordnung in euren Attribut-Sets schaffen?
Von der Bestandsaufnahme über Data Patches bis zum Audit-Werkzeug: Wir bringen Struktur in gewachsene Magento-Kataloge und verhindern, dass Attribut-Sets erneut unkontrolliert wachsen.
Analyse
Vollständige Bestandsaufnahme aller Attribut-Sets und ihrer tatsächlichen Nutzung
Umsetzung
Data Patches für Konsolidierung, Zuweisung und Bereinigung ohne Datenverlust
Langfristig
Governance-Prozess und Audit-Command für dauerhaft saubere Strukturen