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

Die Reward-Collection mit EAV-Joins richtig filtern

Die Reward-Collection mit EAV-Joins richtig filtern

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

Der Prämienkatalog aus Kapitel 8 (Cache-Typ) und Kapitel 1 (Business-Szenario) braucht mehr als das Laden einer einzelnen Prämie: eine Liste aller aktiven Prämien, gefiltert nach Punktepreis oder Typ. Dafür baut dieses Kapitel die Collection-Klasse - anders als bei der flachen Ledger-Collection aus Kapitel 4 auf Basis von Magento\Eav\Model\Entity\Collection\AbstractCollection, die eingebaute Unterstützung für EAV-Joins mitbringt.

Die Collection-Klasse

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

declare(strict_types=1);

namespace Mironsoft\Loyalty\Model\ResourceModel\Reward;

use Magento\Eav\Model\Entity\Collection\AbstractCollection;
use Mironsoft\Loyalty\Model\Reward as RewardModel;
use Mironsoft\Loyalty\Model\ResourceModel\Reward as RewardResource;

/**
 * Collection of reward entities, backed by the mironsoft_loyalty_reward EAV entity type.
 */
class Collection extends AbstractCollection
{
    /**
     * Binds the collection to its model and resource model pair.
     *
     * @return void
     */
    protected function _construct(): void
    {
        $this->_init(RewardModel::class, RewardResource::class);
    }

    /**
     * Restricts the collection to active rewards.
     *
     * @return $this
     */
    public function addActiveFilter(): self
    {
        $this->addAttributeToFilter('is_active', ['eq' => 1]);

        return $this;
    }

    /**
     * Restricts the collection to a single reward type.
     *
     * @param string $rewardType One of the RewardType::TYPE_* constants.
     * @return $this
     */
    public function addRewardTypeFilter(string $rewardType): self
    {
        $this->addAttributeToFilter('reward_type', ['eq' => $rewardType]);

        return $this;
    }

    /**
     * Restricts the collection to rewards affordable with at most the given points.
     *
     * @param int $maxPointsCost Maximum points cost a reward may have.
     * @return $this
     */
    public function addMaxPointsCostFilter(int $maxPointsCost): self
    {
        $this->addAttributeToFilter('points_cost', ['lteq' => $maxPointsCost]);

        return $this;
    }
}

addAttributeToFilter() statt addFieldToFilter()

Die auffälligste Änderung gegenüber Kapitel 4: addAttributeToFilter() statt addFieldToFilter(). Beide erwarten dieselbe Array-Form für Operatoren (['eq' => ...], ['lteq' => ...] und so weiter - CLAUDE.mds Vorgabe für addFieldToFilter() gilt inhaltlich identisch auch hier), aber intern passiert deutlich mehr: addAttributeToFilter() schlägt zuerst das Attribut in eav_attribute nach, ermittelt daraus den passenden backend_type und fügt dann einen JOIN auf genau die richtige Wertetabelle ein - für is_active auf mironsoft_loyalty_reward_entity_int, für reward_type auf mironsoft_loyalty_reward_entity_varchar.

AbstractCollection stellt aus Kompatibilitätsgründen auch addFieldToFilter() bereit - als direkten Alias auf addAttributeToFilter(). Beide Namen funktionieren technisch identisch; diese Serie verwendet für EAV-Collections konsequent addAttributeToFilter(), weil der Name ehrlicher beschreibt, was tatsächlich passiert.

addAttributeToSelect() und die Kosten jedes zusätzlichen Joins

Standardmäßig lädt eine frisch erzeugte Collection nur die Spalten der Haupttabelle - keine einzige EAV-Wertetabelle wird ohne explizite Anforderung gejoint. addAttributeToSelect() fordert die tatsächlich benötigten Attribute an, jedes zusätzliche Attribut bedeutet einen zusätzlichen JOIN in der resultierenden SQL-Abfrage.

// Nur die drei für eine Katalog-Kachel benötigten Attribute - drei zusätzliche JOINs.
$collection = $this->rewardCollectionFactory->create();
$collection->addAttributeToSelect(['title', 'points_cost', 'reward_type']);
$collection->addActiveFilter();

// Alle sechs Attribute - fünf zusätzliche JOINs, auch wenn description
// auf der Katalogseite gar nicht angezeigt wird.
$collection->addAttributeToSelect('*');

Achtung: addAttributeToSelect('*') ist bequem, aber teuer: bei sechs Attributen über vier verschiedene Wertetabellen bedeutet das bis zu fünf zusätzliche JOINs pro Abfrage (die Haupttabelle selbst zählt nicht mit), unabhängig davon, ob die Attribute im konkreten Anwendungsfall überhaupt gebraucht werden. Kapitel 18 zeigt, ab welcher Zeilenzahl dieser Unterschied messbar wird.

Zusammenspiel mit dem Cache-Typ aus Kapitel 8

Genau diese Collection ist es, die LoyaltyCatalog aus Kapitel 8 cachen soll: eine mit addActiveFilter() und addAttributeToSelect() auf die im Katalog sichtbaren Felder eingeschränkte Abfrage, deren Ergebnis sich nicht bei jedem Seitenaufruf neu durch fünf JOINs rechnen muss.

Mit einer filterbaren Collection existiert jetzt alles, was ein Admin-Grid braucht, um Prämien anzuzeigen - Kapitel 16 baut genau dieses Grid, mit einem kurzen Rückbezug auf die dedizierte Admin-Grids-Serie dieses Katalogs.