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

Widget Parameters and Admin Configuration (widget.xml)

Widget Parameters and Admin Configuration (widget.xml)

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

The widget from chapter 56 works, but it's hard-wired: the tier badge is always on, with no way for an editor to adapt it to a given page's design. This chapter makes exactly that configurable in the admin.

Parameter types at a glance

  • text / textarea - free text input, e.g. for additional CSS classes.
  • select / multiselect - dropdown with fixed <option> values, typically also used for yes/no toggles (no dedicated boolean type exists).
  • block - a chooser dialog letting an editor pick an existing CMS block instead of typing an ID by hand.
  • url - link chooser, identical to the URL field in Page Builder.
  • date - date picker, e.g. for time-limited promotions.

widget.xml with two real parameters

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>

The widget class reads the parameters

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: From here on, $this->setTemplate() points at a NEW file, widget/points-balance-widget.phtml, instead of continuing to use widget/points-balance.phtml from chapter 53. Reason: Magento\Framework\View\Element\AbstractBlock extends \Magento\Framework\DataObject and inherits the same __call() magic chapter 49 already explained for AbstractModel::getData() - the generic Template block from chapters 51/52 would therefore also accept $block->getShowTierBadge() without error, just always returning null instead of a real default. Letting one and the same template file behave differently and silently across two contexts is exactly the kind of unnecessary coupling chapter 49 already warned about - hence a clean split as soon as the template contracts genuinely diverge.

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 configuration in practice

  1. Content > Elements > Widgets > "Add Widget" - pick the "Loyalty: My Points Balance" widget type.
  2. Set the design theme and store view assignment (the standard widget instance fields, the same for every widget).
  3. "Widget Options" tab: show_tier_badge and css_class show up automatically as form fields - generated directly from widget.xml, no admin form of our own needed.
  4. "Layout Updates" tab: pick the page or container where the widget should appear - internally generates the layout XML update from chapter 55.

Alternatively: click "Insert Widget" directly in any CMS page's WYSIWYG editor - the same parameter dialog appears as a modal there, and the result lands as a {{widget type="Mironsoft\Loyalty\Model\Widget\PointsBalanceWidget" show_tier_badge="1"}} directive right in the page content, with no saved widget instance at all.

Tipp: After every widget.xml change: bin/cache-clean config. After every template change, bin/cache-clean block_html is enough - both cache types independent of each other, same as in previous blocks of this series.

Achtung: css_class and show_tier_badge are purely presentational - they change nothing about the full page cache danger described in chapter 56 for customer-specific widgets on public pages. That warning applies unchanged no matter which parameters are set in the admin.