Modul-Grundgerüst: registration.php, module.xml und Namespace-Konventionen
Modul-Grundgerüst: registration.php, module.xml und Namespace-Konventionen
~6 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026
Bevor Kapitel 3 die erste Tabelle entwirft, braucht es das Modul selbst: einen registrierten Namespace, den Magento beim Start erkennt. Dieses Kapitel legt Mironsoft\Loyalty an und erklärt die Namenskonventionen, an die sich alle folgenden 105 Kapitel halten.
Namespace und Modulname
Der PHP-Namespace ist Mironsoft\Loyalty, der Magento-Modulname (mit Unterstrich statt Backslash, wie in module.xml und registration.php verlangt) ist Mironsoft_Loyalty. Der Modulordner liegt unter app/code/Mironsoft/Loyalty/ - Vendor- und Modulname bilden direkt die ersten beiden Verzeichnisebenen, PSR-4-konform.
Storefront-URLs bekommen später (Block 6) einen eigenen Front Name: treuepraemien auf der deutschen und rewards auf der englischen Store-View. Das ist an dieser Stelle nur eine Randnotiz - der Router selbst entsteht erst in Kapitel 46, sobald es tatsächlich Controller gibt, die er ansteuern kann.
Zielstruktur nach Block 1
Die folgende Übersicht zeigt, wie das Modul aussieht, sobald alle neun Kapitel dieses Blocks abgeschlossen sind. Die einzelnen Dateien entstehen nach und nach in den Kapiteln 3 bis 9 - spätere Blöcke ergänzen weitere Verzeichnisse (Model/Reward/, Observer/, Cron/, Controller/ und so weiter), ohne etwas aus Block 1 zu verändern.
Mironsoft\Loyalty nach Block 1 (Kapitel 1-9)
app/code/Mironsoft/Loyalty/
├── registration.php
├── composer.json
├── etc/
│ ├── module.xml
│ ├── di.xml
│ ├── acl.xml
│ ├── cache.xml
│ ├── config.xml
│ └── adminhtml/
│ └── system.xml
├── Api/
│ ├── PointsLedgerRepositoryInterface.php
│ └── Data/
│ └── PointsLedgerInterface.php
├── Model/
│ ├── PointsLedger.php
│ ├── PointsLedgerRepository.php
│ ├── Cache/
│ │ └── Type/
│ │ └── LoyaltyCatalog.php
│ ├── Config/
│ │ └── LoyaltyConfig.php
│ ├── ResourceModel/
│ │ ├── PointsLedger.php
│ │ └── PointsLedger/
│ │ └── Collection.php
│ └── Service/
│ └── PointsCalculator.php
└── Console/
└── Command/
└── RecalculatePointsCommand.phpregistration.php
Jedes Magento-2-Modul registriert sich selbst über ComponentRegistrar::register() - ohne diese Datei bleibt das Modul für Magento unsichtbar, egal was sonst im Ordner liegt.
<?php
declare(strict_types=1);
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'Mironsoft_Loyalty',
__DIR__
);module.xml mit sequence
module.xml deklariert den Modulnamen und - wichtig für Kapitel 3 - eine <sequence>. Der Punkte-Ledger referenziert per Fremdschlüssel Tabellen aus Magento_Customer und Magento_Sales; damit setup:upgrade diese Tabellen garantiert schon angelegt hat, bevor db_schema.xml darauf verweist, müssen beide Module in der sequence stehen.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Mironsoft_Loyalty">
<sequence>
<module name="Magento_Customer"/>
<module name="Magento_Sales"/>
</sequence>
</module>
</config>Achtung: Eine fehlende sequence fällt nicht sofort auf: Auf einem Entwicklungssystem, auf dem Magento_Customer und Magento_Sales ohnehin schon installiert sind, funktioniert alles. Bei einer frischen Installation kann Magento die Modul-Reihenfolge dagegen anders wählen und db_schema.xml versucht, einen Fremdschlüssel auf eine noch nicht existierende Tabelle zu legen - setup:upgrade bricht dann mit einem SQL-Fehler ab.
Namenskonventionen für die restliche Serie
- PHP-Klassen: immer
declare(strict_types=1), Constructor Property Promotion für Dependency Injection, vollständiges PHPDoc auf jeder Klasse und jeder Methode. - Datenbanktabellen:
mironsoft_loyalty_<entität>, zum Beispielmironsoft_loyalty_points_ledger(Kapitel 3). - Konfigurationspfade:
mironsoft_loyalty/<gruppe>/<feld>, zum Beispielmironsoft_loyalty/general/points_per_euro(Kapitel 7). - ACL-Ressourcen:
Mironsoft_Loyalty::<bereich>, konsistent mit den anderen Mironsoft-Modulen dieses Projekts. addFieldToFilter()mit einem Integer-Wert wird immer als['eq' => $value]geschrieben, nie als nackter Skalar - Kapitel 4 zeigt das an der ersten Collection.
Tipp: Diese Konventionen stammen direkt aus der Projekt-CLAUDE.md - sie gelten identisch für alle zwölf Blöcke dieser Serie, nicht nur für Block 1.
Mit registriertem Modul und korrekter sequence kann Kapitel 3 die erste Tabelle entwerfen.