Magento 2 · Modul-Entwicklung

Eigenes Magento 2 Modul
Schritt für Schritt erstellen

Ein sauberes Magento 2 Modul besteht nicht nur aus registration.php und module.xml. Für produktiven Code gehören Konfiguration, ACL, Admin-Menüpunkt, Layout XML, ViewModel und klare Dependency Injection dazu.

14 Min. Lesezeit PHP 8.4 Magento 2.4.8

1. Ziel des Moduls

Ein Magento 2 Modul erstellen bedeutet mehr als ein paar Dateien unter app/code abzulegen. Ein Modul ist eine eigenständige Funktionseinheit mit Name, Registrierung, Konfiguration, Berechtigungen und klaren Einstiegspunkten. In diesem Tutorial bauen wir ein kleines Modul Mironsoft_HelloModule, das eine Frontend-Seite ausgibt und über den Adminbereich aktiviert oder deaktiviert werden kann.

Der Fokus liegt auf einer Struktur, die auch in echten Projekten Bestand hat. Das Modul bekommt registration.php, etc/module.xml, etc/config.xml, etc/adminhtml/system.xml, etc/acl.xml, eine eigene Frontend-Route, einen Controller, Layout XML, ein Hyvä-kompatibles Template und ein ViewModel. Genau so sollte man ein Magento 2 Modul erstellen, wenn es nicht nur als Demo, sondern als wartbare Erweiterung gedacht ist.

Wir verwenden bewusst keine Luma-spezifischen UI Components, kein Knockout.js und kein jQuery. Für Frontend-Logik wäre in Hyvä Alpine.js die passende Wahl. In diesem Beispiel reicht serverseitiges Rendering mit einem ViewModel. Das ist schnell, testbar und passt gut zu Magento 2.4.8 mit PHP 8.4.

2. Modulstruktur anlegen

Die Modulstruktur liegt bei Magento standardmäßig unter app/code/Vendor/Module. Für dieses Tutorial ist der vollständige Pfad app/code/Mironsoft/HelloModule. Der Vendor ist Mironsoft, das Modul heißt HelloModule, der vollständige Modulname lautet Mironsoft_HelloModule. Diese Schreibweise ist wichtig, weil sie in XML-Dateien, ACL-Ressourcen, Konfiguration und CLI-Ausgaben wieder auftaucht.

Wenn du ein Magento 2 Modul erstellen willst, solltest du die Verzeichnisse von Anfang an sauber trennen. Globale Konfiguration liegt in etc. Admin-spezifische Konfiguration liegt in etc/adminhtml. Frontend-Routen liegen in etc/frontend. Templates gehören in view/frontend/templates, Layout XML in view/frontend/layout. Diese Konvention macht spätere Erweiterungen nachvollziehbar.


app/code/Mironsoft/HelloModule/
├── Controller/
│   └── Index/
│       └── Index.php
├── etc/
│   ├── acl.xml
│   ├── config.xml
│   ├── module.xml
│   ├── adminhtml/
│   │   └── system.xml
│   └── frontend/
│       └── routes.xml
├── registration.php
├── ViewModel/
│   └── Hello.php
└── view/
    └── frontend/
        ├── layout/
        │   └── hellomodule_index_index.xml
        └── templates/
            └── hello.phtml

Die erste Datei ist app/code/Mironsoft/HelloModule/registration.php. Sie registriert das Modul beim Magento Component Registrar. Ohne diese Datei erkennt Magento die Erweiterung nicht.


<?php
declare(strict_types=1);

use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Mironsoft_HelloModule',
    __DIR__
);

Danach folgt app/code/Mironsoft/HelloModule/etc/module.xml. Diese Datei beschreibt das Modul für Magento. In einfachen Modulen reicht der Name. Wenn dein Modul von anderen Modulen abhängt, würdest du hier sequence ergänzen.


<?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_HelloModule"/>
</config>

Nach diesen beiden Dateien kann Magento das Modul grundsätzlich erkennen. In einer Mark-Shust-Umgebung verwendest du dafür den Wrapper, nicht direkt php bin/magento. Der typische Ablauf ist bin/magento module:status, bin/magento setup:upgrade und danach Cache leeren. In echten Projekten gehört anschließend auch Codequalität dazu: PHPStan, PHPCS und gezielte Tests.

3. Konfiguration, Systembereich und ACL

