9.8.1. hook_form_alter() voeg submit- en validatiefuncties toe aan bestaand formulier.
In een van de vorige lessen hebben we geleerd wat hooks zijn. In deze les gaan we praktisch aan de slag met hook_form_alter() hooks en voegen we functionaliteit toe aan het bestaande formulier.
Voorbeelden van de code kunnen worden gevonden op github:
https://github.com/levmyshkin/drupalbook8
In deze les gaan we de hooks in de praktijk bekijken, later zullen we terugkomen op hooks en een paar andere voorbeelden behandelen. Laten we nu beginnen met hook_form_alter().
Om een hook aan de module toe te voegen, moeten we een bestand maken met de naam MODULENAME.module, dus we maken een bestand aan genaamd drupalbook.module. Dit wordt een PHP-bestand waarin onze hooks en hulpfuncties worden opgeslagen, voor de rest kun je het beste aparte bestanden en klassen in de src-map gebruiken. Tot nu toe kun je alleen de openingstag aan het bestand toevoegen.
Laten we nu hook_form_alter() toevoegen. Als je PhpStorm gebruikt, begin dan de naam van de hooks te typen en PhpStorm biedt je aan een van de hooks te kiezen. Wanneer je op deze manier een hook selecteert, voegt PhpStorm automatisch de argumenten in de functie in, zodat je niet hoeft te onthouden of in de documentatie te kijken welke argumenten je moet toevoegen:
Wanneer je een hook wilt toevoegen, moet je het woord hook vervangen door de naam van je module, en vervolgens zal Drupal automatisch je code op de juiste plaats van de hooks invoegen. Het resultaat zou deze functie moeten zijn:
/**
* Implementeert hook_form_alter().
*/
function drupalbook_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
}
In de laatste les hebben we al besproken wat er in $form, $form_state wordt opgeslagen, in $form_id moeten we de formulier-id opslaan, die we in de juiste methode hebben gedefinieerd:
9.8. Werken met formulieren in Drupal. Voeg een configuratieformulier programmatically toe.
Laten we nu het formulier voor het toevoegen van API-sleutels uitbreiden, zoals we in de laatste les deden. Hiervoor moeten we de formulier-id definiëren, die je in deze methode kunt zien:
function getFormId() {
return 'drupalbook_admin_settings';
}
Of open de DOM-inspecteur en kijk naar het id-attribuut in de HTML:
Hier moeten we - (koppelteken) vervangen door _ (onderstreepjes). Maar wees voorzichtig, het id in het formulier-tagattribuut kan toenemen na een AJAX-aanroep, bijvoorbeeld drupalbook-admin-settings-0, drupalbook-admin-settings-1, drupalbook-admin-settings-2, enzovoort. We hebben een deel zonder een nummer aan het einde nodig, dit is het deel dat gevormd wordt uit de formulier-id, die is opgegeven in de getFormId() methode.
Nu moeten we de uitvoering van onze code beperken tot alleen het formulier drupalbook_admin_settings, omdat de code in hook_form_alter() wordt uitgevoerd voor absoluut alle formulieren die via de Drupal Form API worden gegenereerd:
($form_id == 'drupalbook_admin_settings') {
// Verdere code hier.
}
Binnen de if kunnen we onze code voor het API-sleutelformulier schrijven. Laten we placeholders toevoegen voor de tekstvelden zodat lege velden laten zien wat ingevuld moet worden:
($form_id == 'drupalbook_admin_settings') {
$form['drupalbook_api_key']['#attributes']['placeholder'] = 'API key';
$form['drupalbook_api_client_id']['#attributes']['placeholder'] = 'API client ID';
}
Om hook_form_alter() toe te passen, moet je de cache wissen.
Hoewel #attributes niet in de documentatie wordt vermeld als een mogelijke sleutel voor de tekstveld:
Kan deze sleutel nog steeds worden gebruikt om de attributen van de tags te specificeren: class, placeholder, verschillende rel-attributen. Een duidelijkere tabel is voor Drupal 7 Form API:
https://api.drupal.org/api/drupal/developer%21topics%21forms_api_reference.html/7.x
Je kunt #attributes hier vinden voor tekstveldvelden.
De attribuutwaarden worden ingesteld in de Form API bij het genereren (renderen) van een formelement in de setAttributes-methode:
Hoewel dit niet in de API-documentatie voor formulieren wordt vermeld, kun je zelf zien dat het werkt.
Je kunt ook zien welke andere waarden je kunt instellen voor de formuliervelden in de basisklasse FormElement:
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21Element%21FormElement.php/class/FormElement/8.2.x
De Drupal Form API lijkt onduidelijk en verwarrend, dus dat is het ook, ik begrijp nog steeds niet sommige dingen erin. Maar om deze API te gebruiken is het eenvoudig genoeg en duidelijk genoeg, als je een werkend voorbeeld bij de hand hebt. Daarom, als je vragen hebt en je weet niet hoe je iets in dit formulier moet doen, zoek dan op internet en je vindt een 100% werkend voorbeeld. Je moet weten dat alle formulieren door alle APIs van Drupal Cache, Render, Theming gaan en elk veld in het formulier kun je veranderen via hook_form_alter(), en je kunt nu al uitvinden hoe je dat doet via Google. Ook hoe vaker je met de Form API werkt en hoe meer je de voorbeelden ermee ontleedt, hoe gemakkelijker en duidelijker deze Form API voor jou zal worden. Maak je geen zorgen dat het zo groot is, gebruik gewoon alleen het gedeelte dat je nu nodig hebt.
Valideren
Laten we nu kijken hoe we validatiefuncties kunnen gebruiken via de Form API. Laten we het Key API-veld controleren zodat het begint met het woord google, bijvoorbeeld google-KEY123a3sa. Als we de code voor ons formulier aanvankelijk zouden hebben geschreven, zouden we de validateForm() methode kunnen invoegen en alle controles daarin kunnen doen:
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
}
Maar vaak bevinden de formulieren waarmee we werken zich in de bijgedragen modules en is het beter om code voor dergelijke formulieren in aangepaste modules te schrijven. Laten we dus een nieuwe validatiefunctie aan het formulier toevoegen:
['#validate'][] = 'drupalbook_settings_validate';
In de #validate-array slaan we alle callback's en validatiefuncties op. Callback is een oproep van een functie op basis van de naam, dat wil zeggen, we voegen de array ['callback_function1', 'callback_function2'] toe en nemen vervolgens deze namen uit de array en drupal zal deze functies aanroepen om ons formulier te controleren. En nu moeten we de functie drupalbook_settings_validate maken in het bestand drupalbook.module. In deze functie hebben we de parameters $form, $form_state:
/**
* Aangepaste validatie callback.
*/
function drupalbook_settings_validate(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
}
En nu voegen we de controle voor het API Key-veld zelf toe:
/**
* Aangepaste validatie 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".'));
}
}
We gebruiken de hardvergelijkingsoperator ===, omdat als google aan het begin van de string staat, strpos() 0 retourneert en voor PHP (0 == FALSE) TRUE zal retourneren, omdat in PHP '', 0, NULL, FALSE allemaal lege waarden zijn die gelijk zijn bij een simpele vergelijking ==.
Nu wordt elke keer wanneer je het formulier voor de instellingen opslaat, een controle uitgevoerd, en als de controle niet wordt doorstaan, zal Drupal een fout genereren en worden de instellingen niet opgeslagen:
Verzenden
Nadat alle validatiefuncties zijn uitgevoerd en er geen fouten zijn opgetreden, roept Drupal de verzendfuncties aan, die werken nadat de gegevens van het formulier zijn verzonden. Je hebt de methode voor submitForm() al gezien in de laatste les, waar we de gegevens in configuraties opslaan. Maar we kunnen andere acties uitvoeren bij verzending, zoals gegevens wijzigen of gegevens opslaan in andere entiteiten. Laten we een andere validatiefunctie maken die een extra bericht weergeeft over hoe de API Key te gebruiken. In hook_form_alter() voegen we een functienaam toe:
['#submit'][] = 'drupalbook_settings_submit';
Nu, in de functie drupalbook_settings_settings_submit(), waar we ook $form, $form_state als parameters doorgeven, tonen we het bericht.
/**
* Aangepaste verzend callback.
*/
function drupalbook_settings_submit(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
\Drupal::messenger()->addStatus(
t(htmlentities('Insert API key in your tag: .'))
);
}
We gebruiken de htmlentites() functie om ervoor te zorgen dat alle speciale tekens die we gebruiken om de "<", "/", ">" tags weer te geven, niet door Drupal worden verwijderd. Alle HTML-tekst wordt uit de t() functie gesneden, zodat het niet in de tekst kan worden geplakt, bijvoorbeeld als dergelijk code een javascript-omleiding naar een andere site bevat, wordt deze omgeleid bij het weergeven van berichten. Daarom gebruiken we de htmlentities functie om de tags weer te geven.
Het bericht wordt niet weergegeven als de validatiefuncties niet slagen, alleen als er geen fouten in het formulier zijn, zien we het bericht.
Voorheen werd de drupal_set_message functie gebruikt in Drupal om berichten weer te geven:
(t('An error occurred and processing did not complete.'));
Maar nu probeert iedereen de OOP-aanpak overal te standaardiseren.
Hieronder staat de volledige huidige code van het drupalbook.module bestand:
/**
* Implementeert 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';
}
}
/**
* Aangepaste validatie 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".'));
}
}
/**
* Aangepaste verzend callback.
*/
function drupalbook_settings_submit(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
// drupal_set_message is deprecated
// drupal_set_message(t('An error occurred and processing did not complete.'));
\Drupal::messenger()->addStatus(
t(htmlentities('Insert API key in your tag: .'))
);
}
Dit is het einde van onze les over de Form API, maar het einde van het bestuderen van de Form API is het nog niet, we zullen nog vaak functies van validate, submit en verschillende velden tegenkomen. We zullen ook in een van de lessen begrijpen hoe we AJAX en form_states via de Form API kunnen gebruiken.
Voorbeelden van de code kunnen worden gevonden op github:
https://github.com/levmyshkin/drupalbook8