Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Model/ResourceModel für eine EAV-Entity schreiben

Model/ResourceModel für eine EAV-Entity schreiben

~7 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026

Die Tabellen aus Kapitel 11 existieren, aber kein PHP-Code kennt sie noch. Anders als beim flachen Punkte-Ledger (Kapitel 4, AbstractDb) braucht eine EAV-Entity ein ResourceModel, das mit mehreren Wertetabellen gleichzeitig umgehen kann - Magentos Magento\Eav\Model\Entity\AbstractEntity übernimmt genau diese Arbeit.

Das Model

Das Model selbst unterscheidet sich kaum vom flachen Muster aus Kapitel 4 - es erbt weiterhin von AbstractModel und bindet sich an sein ResourceModel. Neu ist die ENTITY-Konstante: der Entity-Type-Code, den sowohl das ResourceModel als auch das Setup-Skript aus Kapitel 13 referenzieren, statt die Zeichenkette mironsoft_loyalty_reward an mehreren Stellen zu wiederholen.

app/code/Mironsoft/Loyalty/Model/Reward.php
<?php

declare(strict_types=1);

namespace Mironsoft\Loyalty\Model;

use Magento\Framework\Model\AbstractModel;
use Mironsoft\Loyalty\Model\ResourceModel\Reward as RewardResource;

/**
 * Reward EAV entity model, represents a single redeemable reward.
 */
class Reward extends AbstractModel
{
    /**
     * @var string
     */
    public const ENTITY = 'mironsoft_loyalty_reward';

    /**
     * Binds the model to its resource model.
     *
     * @return void
     */
    protected function _construct(): void
    {
        $this->_init(RewardResource::class);
    }
}

Das ResourceModel: AbstractEntity statt AbstractDb

AbstractEntity übernimmt weit mehr als AbstractDb: Es kennt die Haupttabelle, ermittelt beim Laden automatisch die zum Entity Type gehörenden Attribute, holt deren Werte aus den passenden Wertetabellen und fügt sie dem Model als gewöhnliche Datenfelder hinzu - $reward->getTitle() funktioniert am Ende genauso wie bei einer flachen Entity, obwohl der Wert aus einer völlig anderen Tabelle stammt als $reward->getIdentifier().

app/code/Mironsoft/Loyalty/Model/ResourceModel/Reward.php
<?php

declare(strict_types=1);

namespace Mironsoft\Loyalty\Model\ResourceModel;

use Magento\Eav\Model\Entity\AbstractEntity;
use Magento\Framework\App\ResourceConnection;
use Mironsoft\Loyalty\Model\Reward as RewardModel;

/**
 * EAV resource model for the reward entity, maps entity_id onto
 * mironsoft_loyalty_reward_entity and its five attribute value tables.
 */
class Reward extends AbstractEntity
{
    /**
     * Sets the entity type and binds the default read/write connection.
     *
     * @return void
     */
    protected function _construct(): void
    {
        $this->setType(RewardModel::ENTITY);
        $this->setConnection(ResourceConnection::DEFAULT_CONNECTION);
    }

    /**
     * Declares the columns of the main entity table that are stored directly on
     * mironsoft_loyalty_reward_entity, NOT as EAV attribute values.
     *
     * @return array<int, string>
     */
    protected function _getDefaultAttributes(): array
    {
        return array_merge(parent::_getDefaultAttributes(), [
            'entity_id',
            'attribute_set_id',
            'type_id',
            'identifier',
            'created_at',
            'updated_at',
        ]);
    }
}

Was setConnection() und _getDefaultAttributes() genau tun

  • setType() teilt AbstractEntity mit, welcher Eintrag aus eav_entity_type (Kapitel 13) zu diesem ResourceModel gehört - darüber findet es Haupttabelle, Attribut-Set und alle registrierten Attribute.
  • setConnection(ResourceConnection::DEFAULT_CONNECTION) nutzt dieselbe Standardverbindung für Lesen und Schreiben; große EAV-Entities wie catalog_product trennen hier catalog_read/catalog_write, was für Rewards nicht nötig ist.
  • _getDefaultAttributes() teilt der Basisklasse mit, welche Spalten nicht über eav_attribute aufgelöst werden, sondern direkt aus der Haupttabelle kommen - ohne diese Liste würde AbstractEntity versuchen, auch für identifier eine EAV-Wertetabelle anzusprechen.

Achtung: Eine vergessene Spalte in _getDefaultAttributes() fällt nicht beim Speichern auf, sondern erst beim Lesen: AbstractEntity versucht dann, einen nicht existierenden EAV-Attribut-Eintrag für diese Spalte zu laden und liefert einfach null zurück - ein stiller Bug, kein Fehler.

Warum hier noch kein Repository?

Anders als beim Punkte-Ledger in Kapitel 6 führt dieser Block bewusst noch kein RewardRepositoryInterface und kein Api\Data\RewardInterface ein. Das folgt konkret in Block 10, sobald die Prämie tatsächlich über REST und GraphQL angesprochen wird und ein Service Contract einen echten Zweck erfüllt (Kapitel 79 baut darauf explizit auf). Bis dahin genügt der direkte Zugriff über Model, ResourceModel und - ab Kapitel 15 - die Collection, wie es auch zahlreiche rein admin-seitige EAV-Grids in Magento selbst tun.

Model und ResourceModel allein reichen bereits, um eine einzelne Prämie zu laden und zu speichern: $rewardResource->load($reward, $entityId) und $rewardResource->save($reward). Kapitel 13 sorgt zunächst dafür, dass es überhaupt Attribute gibt, die dabei geladen werden können.