Theme
Pagebuilder-Widgets
Ein Widget ist ein vorkonfiguriertes Frontend-Element, das Redakteure im Visual Pagebuilder auf Seiten platzieren. Widgets liefern eigene Vue-Komponenten fuers Rendering, ein Admin-Formular-Menu und optional einen PHP-Datenlader fuer dynamische Inhalte. Diese Seite erklaert das Konzept und grenzt es zu Admin-Plugins ab.
Widget vs. Admin-Plugin
| Aspekt | Admin-Plugin | Pagebuilder-Widget |
|---|---|---|
| Was | Modul im Backend-Menue | Baustein fuer Pagebuilder-Seiten |
| Verzeichnis | _public/extensions/core/backend/{plugin}/ | _public/extensions/core/backend/{plugin}/widgets/{widget}/ |
| Klasse | {plugin}_BackendPlugin extends install_controller | {widget}_PageBuilderPlugin (keine Vererbung) |
| Registrierung | $this->content_construct + $this->apiEndpoints | $this->pagebuilder_widgets als JSON-Array |
| Frontend | Optional (Admin-layout/index.vue) | Pflicht (template/index.vue) |
| Lebt in | plugin_backend-Tabelle | page_widgets-Tabelle |
Ein Plugin kann beides gleichzeitig anbieten — z. B. einen Admin-Bereich zum Verwalten von Testimonials plus ein Widget, das diese im Frontend anzeigt. Siehe Plugin-Anatomie und das Beispiel unten.
Wozu Widgets bauen?
| Use Case | Beispiel |
|---|---|
| Wiederkehrender Content-Block | Titel-Banner, USP-Iconliste, Testimonial |
| Integration einer Datenquelle | Blog-Preview, Produkt-Teaser, Instagram-Feed |
| Visuelles Element ohne DB | Spacer, Button, Image-Overlay-Text |
| Komplette Subseite | Shop-Checkout, User-Center, 3D-Exhibition |
| Formulare | Newsletter-Anmeldung, Custom-Form |
Das CMS liefert bereits ~42 Widgets mit (siehe Widget-Katalog unten). Eigene Widgets fuegen sich nahtlos in den gleichen Picker ein.
Widget-Lebenszyklus
1. install() # Plugin setzt $this->pagebuilder_widgets = [JSON...]
→ Eintrag in page_widgets (Widget-Registry) pro Widget-Typ
2. vueRegistry-Rebuild # /admin/cache oder Cache-Rebuild
→ Scan aller widgets/*/template/index.vue
→ Generierung von plugin-registry.js pro Theme
3. Picker im Pagebuilder # WidgetPickerModal.vue
→ Liest page_widgets, zeigt widget_title + widget_icon + widget_subtitle
4. Redakteur zieht Widget auf Seite
→ Zeile in page_row_col_widget (DB)
5. Modal-Formular rendert widget_menu-Felder
→ Admin-User befuellt Felder → PATCH /api/backend/pagebuilder
6. Frontend-Rendering (PageService)
→ Widget-Array wird an PagebuilderWidget.vue uebergeben
→ getWidgetContent() laedt ggf. dynamische Daten (PHP)
→ plugin-registry.js loest widget_template → Vue-Komponente
→ Komponente bekommt { data, content } als widget-PropRegistrierung in bootstrap.php des Plugins
Die eigentliche Widget-Registrierung geschieht im Plugin — nicht in der Widget-Ordner-eigenen bootstrap.php. Beispiel aus widgetsBase:
php
// widgetsBase/bootstrap.php (Ausschnitt aus install())
$this->pagebuilder_widgets = [
'{
"widget_title": "Title",
"widget_icon": "icon.png",
"widget_subtitle": "Insert a title (H1-H6)",
"widget_template": "title",
"widget_menu": [
{"row": "title", "type": "text", "placeholder": "Title"},
{"row": "subtitle", "type": "select", "placeholder": "Typ",
"value": "id", "title": "title",
"options": [
{"id": "0", "title": "H1"},
{"id": "1", "title": "H2"},
{"id": "2", "title": "H3"}
]}
]
}',
'{
"widget_title": "Text / HTML",
"widget_icon": "icon.png",
"widget_template": "html",
"widget_menu": [
{"row": "text", "type": "textarea", "placeholder": "Text / HTML"}
]
}'
];Jeder Eintrag ist ein JSON-String mit mindestens widget_title, widget_icon, widget_template und widget_menu. Details zum Feldtypen-Schema siehe Widget-Menu-Feldtypen.
Nach Aenderungen am Array: Plugin de-/reinstallieren oder manuell in page_widgets updaten. Nach Aenderungen an template/index.vue: vueRegistry-Rebuild unter /admin/cache.
Datenbank-Tabellen
| Tabelle | Zweck |
|---|---|
page_widgets | Widget-Registry (ein Eintrag pro Widget-Typ, aus $this->pagebuilder_widgets) |
page_row_col_widget | Platzierte Widget-Instanzen pro Seite (mit Spalten wie title, subtitle, text, image, …) |
page_widget_item | Items fuer Repeater-Widgets (Sub-Datensaetze mit base_id/language_short) |
Spalten von page_row_col_widget entsprechen direkt den moeglichen row-Werten im widget_menu. Details: Widget-Menu-Feldtypen › DB-Spalten.
Widget-Katalog
Eingebaute Widgets nach Plugin:
| Plugin | Widgets |
|---|---|
widgetsBase | title, html, image, footer_links, social_footer, pagebuilder_element |
spacer / button / imageButton / imageOverlayText | je ein Widget |
titleBanner / titleBannerElement | Banner-Elemente |
infoBoxes / uspIconList / instagramJourney / locationMap / jobs / gallery | Content-Listen mit Items |
blog | blog, blogpreview |
emailmarketing | newsletter |
productListing | productListing |
shop | productTeaser, categoryBannerNoLink, newReleasesNoLink, user, shop, checkout, abobutton, calendar |
shopware6 | sw6ProductListing, sw6ProductDetail, sw6Cart, sw6Checkout, sw6UserAccount, sw6CategoryTeaser, sw6SearchBar |
elasticsearch | elasticsearch, searchbar |
elearning | elearning |
exhibition | exhibition |
pagebuilder | form |
Insgesamt ~42 Widgets. Die vollstaendige Feld-Definitions-Referenz liegt im Skill pagebuilder-widgets (references/widget-catalog.md im Repo).
shop/widgets/email ist kein Pagebuilder-Widget
Der Ordner _public/extensions/core/backend/shop/widgets/email/ liegt aus Legacy-Gruenden unter widgets/, ist aber kein Picker-Widget — es handelt sich um ein PHPMailer-basiertes Template fuer Auftrags-Bestaetigungsmails. Analog: der menueditor registriert keine Pagebuilder-Widgets (reines Admin-Plugin mit content_construct=template).
Minimal-Widget
Der schnellste Weg zu einem funktionierenden Widget:
_public/extensions/core/backend/{plugin}/
└── widgets/
└── helloWidget/
├── bootstrap.php
├── icon.png
└── template/
└── index.vuephp
// bootstrap.php
<?php
class helloWidget_PageBuilderPlugin
{
public static function getVersion() { return "1.0.0"; }
public static function getName() { return "Hello Widget"; }
public static function getWidgetContent($widget_array, $backend = false)
{
return null; // keine dynamischen Daten
}
}vue
<!-- template/index.vue -->
<template>
<div class="hello-widget">
<h2>{{ widget.data?.title || 'Hello!' }}</h2>
</div>
</template>
<script setup lang="ts">
defineProps<{ widget: { data?: Record<string, any>; content?: any } }>()
</script>Die Registrierung liegt in der bootstrap.php des Plugins (nicht in diesem Widget-Ordner). Vollstaendiges Copy-Paste-Beispiel siehe Testimonial-Widget.
Siehe auch
- Widget-Anatomie — Ordner-Struktur +
_PageBuilderPlugin-Klasse im Detail - Widget-Menu-Feldtypen — alle Feldtypen (text/textarea/select/filemanager/items)
- getWidgetContent() — dynamische Daten in PHP laden
- Widget-Items / Repeater — Sub-Listen mit
page_widget_item - Custom-CSS-Konvention —
.widget_{id}-Wrappers, PurgeCSS-Safelist - User-Center-Items — Plugin-Registrierung im User-Center-Widget
- Beispiel: Testimonial-Widget — komplettes Widget von null
- Plugin-Anatomie — das ueberliegende Plugin-System