5.3. Die Settings API

Durch das Hinzufügen eines Top- bzw. eines Second-Level-Menüs erstellt WordPress automatisch eine Unterseite die Sie mit Inhalt füllen können. In der Regel dient diese Seite zur Darstellung von Formularfeldern. Gleiches gilt, wenn Sie eine neue Metabox hinzufügen.

Die Settings-API wurde entwickelt um solche Formulare halbautomatisch zu bearbeiten und abzuspeichern.

Halbautomatisch deswegen, weil WordPress Ihnen nicht die gesamte Arbeit abnimmt.

Das kann die Settings API:

  • Einstellungen (Settings) registrieren.
  • Formulare in Bereiche (Sections) unterteilen.
  • Fehlermeldungen erfassen.
  • Fehlermeldungen ausgeben.
  • Formularfelder validieren.
  • Formularfelder in die Datenbank speichern.
  • Formulare in einer Tabelle ausgeben (rendern).

Die Settins-API erspart Ihnen das Design einer Benutzeroberfläche, da die WordPress-Internen CSS-Styles benutzt werden. Darüber hinaus kümmert es sich (fast) automatisch um alle nötigen Security-Checks (Sie erfahren im nächsten Kapitel mehr über die Plugin-Sicherheit).

Das kann die Settings API nicht

Lediglich um die Darstellung der einzelnen Formular-Felder (Input, Select, Checkbox, etc.) müssen Sie sich selbst kümmern. Das heißt, es gibt keine vorgefertigten Funktionen für Input-Felder.

Voraussetzung

You’re not allowed to see this content. Please log in first.

Layout vorbereiten

Damit die globalen WordPress Styles auch auf das zu erstellende Formular angewandt wird, bauen wir uns zuerst den Rumpf nach der Vorgabe im Kapitel Konsequenz im Layout.

You’re not allowed to see this content. Please log in first.

Anzeige des Formulars

WordPress benötigt zur richtigen Funktionsweise zwei interne Funktionen. Zum einen settings_fields() und zum Zweiten do_settings_sections().

Die Funktion sieht demnach so aus:

You’re not allowed to see this content. Please log in first.

Einfügen der Pflichtfelder

settings_fields() erzeugt mehrere Hidden-Input Felder, die nötig sind, damit WordPress beim Absenden des Formulars und der anschließenden Verarbeitung weiß, welche Formularfelder abgespeichert werden müssen. Die Funktion erwartet nur einen Parameter. Und das ist eine Gruppenbezeichnung. Hier also mm_settings_group.

Die Ausgabe sieht in etwa so aus:

You’re not allowed to see this content. Please log in first.

Rendern der Einstellungsbereiche (Sections)

Die do_settings_sections() Funktion übernimmt letztlich noch das Rendern (also das Darstellen) der einzelnen Felder und Bereiche (Sections). Da wir allerdings noch keine Felder hinzugefügt haben, sehen wir erst einmal nichts:

Abb.: Leere Einstellungsseite
Abb.: Leere Einstellungsseite

Als einzigen Parameter erwartet die Funktion den so genannten Page-Hook. In der Regel wird hier der Rückgabewert angegeben, der von der Funktion add_submenu_page() oder add_menu_page() erzeugt wird.

Einfügen eines Absende-Buttons

Die Funktion submit_button() nimmt Ihnen ebenfalls Arbeit ab. Sie erzeugt einen Absende-Button im WordPress-Stil und wurde wie folgt definiert:

You’re not allowed to see this content. Please log in first.

Einstellungsbereiche (Sections) registrieren

Zu aller erst möchte WordPress, dass ein Einstellungsbereich (Section) registriert wird, dem dann später die einzelnen Einstellungsfelder (Settings) hinzugefügt werden. Zur Registrierung benutzen Sie:

You’re not allowed to see this content. Please log in first.

Eine Einstellung (Setting) zur Section hinzufügen

Nachdem die erste Section erstellt wurde, lassen sich endlos viele Formularfelder (Settings) anhängen. Dazu benutzen wir folgende von WordPress bereits definierte Funktion:

You’re not allowed to see this content. Please log in first.

Eine Einstellung registrieren

Bis jetzt haben Sie:

  1. Einen Menüpunkt (bzw. dadurch eine Einstellungsseite) erstellt.
  2. Das Formular-Grundgerüst mit Absende-Button zur Einstellungsseite angefügt.
  3. Einen Einstellungsbereich (Section) generiert.
  4. Und schließlich ein Einstellungsfeld (HTML-Input) zur Section hinzufügt.

