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

Widget-Parameter und Admin-Konfiguration (widget.xml)

Widget-Parameter und Admin-Konfiguration (widget.xml)

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

Das Widget aus Kapitel 56 funktioniert, ist aber fest verdrahtet: Tier-Badge immer an, keine Möglichkeit für eine Redakteurin, es an das Design der jeweiligen Seite anzupassen. Dieses Kapitel macht genau das im Admin konfigurierbar.

Parameter-Typen im Überblick

  • text / textarea - freies Texteingabefeld, z. B. für zusätzliche CSS-Klassen.
  • select / multiselect - Dropdown mit fest definierten <option>-Werten, typischerweise auch für Ja/Nein-Schalter genutzt (kein eigener boolean-Typ nötig).
  • block - ein Auswahldialog ("Chooser"), mit dem eine Redakteurin einen bestehenden CMS-Block auswählt, statt eine ID von Hand zu tippen.
  • url - Link-Auswahldialog, identisch zum URL-Feld in Page Builder.
  • date - Datepicker, z. B. für zeitlich befristete Werbeaktionen.

widget.xml mit zwei echten Parametern

app/code/Mironsoft/Loyalty/etc/widget.xml
<?xml version="1.0"?>
<widgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Widget:etc/widget.xsd">
    <widget id="mironsoft_loyalty_points_balance"
            class="Mironsoft\Loyalty\Model\Widget\PointsBalanceWidget"
            is_email_compatible="false">
        <label translate="true">Loyalty: My Points Balance</label>
        <description translate="true">Displays the logged-in customer's points balance and loyalty tier.</description>
        <parameters>
            <parameter name="show_tier_badge" xsi:type="select" visible="true" required="false" sort_order="10">
                <label translate="true">Show Loyalty Tier Badge</label>
                <options>
                    <option name="yes" value="1" selected="true">
                        <label translate="true">Yes</label>
                    </option>
                    <option name="no" value="0">
                        <label translate="true">No</label>
                    </option>
                </options>
            </parameter>
            <parameter name="css_class" xsi:type="text" visible="true" required="false" sort_order="20">
                <label translate="true">Additional CSS Classes</label>
            </parameter>
        </parameters>
    </widget>
</widgets>

Die Widget-Klasse liest die Parameter

app/code/Mironsoft/Loyalty/Model/Widget/PointsBalanceWidget.php
<?php

declare(strict_types=1);

namespace Mironsoft\Loyalty\Model\Widget;

use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;
use Magento\Widget\Block\BlockInterface;
use Mironsoft\Loyalty\ViewModel\PointsBalance;

/**
 * Renders the "My Points" widget insertable into any CMS WYSIWYG content. Delegates all
 * business logic to ViewModel\PointsBalance (chapter 48); the two getters added in this
 * chapter (getShowTierBadge()/getCssClass()) expose only admin-configured presentation
 * parameters that have no equivalent on the view model itself.
 */
class PointsBalanceWidget extends Template implements BlockInterface
{
    /**
     * @param Context $context Framework template context, required by the parent Template class.
     * @param PointsBalance $pointsBalanceViewModel Same view model already used in chapters 48/51/52 - reused unmodified here.
     * @param array<string, mixed> $data Additional block data, populated by Magento's widget system from the admin-configured parameters below.
     */
    public function __construct(
        Context $context,
        private readonly PointsBalance $pointsBalanceViewModel,
        array $data = [],
    ) {
        parent::__construct($context, $data);
        $this->setTemplate('Mironsoft_Loyalty::widget/points-balance-widget.phtml');
    }

    /**
     * Returns the injected view model.
     *
     * @return PointsBalance
     */
    public function getViewModel(): PointsBalance
    {
        return $this->pointsBalanceViewModel;
    }

    /**
     * Whether the tier badge should render, defaulting to true when the admin left the
     * parameter unset (e.g. a widget instance saved before this parameter existed).
     *
     * @return bool
     */
    public function getShowTierBadge(): bool
    {
        $value = $this->getData('show_tier_badge');

        return $value === null || (bool) $value;
    }

    /**
     * Returns any additional CSS classes configured in the admin, empty string if none.
     *
     * @return string
     */
    public function getCssClass(): string
    {
        return (string) ($this->getData('css_class') ?? '');
    }
}