Ein produktives Magento 2 Modul sollte eigene Einstellungen enthalten. Dadurch kann der Shopbetreiber Funktionen aktivieren, Texte ändern oder Verhalten steuern, ohne Code anzufassen. Für unser Beispiel gibt es eine Einstellung enabled und einen Begrüßungstext. Die Standardwerte definieren wir in app/code/Mironsoft/HelloModule/etc/config.xml.


<?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>
        <hellomodule>
            <general>
                <enabled>1</enabled>
                <headline>Willkommen bei Mironsoft</headline>
            </general>
        </hellomodule>
    </default>
</config>

Damit die Einstellung im Adminbereich sichtbar wird, braucht das Modul app/code/Mironsoft/HelloModule/etc/adminhtml/system.xml. Hier entsteht ein eigener Konfigurationsbereich mit eigenem Menüpunkt. Genau diesen Schritt vergessen viele Entwickler, wenn sie ein Magento 2 Modul erstellen. Das Ergebnis ist dann ein Modul ohne saubere Bedienoberfläche.


<?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="900">
            <label>Mironsoft</label>
        </tab>
        <section id="hellomodule" translate="label" sortOrder="10"
                 showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Hello Module</label>
            <tab>mironsoft</tab>
            <resource>Mironsoft_HelloModule::config</resource>
            <group id="general" translate="label" sortOrder="10"
                   showInDefault="1" showInWebsite="1" showInStore="1">
                <label>General Settings</label>
                <field id="enabled" translate="label" type="select" sortOrder="10"
                       showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Enabled</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="headline" translate="label" type="text" sortOrder="20"
                       showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Headline</label>
                    <comment>Displayed on the frontend example page.</comment>
                </field>
            </group>
        </section>
    </system>
</config>

Der Eintrag resource verweist auf eine ACL-Ressource. Dafür braucht das Modul app/code/Mironsoft/HelloModule/etc/acl.xml. Ohne ACL kann Magento die Admin-Berechtigung nicht sauber prüfen. Wer ein Magento 2 Modul erstellen will, sollte ACL von Anfang an mitdenken, auch bei kleinen Modulen.


<?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="Magento_Backend::stores">
                    <resource id="Magento_Backend::stores_settings">
                        <resource id="Magento_Config::config">
                            <resource id="Mironsoft_HelloModule::config"
                                      title="Mironsoft Hello Module Configuration"
                                      sortOrder="10"/>
                        </resource>
                    </resource>
                </resource>
            </resource>
        </resources>
    </acl>
</config>

4. Route, Controller, Layout und Hyvä Template

Als nächstes bekommt das Modul eine Frontend-Seite. Die Route definieren wir in app/code/Mironsoft/HelloModule/etc/frontend/routes.xml. Der frontName bestimmt die URL. In diesem Beispiel lautet sie /hellomodule. Der Controller liegt danach unter Controller/Index/Index.php.


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="hellomodule" frontName="hellomodule">
            <module name="Mironsoft_HelloModule"/>
        </route>
    </router>
</config>

Der Controller sollte dünn bleiben. Er erzeugt nur das Page-Result und enthält keine Geschäftslogik. Das passt zu moderner Magento-Architektur: Controller orchestrieren HTTP, ViewModels liefern Daten für Templates, Services enthalten Fachlogik.


<?php
declare(strict_types=1);

namespace Mironsoft\HelloModule\Controller\Index;

use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\View\Result\PageFactory;

/**
 * Renders the hello module frontend page.
 */
final class Index implements HttpGetActionInterface
{
    public function __construct(
        private readonly PageFactory $pageFactory
    ) {}

    /**
     * Creates the page result for the hello module route.
     */
    public function execute(): ResultInterface
    {
        return $this->pageFactory->create();
    }
}

Das Layout XML verbindet die Route mit dem Template. Der Handle ergibt sich aus Route, Controller und Action: hellomodule_index_index.xml. Die Datei liegt unter app/code/Mironsoft/HelloModule/view/frontend/layout/hellomodule_index_index.xml.


<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="Magento\Framework\View\Element\Template"
                   name="mironsoft.hellomodule.page"
                   template="Mironsoft_HelloModule::hello.phtml">
                <arguments>
                    <argument name="view_model" xsi:type="object">Mironsoft\HelloModule\ViewModel\Hello</argument>
                </arguments>
            </block>
        </referenceContainer>
    </body>
</page>

5. ViewModel statt Block-Logik

