Eigene EAV-Attribute für Prämien definieren (Setup-Skript für Attribute)
Eigene EAV-Attribute für Prämien definieren (Setup-Skript für Attribute)
~8 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026
Sechs Tabellen (Kapitel 11) und ein Model/ResourceModel-Paar (Kapitel 12) reichen noch nicht - solange eav_entity_type nichts von mironsoft_loyalty_reward weiß und eav_attribute keine der sechs Prämien-Eigenschaften kennt, gibt es für AbstractEntity schlicht nichts zu laden. Dieses Kapitel registriert beides über Data Patches - Magentos moderner, deklarativer Ersatz für die alten InstallData/UpgradeData-Skripte, im selben Geist wie CLAUDE.mds Vorgabe, db_schema.xml statt Install-Skripten für das Schema selbst zu nutzen.
Erster Patch: den Entity Type registrieren
EavSetup::addEntityType() legt die Zeile in eav_entity_type an und verknüpft sie mit ResourceModel, Attribut-Model und Haupttabelle. Ist der Entity Type neu, legt die Methode zusätzlich automatisch ein Attribut-Set namens Default mit einer Gruppe namens General an - beide reichen für Rewards vollständig aus, ein eigenes Set ist hier nicht nötig.
<?php
declare(strict_types=1);
namespace Mironsoft\Loyalty\Setup\Patch\Data;
use Magento\Eav\Model\Entity\Attribute as EavAttribute;
use Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection as EavAttributeCollection;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Mironsoft\Loyalty\Model\Reward;
use Mironsoft\Loyalty\Model\ResourceModel\Reward as RewardResource;
/**
* Registers the mironsoft_loyalty_reward EAV entity type, including its
* automatically created "Default" attribute set and "General" group.
*/
class InstallRewardEntityType implements DataPatchInterface
{
/**
* @param ModuleDataSetupInterface $moduleDataSetup Provides the setup connection for the patch.
* @param EavSetupFactory $eavSetupFactory Creates the EavSetup helper used to register the entity type.
*/
public function __construct(
private readonly ModuleDataSetupInterface $moduleDataSetup,
private readonly EavSetupFactory $eavSetupFactory
) {
}
/**
* Adds the eav_entity_type row for rewards.
*
* @return void
*/
public function apply(): void
{
$this->moduleDataSetup->getConnection()->startSetup();
/** @var \Magento\Eav\Setup\EavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$eavSetup->addEntityType(Reward::ENTITY, [
'entity_model' => RewardResource::class,
'attribute_model' => EavAttribute::class,
'table' => 'mironsoft_loyalty_reward_entity',
'entity_attribute_collection' => EavAttributeCollection::class,
]);
$this->moduleDataSetup->getConnection()->endSetup();
}
/**
* @return array<int, string>
*/
public static function getDependencies(): array
{
return [];
}
/**
* @return array<int, string>
*/
public function getAliases(): array
{
return [];
}
}Zweiter Patch: die sechs Attribute
Der zweite Patch hängt über getDependencies() vom ersten ab - der Entity Type muss existieren, bevor ihm Attribute zugeordnet werden können. EavSetup::addAttribute() erwartet den Entity-Type-Code, den Attribut-Code und ein Array mit type (dem backend_type, siehe Kapitel 14), input (dem Admin-Formularfeld), required und weiteren Feldern.
<?php
declare(strict_types=1);
namespace Mironsoft\Loyalty\Setup\Patch\Data;
use Magento\Eav\Model\Entity\Attribute\Source\Boolean as BooleanSource;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Mironsoft\Loyalty\Model\Reward;
use Mironsoft\Loyalty\Model\Reward\Source\RewardType;
/**
* Registers the six custom EAV attributes of the mironsoft_loyalty_reward entity:
* title, description, points_cost, discount_value, reward_type, is_active.
*/
class InstallRewardAttributes implements DataPatchInterface
{
/**
* @param ModuleDataSetupInterface $moduleDataSetup Provides the setup connection for the patch.
* @param EavSetupFactory $eavSetupFactory Creates the EavSetup helper used to register attributes.
*/
public function __construct(
private readonly ModuleDataSetupInterface $moduleDataSetup,
private readonly EavSetupFactory $eavSetupFactory
) {
}
/**
* Adds all six reward attributes to the "General" group of the "Default" attribute set.
*
* @return void
*/
public function apply(): void
{
$this->moduleDataSetup->getConnection()->startSetup();
/** @var \Magento\Eav\Setup\EavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$eavSetup->addAttribute(Reward::ENTITY, 'title', [
'type' => 'varchar',
'label' => 'Title',
'input' => 'text',
'required' => true,
'sort_order' => 10,
'group' => 'General',
]);
$eavSetup->addAttribute(Reward::ENTITY, 'description', [
'type' => 'text',
'label' => 'Description',
'input' => 'textarea',
'required' => false,
'sort_order' => 20,
'group' => 'General',
]);
$eavSetup->addAttribute(Reward::ENTITY, 'points_cost', [
'type' => 'int',
'label' => 'Points Cost',
'input' => 'text',
'required' => true,
'sort_order' => 30,
'group' => 'General',
]);
$eavSetup->addAttribute(Reward::ENTITY, 'discount_value', [
'type' => 'decimal',
'label' => 'Discount Value',
'input' => 'text',
'required' => false,
'sort_order' => 40,
'group' => 'General',
]);
$eavSetup->addAttribute(Reward::ENTITY, 'reward_type', [
'type' => 'varchar',
'label' => 'Reward Type',
'input' => 'select',
'source' => RewardType::class,
'required' => true,
'sort_order' => 50,
'group' => 'General',
]);
$eavSetup->addAttribute(Reward::ENTITY, 'is_active', [
'type' => 'int',
'label' => 'Is Active',
'input' => 'boolean',
'source' => BooleanSource::class,
'required' => false,
'default' => '1',
'sort_order' => 60,
'group' => 'General',
]);
$this->moduleDataSetup->getConnection()->endSetup();
}
/**
* @return array<int, string>
*/
public static function getDependencies(): array
{
return [InstallRewardEntityType::class];
}
/**
* @return array<int, string>
*/
public function getAliases(): array
{
return [];
}
}Das Source Model für reward_type
reward_type ist als Dropdown definiert (input => 'select') und braucht dafür ein Source Model, das die Optionsliste liefert - dieselbe Rolle, die Kapitel 24 in Block 3 für das Treue-Stufen-Dropdown am Kunden übernimmt.
<?php
declare(strict_types=1);
namespace Mironsoft\Loyalty\Model\Reward\Source;
use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;
/**
* Source model for the reward_type dropdown attribute.
*/
class RewardType extends AbstractSource
{
/**
* @var string
*/
public const TYPE_DISCOUNT = 'discount';
/**
* @var string
*/
public const TYPE_FREE_PRODUCT = 'free_product';
/**
* @var string
*/
public const TYPE_FREE_SHIPPING = 'free_shipping';
/**
* Returns the dropdown options shown in the admin form and used for grid filtering.
*
* @return array<int, array{value: string, label: string}>
*/
public function getAllOptions(): array
{
if ($this->_options === null) {
$this->_options = [
['value' => self::TYPE_DISCOUNT, 'label' => __('Discount')],
['value' => self::TYPE_FREE_PRODUCT, 'label' => __('Free Product')],
['value' => self::TYPE_FREE_SHIPPING, 'label' => __('Free Shipping')],
];
}
return $this->_options;
}
}bin/magento setup:upgrade
bin/magento cache:flushTipp: Data Patches laufen automatisch bei jedem setup:upgrade genau einmal - Magento merkt sich ausgeführte Patches in der Tabelle patch_list. Eine spätere Änderung an apply() wirkt sich auf bereits installierte Umgebungen nicht mehr aus; für Attribut-Änderungen nach dem ersten Release braucht es einen neuen, zusätzlichen Patch.
Achtung: getDependencies() ist eine static-Methode - ein leicht zu übersehender Unterschied zu getAliases(), das eine Instanzmethode ist. Ein PHPStan-Level-5-Lauf über Setup/Patch/Data/ macht solche Signatur-Abweichungen sofort sichtbar.
Mit registriertem Entity Type und sechs Attributen existiert jetzt eine vollständig funktionsfähige EAV-Entity. Kapitel 14 sieht sich die vier tatsächlich genutzten backend_type-Werte im Detail an.