System/Config/Setting: eine eigene Konfigurationsseite für das Treuepunkte-Programm
System/Config/Setting: eine eigene Konfigurationsseite für das Treuepunkte-Programm
~7 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026
Vier Werte aus der Spezifikation - aktiviert oder nicht, Punkte pro Euro, Ablaufzeit in Monaten, Tier-Schwellenwerte - brauchen eine Oberfläche, über die ein Shop-Betreiber sie ohne Codeänderung anpassen kann. Magentos System/Config-Framework deklariert das komplett über XML: system.xml für die Formularstruktur, config.xml für Standardwerte, acl.xml für die Berechtigung.
acl.xml: Berechtigungen für Menü und Konfiguration
Zwei ACL-Ressourcen werden gebraucht: Mironsoft_Loyalty::loyalty als übergeordnete Ressource, unter der spätere Blöcke ihre eigenen Admin-Berechtigungen einhängen (zum Beispiel das Prämien-Grid aus Kapitel 16), und Mironsoft_Loyalty::config_section speziell für diese Konfigurationsseite unter Stores > Configuration.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Backend::admin">
<resource id="Mironsoft_Loyalty::loyalty" title="Loyalty & Rewards" sortOrder="50"/>
</resource>
<resource id="Magento_Backend::stores">
<resource id="Magento_Backend::stores_settings">
<resource id="Magento_Config::config">
<resource id="Mironsoft_Loyalty::config_section"
title="Loyalty & Rewards Configuration"/>
</resource>
</resource>
</resource>
</resources>
</acl>
</config>etc/adminhtml/system.xml
Jedes Feld bekommt showInDefault/showInWebsite/showInStore entsprechend der in der Spezifikation festgelegten Scope: enabled und points_per_euro sind auf Website-Ebene einstellbar, points_expiry_months und tier_thresholds bewusst nur auf Default-Ebene - eine unterschiedliche Ablaufzeit pro Website würde die Ledger-Auswertung in Kapitel 33 unnötig verkomplizieren.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="mironsoft" translate="label" sortOrder="300">
<label>Mironsoft</label>
</tab>
<section id="mironsoft_loyalty" translate="label" sortOrder="200"
showInDefault="1" showInWebsite="1" showInStore="0">
<tab>mironsoft</tab>
<label>Loyalty & Rewards</label>
<resource>Mironsoft_Loyalty::config_section</resource>
<group id="general" translate="label" sortOrder="10"
showInDefault="1" showInWebsite="1" showInStore="0">
<label>General Settings</label>
<field id="enabled" translate="label" type="select" sortOrder="10"
showInDefault="1" showInWebsite="1" showInStore="0">
<label>Enable Loyalty Program</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="points_per_euro" translate="label" type="text" sortOrder="20"
showInDefault="1" showInWebsite="1" showInStore="0">
<label>Points per Euro Spent</label>
<validate>validate-number validate-greater-than-zero</validate>
<depends>
<field id="enabled">1</field>
</depends>
</field>
<field id="points_expiry_months" translate="label" type="text" sortOrder="30"
showInDefault="1" showInWebsite="0" showInStore="0">
<label>Points Expiry (Months)</label>
<validate>validate-digits validate-greater-than-zero</validate>
</field>
<field id="tier_thresholds" translate="label,comment" type="textarea" sortOrder="40"
showInDefault="1" showInWebsite="0" showInStore="0">
<label>Tier Thresholds (JSON)</label>
<comment>JSON object with the point totals required to reach the
"silver" and "gold" tier, e.g. {"silver":500,"gold":2000}.</comment>
</field>
</group>
</section>
</system>
</config>config.xml: Standardwerte
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<mironsoft_loyalty>
<general>
<enabled>0</enabled>
<points_per_euro>1.0000</points_per_euro>
<points_expiry_months>12</points_expiry_months>
<tier_thresholds>{"silver":500,"gold":2000}</tier_thresholds>
</general>
</mironsoft_loyalty>
</default>
</config>Achtung: config.xml setzt einen sicheren Standard: das Programm ist enabled = 0, solange niemand es aktiv einschaltet. Ein Modul, das sich standardmäßig selbst aktiviert, überrascht Shop-Betreiber mit unerwartetem Verhalten direkt nach der Installation - ein häufiger, vermeidbarer Fehler bei eigenen Modulen.
Ein typsicherer Config-Reader statt roher ScopeConfigInterface-Aufrufe
ScopeConfigInterface::getValue() direkt in Observern, Controllern oder dem Konsolenbefehl zu verstreuen, bedeutet, den Config-Pfad als Zeichenkette an mehreren Stellen zu wiederholen - ein Tippfehler fällt dann erst zur Laufzeit auf. LoyaltyConfig kapselt alle vier Pfade an einer einzigen Stelle.
<?php
declare(strict_types=1);
namespace Mironsoft\Loyalty\Model\Config;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\ScopeInterface;
/**
* Typed reader for the mironsoft_loyalty/* configuration values.
*/
class LoyaltyConfig
{
private const XML_PATH_ENABLED = 'mironsoft_loyalty/general/enabled';
private const XML_PATH_POINTS_PER_EURO = 'mironsoft_loyalty/general/points_per_euro';
private const XML_PATH_POINTS_EXPIRY_MONTHS = 'mironsoft_loyalty/general/points_expiry_months';
private const XML_PATH_TIER_THRESHOLDS = 'mironsoft_loyalty/general/tier_thresholds';
/**
* @param ScopeConfigInterface $scopeConfig Reads store configuration values.
*/
public function __construct(
private readonly ScopeConfigInterface $scopeConfig,
) {
}
/**
* @param int|null $websiteId Website scope, null falls back to the default scope.
* @return bool
*/
public function isEnabled(?int $websiteId = null): bool
{
return $this->scopeConfig->isSetFlag(
self::XML_PATH_ENABLED,
ScopeInterface::SCOPE_WEBSITE,
$websiteId
);
}
/**
* @param int|null $websiteId Website scope, null falls back to the default scope.
* @return float
*/
public function getPointsPerEuro(?int $websiteId = null): float
{
return (float) $this->scopeConfig->getValue(
self::XML_PATH_POINTS_PER_EURO,
ScopeInterface::SCOPE_WEBSITE,
$websiteId
);
}
/**
* @return int
*/
public function getPointsExpiryMonths(): int
{
return (int) $this->scopeConfig->getValue(self::XML_PATH_POINTS_EXPIRY_MONTHS);
}
/**
* @return string
*/
public function getTierThresholdsJson(): string
{
return (string) $this->scopeConfig->getValue(self::XML_PATH_TIER_THRESHOLDS);
}
}Tipp: getTierThresholdsJson() liefert das JSON unverändert als Zeichenkette zurück und überlässt das Decodieren PointsCalculator::determineTier() (Kapitel 5) - LoyaltyConfig kennt bewusst keine Geschäftslogik, nur Konfigurationswerte. Diese Trennung macht beide Klassen unabhängig voneinander testbar.
Mit Konfiguration und ACL an Ort und Stelle folgt in Kapitel 8 der letzte Infrastruktur-Baustein von Block 1: ein eigener Cache-Typ.