Achtung: Ab hier zeigt $this->setTemplate() auf eine NEUE Datei, widget/points-balance-widget.phtml, statt weiter widget/points-balance.phtml aus Kapitel 53 zu nutzen. Grund: Magento\Framework\View\Element\AbstractBlock erbt von \Magento\Framework\DataObject und damit dieselbe __call()-Magie, die Kapitel 49 bereits bei AbstractModel::getData() erklärt hat - auch der generische Template-Block aus Kapitel 51/52 würde also $block->getShowTierBadge() klaglos entgegennehmen, nur eben immer mit null statt echtem Default. Ein und dieselbe Template-Datei über zwei Kontexte hinweg unterschiedliches, stillschweigendes Verhalten zeigen zu lassen wäre genau die Art unnötiger Kopplung, vor der Kapitel 49 bereits gewarnt hat - deshalb sauber getrennt, sobald die Template-Verträge tatsächlich auseinanderlaufen.

app/code/Mironsoft/Loyalty/view/frontend/templates/widget/points-balance-widget.phtml
<?php

declare(strict_types=1);

use Magento\Framework\Escaper;
use Mironsoft\Loyalty\Model\Widget\PointsBalanceWidget;
use Mironsoft\Loyalty\ViewModel\PointsBalance;

/**
 * @var PointsBalanceWidget $block
 * @var Escaper $escaper
 */
/** @var PointsBalance $viewModel */
$viewModel = $block->getViewModel();
?>
<?php if ($viewModel->canShow()): ?>
    <div class="my-4 rounded-xl border border-gray-200 bg-white p-4 shadow-sm sm:p-6 <?= $escaper->escapeHtmlAttr($block->getCssClass()) ?>">
        <dl class="flex flex-col gap-1 sm:flex-row sm:items-baseline sm:justify-between">
            <div>
                <dt class="text-sm font-medium text-gray-500">
                    <?= $escaper->escapeHtml(__('Your points balance')) ?>
                </dt>
                <dd class="text-3xl font-bold tracking-tight text-gray-900">
                    <?= $escaper->escapeHtml($viewModel->getFormattedPointsBalance()) ?>
                </dd>
            </div>
            <?php if ($block->getShowTierBadge()): ?>
                <span class="inline-flex w-fit items-center rounded-full bg-amber-100 px-3 py-1 text-sm font-semibold capitalize text-amber-800">
                    <?= $escaper->escapeHtml($viewModel->getTier()) ?>
                </span>
            <?php endif; ?>
        </dl>
    </div>
<?php endif; ?>

Admin-Konfiguration in der Praxis

  1. Content > Elements > Widgets > "Add Widget" - Widget-Typ "Loyalty: My Points Balance" wählen.
  2. Design-Theme und Store-View-Zuordnung festlegen (die Standard-Widget-Instance-Felder, für jedes Widget gleich).
  3. Tab "Widget Options": show_tier_badge und css_class erscheinen automatisch als Formularfelder - generiert direkt aus widget.xml, kein eigenes Admin-Formular nötig.
  4. Tab "Layout Updates": Seite oder Container festlegen, wo das Widget erscheinen soll - erzeugt intern die Layout-XML-Aktualisierung aus Kapitel 55.

Alternativ: direkt im WYSIWYG-Editor einer beliebigen CMS-Seite auf "Widget einfügen" klicken - derselbe Parameter-Dialog erscheint dort als Modal, das Ergebnis landet als {{widget type="Mironsoft\Loyalty\Model\Widget\PointsBalanceWidget" show_tier_badge="1"}}-Direktive direkt im Seiteninhalt, ganz ohne gespeicherte Widget Instance.

Tipp: Nach jeder widget.xml-Änderung: bin/cache-clean config. Nach jeder Template-Änderung reicht bin/cache-clean block_html - beide Cache-Typen unabhängig voneinander, wie schon in den vorherigen Blöcken dieser Serie.

Achtung: css_class und show_tier_badge sind rein präsentational - sie ändern nichts an der in Kapitel 56 beschriebenen Full-Page-Cache-Gefahr bei kundenspezifischen Widgets auf öffentlichen Seiten. Diese Warnung gilt unverändert, egal welche Parameter im Admin gesetzt sind.