Klicken Sie jetzt auf „Änderungen speichern“ passiert folgendes: WordPress erzeugt eine Fehlermeldung die besagt: „FEHLER: Die Einstellungsseite wurde nicht gefunden.

Das liegt daran, dass das Einstellungsfeld aus dem Beispiel oben noch nicht registriert wurde. In der Laufzeitumgebung existiert das Feld daher noch nicht und kann nicht gefunden werden. Ergo müssen wir eine letzte Funktion nutzen:

You’re not allowed to see this content. Please log in first.

Settings-API Flussdiagramme

Beim Abrufen der Einstellungsseite passiert dann folgendes:

Abb.: Settings-API Flussdiagramm
Abb.: Settings-API Flussdiagramm
  1. Der Action-Hook admin_init mit der Funktion mm_register_settings() wird aufgerufen.
  2. Eine neue Section wird mittels add_settings_section() registriert.
  3. Ein neues Feld wird mittels add_settings_field() zur Section hinzugefügt.
  4. Das Settings-Feld wird durch register_setting() registriert.
  5. Nun stehen alle Felder, die bis dahin registriert wurde, bereit und können mittels do_settings_sections() gerendert werden.

Beim Speichern der Seite passiert fast das gleiche. Nur, dass die Felder nicht angezeigt sondern abgespeichert werden. Danach erfolgt eine Weiterleitung auf die Einstellungsseite, die dann die Erfolgs- oder Fehlermeldung ausgibt.

Abb.: Settings-API Flussdiagramm des Speicherns
Abb.: Settings-API Flussdiagramm des Speicherns

Einen Einstellungswert auslesen

Was schließlich noch fehlt ist das Auslesen der Felder, da der jetzige Code lediglich immer den selben Inhalt im input-Feld anzeigt. Was aber, wenn sich dieser verändert? Also wenn der Benutzer etwas anderes eingibt als vorgegeben wurde?

You’re not allowed to see this content. Please log in first.

Der vollständige Beispiel-Code

You’re not allowed to see this content. Please log in first.

