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

Eigene Felder an Produkt, Kategorie oder Kunde anhängen

Eigene Felder an Produkt, Kategorie oder Kunde anhängen

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

Kapitel 8 hat das Muster an einem Produktbeispiel eingeführt. Dieses Kapitel zeigt drei konkrete, in der Praxis besonders häufige Erweiterungspunkte im Detail - inklusive der jeweiligen Stolperfallen bei $value.

Kategorie: eigenes Banner-Feld

app/code/Mironsoft/GraphqlDemo/etc/schema.graphqls
extend type CategoryInterface {
    mironsoft_banner_image: String
        @resolver(class: "Mironsoft\\GraphqlDemo\\Model\\Resolver\\Category\\BannerImage")
        @doc(description: "URL of a custom marketing banner shown above the category grid")
}

Anders als bei Produkten heißt der Basistyp hier bewusst CategoryInterface, nicht Category - Magento nennt seine Typen nicht immer konsistent, ein Blick in die Introspection (Kapitel 3) oder in vendor/magento/module-catalog-graph-ql/etc/schema.graphqls schafft hier schnell Klarheit, bevor extend type ins Leere läuft.

app/code/Mironsoft/GraphqlDemo/Model/Resolver/Category/BannerImage.php
<?php

declare(strict_types=1);

namespace Mironsoft\GraphqlDemo\Model\Resolver\Category;

use Magento\Catalog\Model\Category;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;

/**
 * Resolves the custom mironsoft_banner_image field on categories.
 */
class BannerImage implements ResolverInterface
{
    /**
     * Reads the mironsoft_banner_image attribute off the resolved category model.
     *
     * @param Field $field Resolved GraphQL field configuration
     * @param mixed $context Resolver context
     * @param ResolveInfo $info GraphQL resolve tree info
     * @param array|null $value Parent CategoryInterface resolver's value
     * @param array|null $args Arguments passed to this field
     * @return string|null
     */
    public function resolve(
        Field $field,
        $context,
        ResolveInfo $info,
        ?array $value = null,
        ?array $args = null
    ): ?string {
        /** @var Category|null $category */
        $category = $value['model'] ?? null;

        $bannerImage = $category?->getData('mironsoft_banner_image');

        return is_string($bannerImage) && $bannerImage !== ''
            ? $bannerImage
            : null;
    }
}

Kunde: eigene Präferenz auslesen

CustomerOutput - der Rückgabetyp der customer-Query - ist ein besonders sensibler Erweiterungspunkt, weil er praktisch immer personenbezogene Daten trägt. Der Resolver bekommt hier nicht automatisch die eigene Kunden-ID mitgeliefert - er muss sie explizit aus dem Context lesen, genau wie später die geschützte Favoriten-Mutation in Kapitel 18:

app/code/Mironsoft/GraphqlDemo/etc/schema.graphqls
extend type CustomerOutput {
    mironsoft_newsletter_topic: String
        @resolver(class: "Mironsoft\\GraphqlDemo\\Model\\Resolver\\Customer\\NewsletterTopic")
        @doc(description: "The customer's preferred newsletter topic")
}

Auch hier landet in der Praxis meist ein Wert aus $value['model'] (dem geladenen \Magento\Customer\Model\Customer) im Resolver - Magentos eigener CustomerOutput-Resolver liefert das geladene Kundenmodell bereits unter diesem Schlüssel weiter.

Wann lohnt sich ein neues EAV-Attribut statt eines GraphQL-Feldes?

Bei Produkten und Kategorien liegt die Versuchung nahe, jedes neue Feld direkt als eigenes EAV-Attribut über eav_attribute anzulegen und per Admin-Formular pflegbar zu machen. Das lohnt sich, wenn der Wert tatsächlich redaktionell im Adminbereich gepflegt werden soll (siehe die Admin-Grids-&-Formulare-Serie). Für rein berechnete Werte - etwa mironsoft_badge_text aus Kapitel 8, das aus vorhandenen Daten abgeleitet wird - reicht ein reiner GraphQL-Resolver ohne eigenes Datenbank-Attribut völlig aus.

Achtung: extend type auf einem Core-Typ, der intern per Batch-Resolver arbeitet (z. B. Preisfelder bei Produkten), kann bei einem naiven, nicht-batchenden eigenen Feld schnell zum N+1-Flaschenhals werden - eine Liste von 50 Produkten löst dann 50 einzelne zusätzliche Datenbankabfragen für das eigene Feld aus. Kapitel 20 zeigt am Veranstaltungen-Projekt, wie sich das mit Batch-Resolvern vermeiden lässt - dasselbe Muster funktioniert auch für erweiterte Core-Typen.

Kapitel 10 schließt Block 3 ab und geht noch einmal grundlegend auf jeden der vier Parameter von resolve() ein - Wissen, das für die eigene Veranstaltungen-API ab Block 4 vorausgesetzt wird.