Eigene Inhaltstypen
Ein Verein braucht manchmal mehr, als das Plugin mitbringt: Fotoalben, Ausrüstung, Fahrzeuge einer Feuerwehr. Dafür gibt es Add-ons: ein eigenes Plugin, das WP Club Elements voraussetzt und ihm einen Post Type mit Feldern hinzufügt.
Das Add-on schreibt nur die Deklaration. Registrierung, Meta-Boxen, register_post_meta mit REST-Schema, die Felder für Herkunft und Kennung und der Feldwähler der Feld-Blöcke kommen aus der Basis. Dein Typ verhält sich danach wie die eigenen Typen des Plugins.
Was das Add-on liefert
Abschnitt betitelt „Was das Add-on liefert“beispiel-album/├─ beispiel-album.php Plugin-Kopf (Requires Plugins: wp-club-elements), Autoloader├─ includes/│ ├─ Plugin.php hängt sich in wpce_register_post_types ein│ ├─ AlbumCPT.php extends WPCE\Plugin\CustomPostTypes\BaseCPT│ └─ AlbumCategoryTaxonomy extends WPCE\Plugin\CustomTaxonomies\BaseTaxonomy├─ src/blocks/… eigene Blöcke, falls die Feld-Blöcke der Basis nicht reichen└─ templates/, patterns Fallback-Templates und Patterns des Add-onsDas Beispiel hier ist ein Fotoalbum, erfunden für diese Seite. Ein fertiges Add-on dafür gibt es nicht; die Basis testet denselben Weg mit einem Album-Typ (siehe ganz unten).
Der Post Type
Abschnitt betitelt „Der Post Type“Eine Klasse, zwei Methoden. schema() beschreibt die Felder, get_post_type_args() die Argumente für register_post_type():
namespace Beispiel\Album;
use WPCE\Plugin\CustomPostTypes\BaseCPT;use WPCE\Plugin\Schema\FieldDefinition;use WPCE\Plugin\Schema\FieldType;use WPCE\Plugin\Schema\MetaBoxDefinition;use WPCE\Plugin\Schema\PostTypeSchema;
final class AlbumCPT extends BaseCPT {
public static function schema(): PostTypeSchema { return new PostTypeSchema( 'wpce_album', array( new MetaBoxDefinition( 'wpce_album_details', __( 'Album', 'beispiel-album' ), array( new FieldDefinition( '_wpce_album_photos', FieldType::Gallery, __( 'Photos', 'beispiel-album' ), '' ), new FieldDefinition( '_wpce_album_description', FieldType::RichText, __( 'Description', 'beispiel-album' ), '' ), new FieldDefinition( '_wpce_album_sections', FieldType::Reference, __( 'Departments', 'beispiel-album' ), null, array( 'post_type' => 'wpce_section', 'multiple' => true ) ), ) ), ) ); }
protected function get_post_type_args(): array { return array( 'labels' => array( 'name' => 'Alben', 'singular_name' => 'Album' ), 'public' => true, 'has_archive' => true, 'show_in_rest' => true, 'supports' => array( 'title', 'thumbnail' ), 'rewrite' => array( 'slug' => 'album' ), ); }}Die Felder
Abschnitt betitelt „Die Felder“FieldDefinition( key, type, label, admin_label, options ):
| Argument | Bedeutung |
|---|---|
key |
Meta-Key, mit _wpce_-Präfix und dem Präfix des Add-ons dahinter (_wpce_album_…) |
type |
ein FieldType (siehe unten) |
label |
Beschriftung im Feldwähler der Blöcke |
admin_label |
Beschriftung in der Meta-Box: null = wie label, '' = keine (der Titel der Meta-Box trägt sie) |
options |
typabhängig: post_type (Relation, Reference), multiple, choices (Select), allowed_types (Contact), min und max (Number) |
Die Feldtypen (WPCE\Plugin\Schema\FieldType):
| Typ | Für |
|---|---|
Text, Textarea, Number, Select |
einfache Werte |
RichText |
formatierter Text |
Image, Gallery, Files |
ein Bild, mehrere Bilder, Dateien |
Contact, Address, Bank, OpeningHours |
Kontakte, Adresse, Bankverbindung, Öffnungszeiten |
Relation |
Verweis auf Einträge, mit Beschriftung je Verweis (die Leitung eines Kurses) |
Reference |
Verweis auf Einträge, eine Meta-Zeile je ID |
Place |
ein Ort: Standort oder freier Text |
ScheduleRecurring, ScheduleEvent |
wiederkehrende Zeiten, datierte Tage |
Rooms |
Räume |
Jeder Typ bringt ein Admin-Feld, einen Sanitizer, ein REST-Schema und, außer bei Bank, einen Feld-Block mit.
Die Registrierung
Abschnitt betitelt „Die Registrierung“In der Hauptdatei des Add-ons, sobald WP Club Elements da ist:
add_action( 'wpce_register_post_types', static function (): void { \WPCE\Plugin\Schema\SchemaRegistry::register_post_type( \Beispiel\Album\AlbumCPT::class );} );Die Basis feuert diese Action auf init (Priorität 10), registriert danach alle Post Types, erst die der Basis, dann die der Add-ons, auf init:20 und das Meta auf init:21.
Geprüft an der Testwebsite mit dem Demoverein, mit genau dieser Klasse, ohne etwas zu speichern:
post_type_exists( 'wpce_album' )ist wahr.GET /wpce/v1/fields/wpce_albumliefert_wpce_album_photos,_wpce_album_descriptionund_wpce_album_sections.get_registered_meta_keys( 'post', 'wpce_album' )nennt diese drei Felder und dazu_wpce_sourceund_wpce_external_id, die Felder für Herkunft und Kennung.- Der Listen-Block hat für den neuen Typ einen eigenen Eintrag im Block-Einfüger. Seine Vorbelegung ist
cards, das Bild das Titelbild, der Kurztext das Feld_wpce_album_description. Das Plugin leitet sie aus dem Schema ab. Willst du sie ändern, nutze den Filterwpce_entry_types.
Ab da gilt für das Add-on dasselbe wie für die Basis:
GET /wp/v2/wpce_album/<id>liefert die Felder untermetaals typisierte JSON-Strukturen. Das Schreiben läuft durch denselben Sanitizer wie die Meta-Box.Identity::upsert()funktioniert, die Herkunft eines Eintrags steht in_wpce_sourceund_wpce_external_id.- Die Feld-Blöcke (
wpce/field-gallery,wpce/field-richtext,wpce/field-relationund die anderen) rendern die Felder in der Abfrage und auf Einzelseiten. FieldRegistry::get_value()liest ein Feld so, wie sein Typ es speichert.
Taxonomien
Abschnitt betitelt „Taxonomien“Genauso wie in der Basis. Eine Klasse extends BaseTaxonomy mit get_taxonomy_args(), im Konstruktor parent::__construct( 'wpce_album_category', array( 'wpce_album' ) ):
namespace Beispiel\Album;
use WPCE\Plugin\CustomTaxonomies\BaseTaxonomy;
final class AlbumCategoryTaxonomy extends BaseTaxonomy {
public function __construct() { parent::__construct( 'wpce_album_category', array( 'wpce_album' ) ); }
protected function get_taxonomy_args(): array { return array( 'hierarchical' => true, 'labels' => array( 'name' => 'Albumarten', 'singular_name' => 'Albumart' ), 'show_ui' => true, 'show_admin_column' => true, 'show_in_rest' => true, 'rewrite' => array( 'slug' => 'albumart' ), ); }}Registriert wird sie in derselben Action. Sie hängt sich selbst auf init:25:
add_action( 'wpce_register_post_types', static function (): void { \WPCE\Plugin\Schema\SchemaRegistry::register_post_type( \Beispiel\Album\AlbumCPT::class ); ( new \Beispiel\Album\AlbumCategoryTaxonomy() )->register();} );Geprüft: Danach gibt es wpce_album_category und sie hängt an wpce_album.
Prüfungen, Listen und Hilfetexte
Abschnitt betitelt „Prüfungen, Listen und Hilfetexte“Was die Basis für ihre Typen im Backend tut, bekommt ein Add-on-Typ aus seinem Schema, ohne eigenen Code:
-
Prüfungen nach dem Speichern. Jedes Feld wird nach seinem Typ geprüft (Zeit endet vor Beginn, Zeile ohne Uhrzeit, E-Mail, Telefonnummer, IBAN, BIC). Das Ergebnis steht als Hinweis oben auf der Maske, als Markierung „Bitte prüfen“ in der Liste und im Dashboard. Gespeichert wird immer. Drei Optionen einer
FieldDefinitionergänzen, was der Typ allein nicht wissen kann:Option Wert Wirkung neededwarningoderhintmeldet das leere Feld formatamountTextfeld, das einen Geldbetrag enthält at_mostMeta-Key die Zahl darf das andere Feld nicht übersteigen (Mindestalter, Höchstalter) hide_when_emptytruenur bei Textfeldern: Die Website lässt einen Eintrag ohne diesen Wert aus allen Listen und Seiten aus, so wie sie einen Mitgliedsbeitrag ohne Betrag auslässt ( 0ist ein Wert). Das Backend, WP-CLI und Cron sehen den Eintrag weiter. Der Filterwpce_hide_incomplete_entriesschaltet es ab. Nur gegen den Quelltext gelesen. Damit der Redakteur es erfährt, setz zusätzlichneeded => warningGeprüft mit einem Feld
needed => warning, einem Zahlenfeldneeded => hintund einem Textfeldformat => amountmit dem Inhalt „abc“. Das Ergebnis: eine Warnung „Noch nicht ausgefüllt.“, ein Hinweis „Noch nicht ausgefüllt.“ und eine Warnung „„abc“ ist kein Betrag.“Eigene Prüfungen hängt der Filter
wpce_entry_problemsan, siehe Filter und Hooks. -
Hilfetext am Feld. Die Option
helpeinerFieldDefinition: ein Satz unter der Beschriftung. -
Listenspalten. Abteilungen (mit Filter), Standort, Personen, Dateizahl und Betrag folgen aus den Feldtypen. Jede Taxonomie mit
show_admin_columnbekommt einen Filter. Ein Typ mit datierten Tagen (FieldType::ScheduleEvent) wird nach Termin sortiert, Vergangenes ist ausgegraut. Ist das Feldneeded => warning, bleiben Einträge ohne Tag aus den Listen der Website. -
Leere Liste. Der Satz aus
descriptionder Post-Type-Argumente und ein Knopf zum Anlegen. -
Link zur Doku. Liste, Maske und Kategorien eines Typs zeigen oben rechts „Hilfe zu dieser Seite“, sobald es eine Adresse dafür gibt. Für die Typen der Basis kennt das Plugin sie. Ein Add-on nennt seine über den Filter
wpce_doc_urlmit dem Schlüsselpost_type:wpce_album, siehe Filter und Hooks. Derselbe Filter gibt dem Listen-Block, der den Typ zeigt (block:wpce/entries:wpce_album), und jedem eigenen Block des Add-ons (block:beispiel-album/album) den Link „Hilfe zu diesem Block“. Das sind je eine Zeile Code.
Was die Basis nicht übernimmt
Abschnitt betitelt „Was die Basis nicht übernimmt“- Templates und Patterns. Das Add-on registriert eigene Fallback-Templates (
register_block_template( 'beispiel-album//single-wpce_album', … )) und Patterns. Das Theme darf sie überschreiben. - Eigene Blöcke. Nur, wenn die Feld-Blöcke nicht reichen. Der Namensraum ist
beispiel-album/. - Migrationen. Eigene Änderungen am Speicherformat versioniert das Add-on selbst, mit eigener Option und eigenem Upgrader nach dem Muster der Basis, siehe Datenmigrationen. Eine Vorlage dafür kommt, sobald das erste Add-on sie braucht.
- Übersetzungen. Eigene Text Domain (
beispiel-album).
Ein Test als Vorlage
Abschnitt betitelt „Ein Test als Vorlage“tests/integration/AddonApiTest.php in der Basis registriert einen Album-Typ auf genau diesem Weg und prüft Registrierung, Meta, Kennung, Feldwähler und Block-Rendering. Ein Add-on kann ihn als Vorlage für seine eigene Integrationssuite nehmen.
Was schiefgeht
Abschnitt betitelt „Was schiefgeht“- Die Registrierung außerhalb der Action.
wpce_register_post_typesfeuert aufinit:10. Wer den Typ erst nachinit:10meldet, verpasst die Runde, in der die Basis alle Typen registriert. Melde ihn darum in der Action. - Das Add-on lädt vor der Basis. Dann feuert die Action nie. Trag im Kopf
Requires Plugins: wp-club-elementsein und prüfe im Zweifel mitclass_exists( '\WPCE\Plugin\Schema\SchemaRegistry' ). - Ein Meta-Key ohne die beiden Präfixe. So ist es Konvention:
_wpce_, dann das Präfix des Add-ons. Ohne sie kann dein Key mit dem Key eines Typs der Basis oder eines anderen Add-ons zusammenfallen. - Ein Typ ohne eigene Seite (
public => false). Er hat keine Seite im Netz, und der Listen-Block bietet ihn nur an, wenn jemand ihn überwpce_entry_typesdefiniert. - Eine Taxonomie ohne
show_in_rest. Der Block-Editor findet sie nicht.