In Hyvä-Projekten ist ein ViewModel meistens die bessere Wahl als eine eigene Block-Klasse. Das ViewModel implementiert ArgumentInterface und liefert typisierte Methoden für das Template. So bleibt die Darstellung schlank und die Datenlogik testbar. Wenn du ein Magento 2 Modul erstellen willst, das zu Hyvä passt, ist diese Trennung besonders wichtig.

Unser ViewModel liest die Konfiguration aus. Dafür nutzt es ScopeConfigInterface. Die Pfade definieren wir als Konstanten. So vermeidest du Magic Strings im Code und kannst die Werte später leichter wiederverwenden.


<?php
declare(strict_types=1);

namespace Mironsoft\HelloModule\ViewModel;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\View\Element\Block\ArgumentInterface;
use Magento\Store\Model\ScopeInterface;

/**
 * Provides configuration values for the hello module template.
 */
final class Hello implements ArgumentInterface
{
    /**
     * Defines the enabled configuration path.
     */
    private const string XML_PATH_ENABLED = 'hellomodule/general/enabled';

    /**
     * Defines the headline configuration path.
     */
    private const string XML_PATH_HEADLINE = 'hellomodule/general/headline';

    public function __construct(
        private readonly ScopeConfigInterface $scopeConfig
    ) {}

    /**
     * Checks whether the module output should be visible.
     */
    public function isEnabled(): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_ENABLED,
            ScopeInterface::SCOPE_STORE
        );
    }

    /**
     * Returns the configured frontend headline.
     */
    public function getHeadline(): string
    {
        return (string) $this->scopeConfig->getValue(
            self::XML_PATH_HEADLINE,
            ScopeInterface::SCOPE_STORE
        );
    }
}

Zum Schluss kommt das Template app/code/Mironsoft/HelloModule/view/frontend/templates/hello.phtml. Es nutzt keine externen Skripte, kein jQuery und keine Luma-Abhängigkeiten. Werte werden escaped. Falls ein Inline-script nötig wäre, müsste in einem Hyvä-CSP-Theme direkt danach $hyvaCsp->registerInlineScript() stehen. In diesem Beispiel brauchen wir kein JavaScript.


<?php
declare(strict_types=1);

use Magento\Framework\Escaper;
use Magento\Framework\View\Element\Template;
use Mironsoft\HelloModule\ViewModel\Hello;

/**
 * @var Template $block
 * @var Escaper $escaper
 * @var Hello $viewModel
 */
$viewModel = $block->getData('view_model');
?>

<?php if ($viewModel && $viewModel->isEnabled()): ?>
    <section class="mx-auto max-w-3xl px-4 py-12">
        <div class="rounded-lg border border-slate-200 bg-white p-6 shadow-sm">
            <p class="text-sm font-semibold uppercase tracking-wide text-blue-700">
                Mironsoft Hello Module
            </p>
            <h2 class="mt-2 text-2xl font-bold text-slate-900">
                <?= $escaper->escapeHtml($viewModel->getHeadline()) ?>
            </h2>
            <p class="mt-3 text-sm leading-6 text-slate-700">
                Dieses Template wird über Layout XML eingebunden und erhält seine Daten aus einem ViewModel.
            </p>
        </div>
    </section>
<?php endif; ?>

Nach dem Anlegen der Dateien aktivierst du das Modul über die Magento-CLI im Docker-Wrapper. In diesem Projekt bedeutet das: bin/magento setup:upgrade, danach Cache leeren. Für Frontend-Änderungen im Theme wäre die Deploy-Sequenz mit Tailwind-Build, statischen Dateien und Cache relevant. Bei einem reinen Modul-Template reicht in Entwicklung oft Cache Clean, abhängig von Modus und Umgebung.

6. Vergleich: Minimal-Modul vs. produktives Modul

Viele Tutorials zeigen nur registration.php und module.xml. Das reicht, um ein Modul sichtbar zu machen, aber nicht für produktive Arbeit. Ein echtes Modul braucht klare Konfiguration, Berechtigungen, eine saubere View-Schicht und nachvollziehbare Erweiterungspunkte. Wer ein Magento 2 Modul erstellen will, sollte deshalb von Anfang an eine vollständige Struktur wählen.

