Custom-Renderer und Custom-Column-Klassen schreiben
Custom-Renderer und Custom-Column-Klassen schreiben
~9 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026
Kapitel 17 hat bereits eine eigene Column-Klasse für die Actions-Spalte gebaut. Dieses Kapitel verallgemeinert das Muster: Wie entscheidet man, ob eine PHP-Column-Klasse reicht oder ob eine eigene JavaScript-Komponente nötig ist - und wie baut man beides sauber?
Zwei Ebenen von Custom Rendering
- PHP-Column-Klasse (
prepareDataSource()) - formt die Rohdaten pro Zeile um, bevor sie an den Browser gehen. Reicht für alles, was sich als reiner Text/HTML-Wert oder als zusätzliches Datenfeld ausdrücken lässt. - Eigene JavaScript-Komponente (KnockoutJS-Template +
uiComponent) - nötig, sobald im Browser selbst Interaktivität nötig ist (Klick-Handler, dynamisches Nachladen), die über einen einfachen Link mitconfirmhinausgeht.
Für die Kundenstimmen reicht die erste Ebene: eine Rating-Spalte, die die Zahl 1-5 als Sternchen-Text darstellt, statt einer nackten Zahl.
Eigene Rating-Spalte
<?php
declare(strict_types=1);
namespace Mironsoft\Testimonial\Ui\Component\Listing\Column;
use Magento\Framework\Escaper;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;
/**
* Renders the numeric rating as a star string, e.g. "★★★★☆".
*/
class Rating extends Column
{
private const MAX_RATING = 5;
/**
* @param ContextInterface $context Rendering context.
* @param UiComponentFactory $uiComponentFactory Factory for nested UI components.
* @param Escaper $escaper Escapes the rendered star string for safe HTML output.
* @param array<string, mixed> $components Nested UI components.
* @param array<string, mixed> $data Additional UI Component data configuration.
*/
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
private readonly Escaper $escaper,
array $components = [],
array $data = [],
) {
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* Replaces the raw rating integer with a rendered star string.
*
* @param array<string, mixed> $dataSource Raw grid data source.
* @return array<string, mixed>
*/
public function prepareDataSource(array $dataSource): array
{
if (!isset($dataSource['data']['items'])) {
return $dataSource;
}
$fieldName = $this->getData('name');
foreach ($dataSource['data']['items'] as &$item) {
$rating = max(0, min(self::MAX_RATING, (int) $item[$fieldName]));
$stars = str_repeat('★', $rating) . str_repeat('☆', self::MAX_RATING - $rating);
$item[$fieldName] = $this->escaper->escapeHtml($stars);
}
return $dataSource;
}
}<column name="rating" class="Mironsoft\Testimonial\Ui\Component\Listing\Column\Rating">
<settings>
<filter>textRange</filter>
<label translate="true">Rating</label>
</settings>
</column>Achtung: $this->escaper->escapeHtml() ist hier keine Formalität: Die Grid-JavaScript-Templates rendern Spaltenwerte teilweise ungebunden als HTML. Ein Column-Renderer, der Benutzereingaben (nicht bloß berechnete Werte wie hier) unescaped zurückgibt, öffnet eine Stored-XSS-Lücke direkt im Admin-Grid.
Wann reicht PHP nicht mehr?
Ein Beispiel, das eine eigene JavaScript-Komponente bräuchte: ein Klick auf die Sterne-Spalte, der die Bewertung direkt im Grid ohne Reload ändert. Dafür wäre ein eigenes RequireJS-Modul (Erweiterung von Magento_Ui/js/grid/columns/column) samt eigenem KnockoutJS-Template nötig - deutlich mehr Aufwand als der PHP-Renderer oben, und außerhalb des Rahmens dieser Serie. Kapitel 20 zeigt mit Inline-Edit einen fertigen, bereits eingebauten Mittelweg für genau diesen Anwendungsfall.
Die Grenze erneut benannt
Wie bereits in Kapitel 1 erwähnt: Rating hier ist ein klassisches Beispiel dafür, dass UI Components eigene Konventionen erzwingen. Die Klasse muss Column erweitern und prepareDataSource() implementieren - ein ArgumentInterface-ViewModel könnte hier nicht eingesetzt werden. Die reine Formatierungslogik (Zahl zu Sternchen-String) wäre trotzdem leicht in eine eigene, testbare Utility-Methode auslagerbar, wenn sie an mehreren Stellen im Projekt gebraucht würde.