Darstellung
Eigene Feldtypen
Ein Feldtyp beschreibt sich selbst: Er liefert seinen Eintrag für die Builder-Palette, sein Inspector-Schema, sein Frontend-Markup und die Regeln zum Sanitizing und Validieren des übermittelten Werts. Wer FieldInterface implementiert und sich registriert, erscheint automatisch im Builder, im REST-Katalog und im Renderer.
Das Interface
Dingfelder\AdForm\Fields\FieldInterface verlangt 14 Methoden.
Identität und Palette
php
public function type(): string;
public function label(): string;
public function default_label(): string;
public function description(): string;
public function icon(): string;
public function category(): string;
public function supports_required(): bool;| Methode | Bedeutung |
|---|---|
type() | Stabiler Typ-Schlüssel, z. B. text. Zugleich der Schlüssel in der Registry – ein zweiter Feldtyp mit demselben Wert überschreibt den ersten. |
label() | Lesbares Label für die Builder-Palette. |
default_label() | Label, das ein neu eingefügtes Feld auf dem Canvas bekommt. |
description() | Kurzbeschreibung in der Palette. |
icon() | Dashicon-Slug ohne das Präfix dashicons-, z. B. editor-textcolor. |
category() | Palette-Kategorie: basic, advanced, business, wordpress oder layout. |
supports_required() | Ob der Inspector einen Pflichtfeld-Schalter anzeigt. |
Einstellungen und Inspector
php
/** @return array<string, mixed> */
public function default_settings(): array;
/** @return list<array<string, mixed>> */
public function inspector_fields(): array;
/**
* @param array<string, mixed> $settings Raw settings.
* @return array<string, mixed>
*/
public function sanitize_settings( array $settings ): array;
/** @return array<string, mixed> */
public function to_rest_array(): array;default_settings() wird beim Einfügen eines Feldes in die Definition kopiert. inspector_fields() beschreibt die Steuerelemente im Inspector; jeder Eintrag hat mindestens name, path, type und label, optional help und options. Der path zeigt auf die Stelle in der Feldinstanz (label, required, key, settings.placeholder, …).
to_rest_array() liefert die Palette-/REST-Nutzlast. AbstractField baut sie bereits aus den übrigen Methoden zusammen – ergänzt um required_entitlements – sodass sie in der Regel nicht überschrieben werden muss.
Rendering und Übermittlung
php
/**
* @param array<string, mixed> $field Sanitized field from the definition.
* @param array<string, mixed> $context Renderer context (values, errors, form_id).
*/
public function render_html( array $field, array $context = array() ): string;
public function sanitize_submitted_value( mixed $value ): mixed;
/**
* @param array<string, mixed> $field Field instance.
*/
public function validate_submitted_value( mixed $value, array $field ): ?string;validate_submitted_value() gibt null zurück, wenn der Wert in Ordnung ist, und sonst die übersetzte Fehlermeldung.
AbstractField als Basis
Dingfelder\AdForm\Fields\AbstractField implementiert alles außer type() und label(). In der Praxis wird immer davon abgeleitet – eine direkte FieldInterface-Implementierung lohnt sich nur für vollständig eigenes Markup.
Kategorie-Konstanten:
php
AbstractField::CATEGORY_BASIC; // 'basic'
AbstractField::CATEGORY_ADVANCED; // 'advanced'
AbstractField::CATEGORY_BUSINESS; // 'business'
AbstractField::CATEGORY_WORDPRESS; // 'wordpress'
AbstractField::CATEGORY_LAYOUT; // 'layout'Was die Basisklasse übernimmt
render_html() baut den kompletten Wrapper: CSS-Klassen (ad-form__field, ad-form__field--{type}, ad-form__field--w{width}), die Data-Attribute data-field-id, data-field-key, data-required und data-conditions, den Sichtbarkeitszustand aus dem Renderer-Kontext sowie Label, Beschreibung und Fehlermeldung mit den passenden aria-describedby-Verknüpfungen.
sanitize_settings() startet von default_settings() und schickt jeden Wert durch sanitize_setting(). sanitize_submitted_value() ruft sanitize_text_field() auf – bei Arrays für jeden Eintrag. validate_submitted_value() prüft nur die Pflichtfeld-Regel.
Erweiterungspunkte für Unterklassen
| Methode | Zweck |
|---|---|
control_html( array $field, string $html_id, string $name, mixed $value, string $error, bool $required, array $context ): string | Kompletter Inhalt des Wrappers inklusive Label und Fehlern |
input_html( array $field, string $html_id, string $name, mixed $value, bool $required, array $described ): string | Nur das Eingabe-Element – der übliche Einstiegspunkt |
label_html( string $html_id, string $label, bool $required ): string | Label-Markup |
common_settings(): array | Basis-Settings: admin_label, placeholder, default_value, description, css_class, width |
identity_inspector(): array | Label, Required, Field key, Admin label |
extra_inspector(): array | Typspezifische Steuerelemente – hier wird meist ergänzt |
style_inspector(): array | Beschreibung, CSS-Klasse, Breite |
width_options(): array | Auswahl 100 / 75 / 66 / 50 / 33 / 25 |
extra_wrap_attributes( array $field, array $context ): array | Zusätzliche Attribute am Wrapper, z. B. data-formula |
sanitize_setting( string $key, mixed $value ): mixed | Sanitizing eines einzelnen Settings-Werts |
sanitize_options( mixed $value ): array | Normalisiert {label, value}-Paare |
is_empty_value( mixed $value ): bool | Leer-Prüfung für die Pflichtfeld-Validierung |
has_error_id( array $described ): bool | Ob unter den aria-describedby-IDs eine Fehler-ID ist |
required_entitlements()
php
/** @return list<string> */
public function required_entitlements(): array;Liefert die Feature-Schlüssel, die für diesen Feldtyp freigeschaltet sein müssen. AbstractField gibt eine leere Liste zurück – ein eigener Feldtyp ist also standardmäßig uneingeschränkt nutzbar. Die Methode steht bewusst auf der abstrakten Klasse und nicht auf FieldInterface, damit bestehende Interface-Implementierungen unverändert weiterlaufen. Der Wert erscheint zusätzlich als required_entitlements in to_rest_array().
Registrierung
Der Hook ad_form_register_fields feuert innerhalb von FieldRegistry::boot(), nachdem die Kern-Feldtypen registriert wurden, und bekommt die Registry als Parameter.
php
use Dingfelder\AdForm\Fields\FieldRegistry;
add_action(
'ad_form_register_fields',
static function ( FieldRegistry $registry ): void {
$registry->register( new Acme\AdFormFields\PhoneField() );
}
);Direkt danach läuft der Filter ad_form_fields über die fertige Map type => FieldInterface. Er eignet sich zum Entfernen oder Ersetzen von Feldtypen:
php
add_filter(
'ad_form_fields',
static function ( array $fields ): array {
unset( $fields['calculation'] );
return $fields;
}
);Nur einmal booten
FieldRegistry::boot() läuft genau einmal pro Request und wird beim ersten has(), get(), all() oder to_rest_array() ausgelöst. Wer sich zu spät einhängt – etwa erst auf wp –, verpasst die Registrierung. ad_form_register_fields und ad_form_fields sind die einzigen verlässlichen Zeitpunkte.
Die Kern-Feldtypen sind text, email, number, textarea, select, checkbox, calculation und submit.
Vollständiges Beispiel: Telefonnummer-Feld
php
<?php
/**
* Phone number field for AD Form.
*
* @package Acme\AdFormFields
*/
declare(strict_types=1);
namespace Acme\AdFormFields;
use Dingfelder\AdForm\Fields\AbstractField;
use Dingfelder\AdForm\Support\Html;
final class PhoneField extends AbstractField {
public const TYPE = 'phone';
public function type(): string {
return self::TYPE;
}
public function label(): string {
return __( 'Phone', 'acme-ad-form-fields' );
}
public function description(): string {
return __( 'Telephone number with optional pattern check.', 'acme-ad-form-fields' );
}
public function icon(): string {
return 'phone';
}
public function category(): string {
return self::CATEGORY_ADVANCED;
}
/**
* @return array<string, mixed>
*/
public function default_settings(): array {
$settings = $this->common_settings();
$settings['min_digits'] = '6';
$settings['autocomplete_tel'] = true;
return $settings;
}
/**
* @return list<array<string, mixed>>
*/
protected function extra_inspector(): array {
$fields = parent::extra_inspector();
$fields[] = array(
'name' => 'min_digits',
'path' => 'settings.min_digits',
'type' => 'text',
'label' => __( 'Minimum digits', 'acme-ad-form-fields' ),
'help' => __( 'Digits are counted after stripping spaces, slashes and dashes.', 'acme-ad-form-fields' ),
);
$fields[] = array(
'name' => 'autocomplete_tel',
'path' => 'settings.autocomplete_tel',
'type' => 'toggle',
'label' => __( 'Enable browser autocomplete', 'acme-ad-form-fields' ),
);
return $fields;
}
/**
* @param array<string, mixed> $field Field instance.
* @param list<string> $described Description/error ids.
*/
protected function input_html( array $field, string $html_id, string $name, mixed $value, bool $required, array $described ): string {
$settings = is_array( $field['settings'] ?? null ) ? $field['settings'] : array();
$placeholder = (string) ( $settings['placeholder'] ?? '' );
return '<input' . Html::attributes(
array(
'type' => 'tel',
'id' => $html_id,
'name' => $name,
'value' => (string) $value,
'placeholder' => '' !== $placeholder ? $placeholder : null,
'class' => 'ad-form__control',
'required' => $required,
'inputmode' => 'tel',
'autocomplete' => empty( $settings['autocomplete_tel'] ) ? null : 'tel',
'aria-invalid' => $this->has_error_id( $described ) ? 'true' : null,
'aria-required' => $required ? 'true' : null,
'aria-describedby' => array() !== $described ? implode( ' ', $described ) : null,
)
) . ' />';
}
public function sanitize_submitted_value( mixed $value ): mixed {
$text = trim( sanitize_text_field( (string) $value ) );
return preg_replace( '/[^0-9+()\/\s-]/', '', $text ) ?? '';
}
/**
* @param array<string, mixed> $field Field instance.
*/
public function validate_submitted_value( mixed $value, array $field ): ?string {
$required = parent::validate_submitted_value( $value, $field );
if ( null !== $required ) {
return $required;
}
$text = trim( (string) $value );
if ( '' === $text ) {
return null;
}
$settings = is_array( $field['settings'] ?? null ) ? $field['settings'] : array();
$minimum = max( 0, (int) ( $settings['min_digits'] ?? 0 ) );
$digits = preg_replace( '/\D/', '', $text ) ?? '';
if ( strlen( $digits ) < $minimum ) {
return __( 'Please enter a valid phone number.', 'acme-ad-form-fields' );
}
return null;
}
protected function sanitize_setting( string $key, mixed $value ): mixed {
return match ( $key ) {
'min_digits' => (string) max( 0, min( 20, (int) $value ) ),
'autocomplete_tel' => ! empty( $value ),
default => parent::sanitize_setting( $key, $value ),
};
}
}Registrierung in der Add-on-Haupt-Datei:
php
<?php
declare(strict_types=1);
use Dingfelder\AdForm\Fields\FieldRegistry;
add_action(
'ad_form_register_fields',
static function ( FieldRegistry $registry ): void {
$registry->register( new Acme\AdFormFields\PhoneField() );
}
);Was danach automatisch funktioniert
Sobald der Feldtyp registriert ist:
- Er erscheint in der Builder-Palette unter seiner Kategorie und im REST-Katalog (
GET /wp-json/ad-form/v1/fields). FieldRegistry::create_instance( 'phone', $existing_keys )erzeugt eine neue Feldinstanz mit eindeutigem Key, stabiler ID,default_label(),default_settings()und leerer Bedingungsgruppe.- Der
FormRendererruftrender_html()mit dem Renderer-Kontext auf. - Der
SubmitServiceruft erstsanitize_submitted_value(), dannvalidate_submitted_value(). - Bedingte Logik und Multi-Step funktionieren ohne Zutun, weil sie am Wrapper und an der Definition ansetzen, nicht am Feldtyp.
Verwandte Referenzen
- Formular-Schema – Aufbau einer Feldinstanz in
definition.fields - Visueller Builder – wie Palette und Inspector das Schema verwenden
- Frontend-Renderer – Markup, CSS-Klassen und Submit-Ablauf
- Hook-Referenz –
ad_form_register_fieldsundad_form_fields