11 Kommentare zu “5.3. Die Settings API

  1. wenn man Optionsfelder setzt, sollten dann diese auch beim deaktivieren des Plugins entfernt werden?
    Reicht dann die Funktion unregister_setting()? Oder gibt es auch für die Felder eine delete function? In der Referece bin ich nicht fündig geworden.

    lg
    Andreas

    PS: hast du meine eMail bekommen?

  2. Ja richtig. Optionen sollten immer beim Deinstallieren entfernt werden. Sonst bleiben sie in der Datenbank als Leichen gespeichert.
    unregister_setting() hilft an der Stelle nicht. Denn es würde nur das Einstellungsfeld von der Settings-Page entfernen falls dort bereits hinzugefügt wurde.

    Es kommt darauf an, wo man seine Einstellungen gespeichert hat. In der Regel nutzt man die Options API. Ein delete_option() wäre dann richtig um die Einstellung aus der Datenbank zu entfernen.

  3. Wenn ich ein inputfeld hinzufüge, das ich nur zur überprüfung benötige und nicht gespeichert werden soll, wie filtere ich das zb. $_POST[‚beispiel‘] nach dem klicken auf den submit button heraus?

  4. Mein Problem daran ist, dass ich auf das einzelne Input Feld keinen Einfluss habe. Ich habe in meinem Form eine checkbox, das gesetzt wird, wenn zb die Tabellen in Datenbank neu erstellt werden sollen. Mit dem $_POST kann ich zwar das Feld abfragen, aber nicht verhindern, das dieses Feld nicht mit der setting_api in der wp_option mitgespeichert wird. Die checkbox erstelle ich genau so wie in diesem Beispiel beschrieben. Daher meine Frage wie kann ich verhindern, das die Einstellung dieser Checkbox nicht in die wp_options gespeichert wird

  5. Wenn du nicht möchtest, dass ein Feld überhaupt gespeichert wird, kannst du den Filter whitelist_options nutzen und das Feld entfernen bevor es gespeichert wird. Im Beispiel von oben würde das z.B. so funktionieren:

    
    add_filter( 'whitelist_options', function ( $whitelist_options ) {
    
    	if ( isset( $whitelist_options['mm_settings_group'] ) ) {
    		$k = array_search( 'mm_settings_section_1_field_1', $whitelist_options['mm_settings_group'] );
    
    		if ( false !== $k ) {
    			unset( $whitelist_options['mm_settings_group'][ $k ] );
    		}
    	}
    
    	return $whitelist_options;
    }, 20 );
    
  6. Ich habe ein Problem mit den register_setting. folgenden code habe ich geschrieben:

    
    public function options_update() {
    		register_setting( $this->plugin_name, $this->plugin_name, [$this, 'fsct_validate'] );
    		if( isset( $_REQUEST['settings-updated'] ) ){
    			print_r($this->_renew_table);
    			$fsct_db = new fsct_database( $this->plugin_name );
    			$fsct_db->fsct_install();
    		}
    	}
    
    public function fsct_validate( $input ) {
    		// All checkboxes inputs	
    		$valid = array();
    		print_r( $input );
    		$valid['db_name'] = ( isset( $input['db_name'] ) && !empty( $input['db_name'] ) ) ? sanitize_text_field( $input['db_name'] ) : DB_NAME;
    		$valid['db_user'] = ( isset( $input['db_user'] ) && !empty( $input['db_user'] ) ) ? sanitize_text_field( $input['db_user'] ) : DB_USER;
    		$valid['db_pwd'] = ( isset( $input['db_pwd'] ) && !empty( $input['db_pwd'] ) ) ? sanitize_text_field( $input['db_pwd'] ) : DB_PASSWORD;
    		$valid['db_host'] = ( isset( $input['db_host'] ) && !empty($input['db_host'] ) ) ? sanitize_text_field( $input['db_host'] ) : DB_HOST;
    		$valid['db_prefix'] = ( isset( $input['db_prefix'] ) && !empty($input['db_prefix'] ) ) ? sanitize_text_field( $input['db_prefix'] ) : 'fs';
    		$valid['tbl_installed'] = ( isset( $input['tbl_installed'] ) && !empty($input['tbl_installed'] ) ) ? sanitize_text_field( $input['tbl_installed'] ) : '0';
    		$valid['renew_tables'] = ( isset( $input['renew_tables'] ) && !empty( $input['renew_tables'] ) ) ? '1' : '0';
    
    		$this->_renew_table = $valid['renew_tables'];
    		return $valid;		
    	}
    

    jedoch schein es so als würde mir der Validate nicht aufgerufen. Denn die var $this->_renew_table wird in dieser Funktion nie gesetzt auch kann ich $input nicht abfragen. Irgend etwas ist in meiner Überlegung falsch und im Internet komme ich auch nicht weiter. Hast du einen Tipp wo ich meinen Fehler habe?

  7. Servus Andreas,
    seit WordPress 4.7.0 benötigt register_setting() als letzten Parameter ein Array mit Argumenten. Dort kannst du dann den Callback angeben. Richtig wäre also in deinem Fall:

    
    register_setting(
    	$this->plugin_name,
    	$this->plugin_name,
    	[
    		'sanitize_callback' => [ $this, 'fsct_validate' ],
    	]
    );
    

    Bitte versuch es doch einmal so.

  8. Ja so wie du beschrieben hast funktioniert jetzt der Aufruf von fsct_validate. Ich setze in dieser Funktion die Variable $this->_renew_table = $valid[‚renew_tables‘]; diese Private Var kann ich aber in der klassen funktion nach dem register_setting aufruf nicht abfragen. Innerhalb der fsct_validate wird der Wert nicht der Klassen Var zugewiesen.

    
    $valid['renew_tables'] = ( isset( $input['renew_tables'] ) && !empty( $input['renew_tables'] ) ) ? '1' : '0';
    $this->_renew_table = $valid['renew_tables'];
    

    Auch dieser Filter for dem register_setting funktioniert nicht.

    
    add_filter( 'whitelist_options', function ( $whitelist_options ) {
      if( isset( $whitelist_options[ $this->plugin_name ] ) ){
    	print_r( $whitelist_options );
      }
    });
    

    ich bekomme immer einen Array ausgegeben (mit print_r mache ich das damit ich sehe ob daten drinnen stehen) mit dem error ERROR: options page not found.

    Bei deiner zweiten Anmerkung, bin ich mir nicht sicher. Ich habe im Internet gestöbert und es ein paarmal so vorgefunden, wenn ich alle options in einem speichern möchte und nicht jeden Wert einzeln.

  9. A) Wie stellst du fest, dass der Wert an $this->_renew_table nicht zugewiesen wird?
    B) Beachte bitte, dass ein Filter immer einen Rückgabewert haben muss. Bei dir fehlt return $whitelist_options. Wahrscheinlich führt das zu einem genannten Fehler.