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

Erweiterbarkeit: wie andere Entwickler das Modul erweitern können

Erweiterbarkeit: wie andere Entwickler das Modul erweitern können

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

Ein Modul, das nur unter größtem Aufwand erweiterbar ist, hat sein Ziel als wiederverwendbare Grundlage verfehlt - unabhängig davon, wie sauber sein eigener Code aussieht. Dieses Kapitel dreht die Perspektive um: nicht mehr "wie baut man dieses Modul", sondern "wie baut ein anderes Team eine eigene Erweiterung darauf, ohne Mironsoft_Loyalty selbst anzufassen".

Die Service Contracts als Erweiterungsfläche

Weil praktisch jede Kernaktion über ein Interface läuft - PointsLedgerRepositoryInterface (Kapitel 6), RewardRepositoryInterface (Kapitel 79), RewardRedemptionManagementInterface (Kapitel 81) - kann ein fremdes Modul an jeder dieser Stellen plugin-fähig andocken, genau wie die drei Lern-Plugins aus Kapitel 38 es an getListByCustomerId() vorführen. Keine dieser Kern-Klassen ist final markiert (im Gegensatz zur fiktiven ErpSync\CorporateRewards\Model\MultiplierResolver aus Kapitel 41, die absichtlich als Gegenbeispiel diente) - Drittanbieter-Code darf sich frei mit Plugins einklinken.

declare(strict_types=1);

namespace Vendor\LoyaltyReviews\Plugin;

use Mironsoft\Loyalty\Api\PointsLedgerRepositoryInterface;
use Mironsoft\Loyalty\Api\Data\PointsLedgerInterface;

/**
 * Extends the loyalty ledger read path with a review-bonus hint - a purely
 * illustrative third-party plugin, no code change inside Mironsoft_Loyalty itself.
 */
final class HighlightReviewBonusPlugin
{
    /**
     * Adds a human-readable marker to review-bonus ledger entries after they are
     * loaded, without touching the underlying repository implementation.
     *
     * @param PointsLedgerRepositoryInterface $subject   The intercepted repository.
     * @param PointsLedgerInterface[]         $result    The original ledger entries.
     * @param int                              $customerId The customer whose ledger was requested.
     * @return PointsLedgerInterface[] The (unmodified) list, side effects only.
     */
    public function afterGetListByCustomerId(
        PointsLedgerRepositoryInterface $subject,
        array $result,
        int $customerId
    ): array {
        return $result;
    }
}

Das eigene Custom Event als Erweiterungspunkt

LoyaltyTierBackend::EVENT_TIER_CHANGED (Kapitel 35) ist der einzige selbst dispatchte Event dieses Moduls - und genau deshalb der kanonische Erweiterungspunkt für alles, was auf eine Tier-Änderung reagieren soll, ohne die Backend-Klasse selbst zu ändern. Ein fiktives Marketing-Modul könnte darauf zum Beispiel eine Glückwunsch-E-Mail auslösen:

declare(strict_types=1);

namespace Vendor\LoyaltyMarketing\Observer;

use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;
use Psr\Log\LoggerInterface;

/**
 * Reacts to the loyalty module's own tier-change event to trigger a marketing
 * action - registered externally in etc/events.xml on
 * "mironsoft_loyalty_customer_tier_changed" (chapter 35), no change to
 * Mironsoft_Loyalty required.
 */
final class SendTierUpgradeCongratulationsObserver implements ObserverInterface
{
    /**
     * Initializes the observer with a logger dependency.
     *
     * @param LoggerInterface $logger Logs the congratulations trigger for auditing.
     */
    public function __construct(
        private readonly LoggerInterface $logger,
    ) {
    }

    /**
     * Reads the event data payload (customer, previous_tier, new_tier, chapter 35)
     * and triggers the actual marketing action.
     *
     * @param EventObserver $observer The dispatched event wrapper.
     * @return void
     */
    public function execute(EventObserver $observer): void
    {
        $newTier = (string) $observer->getEvent()->getData('new_tier');
        $this->logger->info("Customer upgraded to tier: {$newTier}");
    }
}
app/code/Vendor/LoyaltyMarketing/etc/events.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="mironsoft_loyalty_customer_tier_changed">
        <observer name="vendor_loyalty_marketing_send_tier_upgrade_congratulations"
                  instance="Vendor\LoyaltyMarketing\Observer\SendTierUpgradeCongratulationsObserver"/>
    </event>
</config>

Bewährte Registrierungsmuster zum Nachbauen

Drei Registrierungs-Muster aus dieser Serie eignen sich unverändert als Vorlage für eine eigene, unabhängige "Sub-Erweiterung" desselben Programms - ohne irgendeine Kernklasse zu ändern:

  • product_types.xml (Kapitel 72): Muster für einen weiteren, ganz eigenen Produkttyp - etwa ein "Punkte-Gutschein"-Produkttyp eines Drittanbieters, analog zu PointsPackage aufgebaut.
  • widget.xml (Kapitel 56): Muster für ein weiteres CMS-Widget, das genauso wie PointsBalanceWidget ein bestehendes ViewModel wiederverwendet, statt eigene Geschäftslogik zu duplizieren.
  • Zahlungsart-Registrierung über payment/*/model (Kapitel 62): Muster für eine weitere, eigene Zahlungsart, die sich - wie die Punkte-Zahlungsart selbst - in denselben Checkout einreiht, ohne den Core-Checkout zu verändern.

Die Preference-Ausnahme verstehen, nicht nachahmen

CompanyMultiplierPreference (Kapitel 41) zeigt bewusst den Sonderfall, in dem eine Erweiterung keine Plugin-Option hat - weil das fiktive Ziel ErpSync\CorporateRewards\Model\MultiplierResolver selbst final ist. Kein einziges Ziel innerhalb von Mironsoft_Loyalty selbst ist so markiert (mit Ausnahme der zustandslosen Utility-Klasse PointsFormatter, Kapitel 44, die aus genau diesem Grund keine Instanzmethoden zum Überschreiben anbietet) - Drittanbieter-Code sollte deshalb für praktisch jede Erweiterung dieses Moduls mit einem Plugin auskommen, nicht mit einer Preference.

Tipp: Der neue Configuration Type aus Kapitel 88 ist ebenfalls ein Erweiterungspunkt: jedes fremde Modul kann eigene Werte über dasselbe app/etc/loyalty_flags.php-Muster registrieren, solange es denselben ConfigTypeInterface-Vertrag respektiert - ganz ohne Datenbankzugriff, rein deploy-gesteuert.

So viel Erweiterbarkeit hilft wenig, wenn im Ernstfall niemand weiß, wo im System etwas kaputtgeht - Kapitel 103 liefert genau dafür den Troubleshooting-Leitfaden für das Gesamtprojekt.