Einen eigenen "Punkte-Banner" Content Type für Page Builder bauen
Einen eigenen "Punkte-Banner" Content Type für Page Builder bauen
~9 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026
Kapitel 58 hat die Grenze klar gezogen: ein eigener Content Type zeigt in der Regel statischen, redaktionellen Inhalt, keinen live-aktuellen Punktestand. Der "Punkte-Banner" dieses Kapitels nimmt das ernst - er wirbt FÜR das Treueprogramm ("Sammle Punkte bei jedem Einkauf!"), zeigt aber niemals den Punktestand eines konkreten Kunden. Frei per Drag-and-drop auf jeder Landingpage platzierbar, mit Überschrift, Text, Call-to-Action und Hintergrundfarbe, alles im Admin editierbar.
Content-Type-Registrierung
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_PageBuilder:etc/content_type.xsd">
<type name="mironsoft_loyalty_points_banner"
label="Points Banner"
translate="label"
component="Magento_PageBuilder/js/content-type"
preview_component="Mironsoft_Loyalty/js/content-type/points-banner/preview"
master_component="Magento_PageBuilder/js/content-type/master"
form="mironsoft_loyalty_points_banner_form"
icon="icon-pagebuilder-banner"
menu_section="elements"
sortOrder="200">
<appearances>
<appearance name="default" default="true"/>
</appearances>
</type>
</config>Tipp: Diese Datei zeigt die wesentliche Form, nicht das komplette XSD - siehe den Tipp am Ende von Kapitel 58 zum eingebauten Banner-Content-Type als vollständigem Referenzbeispiel in der eigenen Installation.
Admin-Formular für die Eigenschaften
form="mironsoft_loyalty_points_banner_form" verweist auf eine UI-Component-Formulardefinition mit den fünf Feldern heading, body_text, cta_label, cta_url und background_color - technisch dieselbe UI-Component-Formular-Technik, die bereits das Prämien-Admin-Grid aus Kapitel 16 genutzt hat. Die separate Tutorial-Serie "Admin-Grids & Formulare in Magento 2" dieses Katalogs (admin-grids-formulare DE / magento-admin-grids-forms EN) behandelt UI-Component-Formulare im Detail - hier reicht die Feldliste, um zu verstehen, was PointsBanner::getHeading() & Co. unten lesen.
Die Content-Type-Klasse
<?php
declare(strict_types=1);
namespace Mironsoft\Loyalty\Block\PageBuilder;
use Magento\Framework\View\Element\Template\Context;
use Magento\PageBuilder\Block\ContentType\ContentTypeAbstract;
/**
* Renders the "Points Banner" Page Builder content type: a static, admin-editable marketing
* banner promoting the loyalty program. Deliberately holds no dependency on
* ViewModel\PointsBalance (chapter 48) or PointsBalanceWidget (chapter 56) - unlike those,
* this content type shows the exact same content to every visitor, so it needs no
* per-customer data at all (chapter 58 explains why that distinction matters here).
* Extends the same base class Magento's own Banner content type extends.
*/
class PointsBanner extends ContentTypeAbstract
{
/**
* @param Context $context Framework template context.
* @param array<string, mixed> $data Additional block data, populated by Page Builder from the saved attributes.
*/
public function __construct(
Context $context,
array $data = [],
) {
parent::__construct($context, $data);
}
/**
* Returns the configured banner heading, falling back to a sensible default.
*
* @return string
*/
public function getHeading(): string
{
return (string) ($this->getData('heading') ?: __('Earn points on every order'));
}
/**
* Returns the configured banner body text, empty string if the editor left it blank.
*
* @return string
*/
public function getBodyText(): string
{
return (string) $this->getData('body_text');
}
/**
* Returns the configured call-to-action label, empty string if unset.
*
* @return string
*/
public function getCtaLabel(): string
{
return (string) $this->getData('cta_label');
}
/**
* Returns the configured call-to-action target URL, empty string if unset.
*
* @return string
*/
public function getCtaUrl(): string
{
return (string) $this->getData('cta_url');
}
/**
* Returns the configured background color as a CSS-safe hex value, defaulting to the
* brand-dark color when the stored value is missing or malformed.
*
* @return string
*/
public function getBackgroundColor(): string
{
$color = (string) $this->getData('background_color');
return $color !== '' && preg_match('/^#[0-9a-fA-F]{6}$/', $color) === 1 ? $color : '#0f172a';
}
}\Magento\PageBuilder\Block\ContentType\ContentTypeAbstract ist die von Page Builder vorgegebene Basisklasse, die Kapitel 47 bereits als dritten Fall angekündigt hat, in dem Magentos Kernkonventionen eine echte Block-Unterklasse erzwingen - genau dieselbe Klasse, von der auch der eingebaute Banner-Content-Type erbt.
Das Frontend-Template
<?php
declare(strict_types=1);
use Magento\Framework\Escaper;
use Mironsoft\Loyalty\Block\PageBuilder\PointsBanner;
/**
* @var PointsBanner $block
* @var Escaper $escaper
*/
?>
<div class="my-8 overflow-hidden rounded-2xl px-6 py-10 text-center sm:px-12"
style="background-color: <?= $escaper->escapeHtmlAttr($block->getBackgroundColor()) ?>;">
<h2 class="text-2xl font-bold text-white sm:text-3xl">
<?= $escaper->escapeHtml($block->getHeading()) ?>
</h2>
<?php if ($block->getBodyText() !== ''): ?>
<p class="mx-auto mt-3 max-w-2xl text-base text-white/80">
<?= $escaper->escapeHtml($block->getBodyText()) ?>
</p>
<?php endif; ?>
<?php if ($block->getCtaLabel() !== '' && $block->getCtaUrl() !== ''): ?>
<a href="<?= $escaper->escapeUrl($block->getCtaUrl()) ?>"
class="mt-6 inline-flex items-center rounded-full bg-white px-6 py-3 text-sm font-semibold text-gray-900 shadow-sm hover:bg-gray-100">
<?= $escaper->escapeHtml($block->getCtaLabel()) ?>
</a>
<?php endif; ?>
</div>Dasselbe Block+Template-Paar übernimmt zwei Rollen: es erzeugt beim Speichern der Seite im Admin das eingefrorene Master-Format-HTML (Kapitel 58) UND liefert - sollte ein Content Type das je erfordern - dieselbe Ausgabe bei direktem Frontend-Rendering. Für einen rein statischen Content Type wie diesen ist das ein Implementierungsdetail ohne sichtbaren Unterschied; wichtig wird es erst bei Content Types, die den {{widget}}-Platzhalter-Trick aus Kapitel 58 nutzen.
Achtung: Bewusst KEINE Verbindung zu ViewModel\PointsBalance oder PointsBalanceWidget. Ein Versuch, hier den eingeloggten Kundennamen oder Punktestand einzublenden, würde entweder denselben (den der Redakteurin beim Speichern) Wert für ALLE Besucher zeigen oder - je nach Master-Format-Erzeugung - gar keinen sinnvollen Wert, niemals den tatsächlich aktuellen Wert der jeweiligen Besucherin. Genau das ist der in Kapitel 58 beschriebene strukturelle Unterschied zum Widget, nicht ein vermeidbarer Implementierungsfehler.
Tipp: Wie widget.xml in Kapitel 55: content_type.xml landet im config-Cache. bin/cache-clean config nach jeder Änderung, damit der neue Content Type in der Page-Builder-Bausteinpalette im Admin auftaucht.