ViewModels (ArgumentInterface) vs. klassische Block-Klassen - warum ViewModels bevorzugt werden
ViewModels (ArgumentInterface) vs. klassische Block-Klassen - warum ViewModels bevorzugt werden
~8 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026
Klassisches Magento löst Datenversorgung von Templates häufig über eigene Block-Klassen, die \Magento\Framework\View\Element\Template erweitern. In Hyvä-Projekten - und generell als moderne Magento-2-Konvention - wird stattdessen ein ViewModel bevorzugt, das \Magento\Framework\View\Element\Block\ArgumentInterface implementiert.
Das Problem mit eigenen Block-Klassen
Eine eigene Block-Klasse erbt automatisch die gesamte Komplexität von Template - Caching-Logik, toHtml(), Kind-Block-Verwaltung und vieles mehr, was für reine Datenversorgung gar nicht gebraucht wird. Außerdem kann ein Template nur einen Block als $block haben - wer mehrere unabhängige Datenquellen braucht, verschachtelt entweder weitere Blocks oder injiziert Abhängigkeiten direkt in die Block-Klasse, was Tests erschwert.
Die ViewModel-Alternative
Ein ViewModel ist eine ganz normale PHP-Klasse mit Constructor-Injection, die ArgumentInterface implementiert - ein reines Marker-Interface ohne vorgeschriebene Methoden. Es kann beliebig viele davon pro Template geben, jedes mit eigenem, sprechendem Namen im Layout-XML.
<?php
declare(strict_types=1);
namespace Mironsoft\TeamPage\ViewModel;
use Magento\Framework\View\Element\Block\ArgumentInterface;
/**
* Supplies team members and categories for the team page.
*/
class TeamMembers implements ArgumentInterface
{
/**
* @param TeamMemberRepositoryInterface $teamMemberRepository Data source for team members.
*/
public function __construct(
private readonly TeamMemberRepositoryInterface $teamMemberRepository,
) {
}
/**
* Returns all active team members.
*
* @return array<int, array{name: string, role: string, category: string}>
*/
public function getTeamMembers(): array
{
return $this->teamMemberRepository->getActive();
}
}Diese Klasse weiß nichts von HTML-Rendering, Caching oder Kind-Blocks - sie liefert reine Daten. Genau diese Trennung macht ViewModels leicht testbar: Ein Unit-Test instanziiert die Klasse mit einem Mock des Repositories, ganz ohne Magentos schwere Block-Infrastruktur mitschleppen zu müssen.
Bindung im Template
Im Template steht das ViewModel unter dem Argument-Namen zur Verfügung, der im Layout-XML vergeben wurde (siehe Kapitel 6):
<?php
/** @var \Magento\Framework\Escaper $escaper */
/** @var \Magento\Framework\View\Element\Template $block */
/** @var \Mironsoft\TeamPage\ViewModel\TeamMembers $teamViewModel */
$teamViewModel = $block->getData('team_view_model');
?>Mehrere ViewModels pro Block
Ein Block kann mehrere ViewModel-Argumente gleichzeitig binden - zum Beispiel ein ViewModel für Team-Daten und ein zweites für allgemeine Store-Konfiguration. Jedes bekommt einen eigenen argument name="..."-Wert im Layout-XML und wird im Template unter genau diesem Namen abgerufen.
Tipp: Faustregel für dieses Projekt: Neue Templates bekommen ein ViewModel, keine eigene Block-Klasse - außer es gibt einen sehr konkreten Grund, Template-Rendering-Verhalten selbst zu überschreiben (was ViewModels grundsätzlich nicht können, weil sie kein toHtml() haben).
Wann doch eine Block-Klasse?
Eine eigene Block-Klasse bleibt sinnvoll, wenn tatsächlich Rendering-Verhalten überschrieben werden muss - etwa eigenes Caching, eigene toHtml()-Logik oder das Verwalten vieler Kind-Blocks mit komplexer Sortierung. Für reine Datenversorgung, wie in praktisch allen Fällen dieser Serie, reicht ein ViewModel.