Bereich Minimal-Modul Produktives Modul
Registrierung registration.php, module.xml Zusätzlich klare Dependencies und Versionierungsstrategie
Konfiguration Oft keine Einstellungen config.xml, system.xml, ACL und eigener Admin-Bereich
Frontend Block enthält oft Logik Layout XML, Template und ViewModel sauber getrennt
Wartbarkeit Schnell gebaut, schwer erweiterbar Klare Pfade, testbare Klassen und stabile Verträge

Der Unterschied zeigt sich spätestens beim zweiten Feature. Ein Minimal-Modul wird schnell unübersichtlich, weil Konfiguration, Ausgabe und Logik ineinanderlaufen. Ein produktives Modul hat mehr Dateien, aber weniger versteckte Abhängigkeiten. Genau das macht Magento-Entwicklung langfristig schneller.

Mironsoft

Magento 2 Module, Hyvä Themes und technische Architektur

Ein Magento 2 Modul sauber entwickeln lassen?

Wir entwickeln Magento 2 Erweiterungen mit Konfiguration, ACL, Service Contracts, ViewModels und Hyvä-kompatibler Ausgabe. Sauber genug für Wartung, Tests und spätere Erweiterungen.

Modulstruktur

registration.php, module.xml, config.xml, system.xml und ACL

Frontend

Layout XML, ViewModels und Hyvä Templates ohne Luma-Ballast

Qualität

PHP 8.4, Dependency Injection, Tests und klare Erweiterungspunkte

8. Zusammenfassung

Ein eigenes Magento 2 Modul erstellen heißt, eine klare, erweiterbare Struktur aufzubauen. Die Basis besteht aus registration.php und module.xml. Für ein professionelles Modul kommen config.xml, system.xml, acl.xml, Routen, Controller, Layout XML, Template und ViewModel hinzu.

Der wichtigste Architekturpunkt ist die Trennung der Verantwortlichkeiten. Controller bleiben dünn, ViewModels liefern Daten, Templates rendern HTML und Konfiguration liegt im Adminbereich. So bleibt das Modul wartbar und passt zu Magento 2.4.8, PHP 8.4 und Hyvä.

Magento 2 Modul erstellen — Das Wichtigste auf einen Blick

Pfad

app/code/Mironsoft/HelloModule mit klarer Vendor- und Modulstruktur.

Pflichtbasis

registration.php und etc/module.xml registrieren das Modul.

Admin

config.xml, system.xml und acl.xml machen Einstellungen bedienbar und berechtigbar.

Hyvä

Daten über ViewModels bereitstellen, Templates schlank halten und kein jQuery oder Knockout.js laden.

9. FAQ: Eigenes Magento 2 Modul erstellen

1 Welche Dateien braucht ein Magento 2 Modul mindestens?
Mindestens registration.php und etc/module.xml. Für produktiven Code kommen Konfiguration, ACL, Routen, Layout XML und ViewModels dazu.
2 Wo liegt ein eigenes Magento 2 Modul?
Unter app/code/Vendor/Module, zum Beispiel app/code/Mironsoft/HelloModule.
3 Warum braucht ein Modul system.xml?
Damit Einstellungen im Adminbereich sichtbar und pflegbar sind. So muss der Shopbetreiber für einfache Optionen keinen Code ändern.
4 Wofür ist acl.xml zuständig?
ACL schützt Admin-Funktionen und Konfigurationen über Berechtigungen. Der system.xml-Abschnitt verweist auf eine ACL-Ressource.
5 Block oder ViewModel für Hyvä?
Für Daten im Template ist ein ViewModel mit ArgumentInterface meistens besser. Block-Klassen sollten nicht mit Geschäftslogik gefüllt werden.
6 Wie aktiviert man ein neues Modul?
Im Mark-Shust-Setup über Wrapper-Befehle, zum Beispiel bin/magento setup:upgrade und danach Cache leeren.
7 Was macht routes.xml?
Sie definiert die Frontend- oder Admin-Route. Der frontName wird Teil der URL.
8 Wie heißt der Layout-Handle?
Für hellomodule/index/index heißt die Layout-Datei hellomodule_index_index.xml.
9 Braucht jedes Modul db_schema.xml?
Nein. Nur wenn eigene Datenbanktabellen oder Spalten nötig sind. Dann deklaratives Schema nutzen, keine alten InstallScripts.
10 Welche Standards gelten für neue Module?
Constructor Property Promotion, englische PHPDocs, Dependency Injection, Service Contracts wo sinnvoll, Plugins statt Preferences und ViewModels für Templates.