9.8.1. hook_form_alter() – Hinzufügen von Submit- und Validierungsfunktionen zu bestehenden Formularen
In einer der vorherigen Lektionen haben wir gelernt, was Hooks sind. In dieser Lektion arbeiten wir praktisch mit hook_form_alter() und fügen einem bestehenden Formular neue Funktionalitäten hinzu.
Beispiele für den Code finden Sie auf GitHub:
https://github.com/levmyshkin/drupalbook8
Wir beginnen mit hook_form_alter(). Um einen Hook im Modul hinzuzufügen, müssen wir eine Datei namens MODULENAME.module anlegen, hier also drupalbook.module. Diese PHP-Datei enthält unsere Hooks und Hilfsfunktionen, während der Rest besser in separaten Dateien und Klassen im src-Ordner organisiert wird. Zunächst genügt ein öffnendes PHP-Tag.
Wenn Sie PhpStorm verwenden, können Sie beim Schreiben eines Hook-Namens eine automatische Auswahl nutzen, die die Funktionsparameter für Sie einfügt:
Sie müssen „hook“ durch Ihren Modulnamen ersetzen. Drupal ruft die Funktion dann an den entsprechenden Stellen auf. Das Ergebnis sollte so aussehen:
/**
* Implements hook_form_alter().
*/
function drupalbook_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
}
In $form, $form_state und $form_id werden das Formular, dessen Status und die Formular-ID gespeichert, die wir in der getFormId() Methode definiert haben:
function getFormId() {
return 'drupalbook_admin_settings';
}
Alternativ können Sie die ID auch im HTML mit einem DOM-Inspector prüfen:
Dabei ersetzen Sie Bindestriche (-) durch Unterstriche (_). Die Formular-ID kann bei AJAX-Aufrufen mit Nummern enden (z. B. drupalbook-admin-settings-0), Sie brauchen aber nur den Basisnamen ohne Nummer.
Wir beschränken nun den Hook-Code auf dieses spezielle Formular, da hook_form_alter() für alle Formulare ausgeführt wird:
if ($form_id == 'drupalbook_admin_settings') {
// Unser Code hier.
}
Fügen wir für das API-Key-Formular Platzhalter in die Textfelder ein, damit Benutzer wissen, was sie eintragen sollen:
if ($form_id == 'drupalbook_admin_settings') {
$form['drupalbook_api_key']['#attributes']['placeholder'] = 'API key';
$form['drupalbook_api_client_id']['#attributes']['placeholder'] = 'API client ID';
}
Leeren Sie danach den Cache, damit die Änderungen wirksam werden.
Auch wenn #attributes nicht offiziell in der Dokumentation für Textfelder steht, funktioniert es, um HTML-Attribute wie class, placeholder etc. zu setzen.
Validierung: Wir wollen sicherstellen, dass der API-Key mit „google“ beginnt. Normalerweise schreibt man dazu eine validateForm() Methode im Formular, aber oft arbeiten wir mit Formularen aus Contrib-Modulen. Daher fügen wir eine eigene Validierungsfunktion per hook_form_alter() hinzu:
$form['#validate'][] = 'drupalbook_settings_validate';
Das #validate-Array enthält alle Callback-Funktionen, die Drupal zur Validierung des Formulars aufruft. Nun definieren wir die Funktion in drupalbook.module:
/**
* Custom validation callback.
*/
function drupalbook_settings_validate(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
}
Die eigentliche Validierung sieht so aus:
function drupalbook_settings_validate(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
if (strpos($form_state->getValue('drupalbook_api_key'), 'google') === FALSE) {
$form_state->setErrorByName('drupalbook_api_key', t('API Key must start from "google".'));
}
}
Wir verwenden den strengen Vergleich ===, da strpos() 0 zurückgibt, wenn „google“ am Anfang steht, und 0 == FALSE in PHP sonst true wäre.
Bei falscher Eingabe wird eine Fehlermeldung angezeigt und das Formular wird nicht abgesendet:
Submit: Nach erfolgreicher Validierung ruft Drupal die Submit-Funktionen auf. Wir hängen eine eigene Callback-Funktion an:
$form['#submit'][] = 'drupalbook_settings_submit';
Und definieren diese Funktion in drupalbook.module:
/**
* Custom submit callback.
*/
function drupalbook_settings_submit(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
\Drupal::messenger()->addStatus(
t(htmlentities('Insert API key in your <head> tag: <script src="https://example.com"></script>.'))
);
}
Wir verwenden htmlentities(), damit HTML-Tags als Text angezeigt werden und nicht ausgeführt werden.
Die Meldung erscheint nur, wenn keine Validierungsfehler vorliegen:
Früher wurde drupal_set_message() verwendet, heute ist der Messenger-Service Standard.
Hier der gesamte Code der drupalbook.module:
/**
* Implements hook_form_alter().
*/
function drupalbook_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
if ($form_id == 'drupalbook_admin_settings') {
$form['drupalbook_api_key']['#attributes']['placeholder'] = 'API key';
$form['drupalbook_api_client_id']['#attributes']['placeholder'] = 'API client ID';
$form['#validate'][] = 'drupalbook_settings_validate';
$form['#submit'][] = 'drupalbook_settings_submit';
}
}
/**
* Custom validation callback.
*/
function drupalbook_settings_validate(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
if (strpos($form_state->getValue('drupalbook_api_key'), 'google') === FALSE) {
$form_state->setErrorByName('drupalbook_api_key', t('API Key must start from "google".'));
}
}
/**
* Custom submit callback.
*/
function drupalbook_settings_submit(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
\Drupal::messenger()->addStatus(
t(htmlentities('Insert API key in your <head> tag: <script src="https://example.com"></script>.'))
);
}
Das war unsere Lektion zum Form API. Wir werden noch öfter Validierung, Submit und AJAX behandeln.
Codebeispiele gibt es auf GitHub:
https://github.com/levmyshkin/drupalbook8