Formularvalidierung

Pflichtelemente

Pflichtelemente markieren wir mit der Methode setRequired(), deren Argument der Text der Fehlermeldungen ist, die angezeigt wird, wenn der Benutzer das Element nicht ausfüllt. Wenn kein Argument angegeben wird, wird die Standardfehlermeldung verwendet.

$form->addText('name', 'Name:')
	->setRequired('Bitte geben Sie einen Namen ein');

Regeln

Validierungsregeln fügen wir Elementen mit der Methode addRule() hinzu. Der erste Parameter ist die Regel, der zweite ist der Text der Fehlermeldungen und der dritte ist das Argument der Validierungsregel.

$form->addPassword('password', 'Passwort:')
	->addRule($form::MinLength, 'Das Passwort muss mindestens %d Zeichen lang sein', 8);

Validierungsregeln werden nur überprüft, wenn der Benutzer das Element ausgefüllt hat.

Nette bringt eine ganze Reihe vordefinierter Regeln mit, deren Namen Konstanten der Klasse Nette\Forms\Form sind. Für alle Elemente können wir diese Regeln verwenden:

Konstante Beschreibung Argumenttyp
Required Pflichtelement, Alias für setRequired()
Filled Pflichtelement, Alias für setRequired()
Blank Element darf nicht ausgefüllt sein
Equal Wert ist gleich dem Parameter mixed
NotEqual Wert ist nicht gleich dem Parameter mixed
IsIn Wert ist gleich einem Element im Array array
IsNotIn Wert ist keinem Element im Array gleich array
Valid Ist das Element korrekt ausgefüllt? (für Bedingungen)

Texteingaben

Für die Elemente addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat() können auch einige der folgenden Regeln verwendet werden:

MinLength Minimale Textlänge int
MaxLength Maximale Textlänge int
Length Länge im Bereich oder genaue Länge Paar [int, int] oder int
Email Gültige E-Mail-Adresse
URL Absolute URL
Pattern Entspricht dem regulären Ausdruck string
PatternInsensitive Wie Pattern, aber Groß-/Kleinschreibung egal string
Integer Ganzzahliger Wert
Numeric Alias für Integer
Float Zahl
Min Minimalwert des numerischen Elements int|float
Max Maximalwert des numerischen Elements int|float
Range Wert im Bereich Paar [int|float, int|float]

Die Validierungsregeln Integer, Numeric und Float konvertieren den Wert direkt in Integer bzw. Float. Des Weiteren akzeptiert die Regel URL auch Adressen ohne Schema (z. B. nette.org) und ergänzt das Schema (https://nette.org). Der Ausdruck in Pattern und PatternIcase muss für den gesamten Wert gelten, d.h. als ob er von den Zeichen ^ und $ umschlossen wäre.

Anzahl der Elemente

Für die Elemente addMultiUpload(), addCheckboxList(), addMultiSelect() können auch die folgenden Regeln zur Begrenzung der Anzahl ausgewählter Elemente bzw. hochgeladener Dateien verwendet werden:

MinLength Minimale Anzahl int
MaxLength Maximale Anzahl int
Length Anzahl im Bereich oder genaue Anzahl Paar [int, int] oder int

Datei-Uploads

Für die Elemente addUpload(), addMultiUpload() können auch die folgenden Regeln verwendet werden:

MaxFileSize Maximale Dateigröße in Bytes int
MimeType MIME-Typ, Platzhalter erlaubt ('video/*') string|string[]
Image Bild JPEG, PNG, GIF, WebP, AVIF
Pattern Dateiname entspricht dem regulären Ausdruck string
PatternInsensitive Wie Pattern, aber Groß-/Kleinschreibung egal string

MimeType und Image erfordern die PHP-Erweiterung fileinfo. Ob es sich um eine Datei oder ein Bild des gewünschten Typs handelt, wird anhand seiner Signatur erkannt und die Integrität der gesamten Datei wird nicht überprüft. Ob ein Bild beschädigt ist, kann beispielsweise durch den Versuch, es zu Laden, festgestellt werden.

Fehlermeldungen

Alle vordefinierten Regeln außer Pattern und PatternInsensitive haben eine Standardfehlermeldung, sodass sie weggelassen werden kann. Durch die Angabe und Formulierung aller Meldungen nach Maß machen Sie das Formular jedoch benutzerfreundlicher.

Die Standardmeldungen können Sie in der Konfiguration ändern, indem Sie die Texte im Array Nette\Forms\Validator::$messages bearbeiten oder einen Übersetzer verwenden.

Im Text der Fehlermeldungen können diese Platzhalter verwendet werden:

%d Ersetzt nacheinander durch die Argumente der Regel
%n$d Ersetzt durch das n-te Argument der Regel
%label Ersetzt durch die Beschriftung des Elements (ohne Doppelpunkt)
%name Ersetzt durch den Namen des Elements (z.B. name)
%value Ersetzt durch den vom Benutzer eingegebenen Wert
$form->addText('name', 'Name:')
	->setRequired('Bitte füllen Sie %label aus');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'mindestens %d und höchstens %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'höchstens %2$d und mindestens %1$d', [5, 10]);

Bedingungen

Neben Regeln können auch Bedingungen hinzugefügt werden. Diese werden ähnlich wie Regeln geschrieben, nur verwenden wir statt addRule() die Methode addCondition() und geben natürlich keine Fehlermeldung an (die Bedingung fragt nur):

$form->addPassword('password', 'Passwort:')
	// wenn das Passwort nicht länger als 8 Zeichen ist
	->addCondition($form::MaxLength, 8)
		// dann muss es eine Ziffer enthalten
		->addRule($form::Pattern, 'Muss eine Ziffer enthalten', '.*[0-9].*');

Eine Bedingung kann auch an ein anderes Element als das aktuelle gebunden werden, indem addConditionOn() verwendet wird. Als ersten Parameter geben wir eine Referenz auf das Element an. In diesem Beispiel wird die E-Mail nur dann erforderlich sein, wenn die Checkbox angekreuzt ist (ihr Wert wird true sein):

$form->addCheckbox('newsletters', 'Senden Sie mir Newsletter');

$form->addEmail('email', 'E-Mail:')
	// wenn die Checkbox angekreuzt ist
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// dann fordere E-Mail an
		->setRequired('Geben Sie eine E-Mail-Adresse ein');

Aus Bedingungen können komplexe Strukturen mithilfe von elseCondition() und endCondition() erstellt werden:

$form->addText(/* ... */)
	->addCondition(/* ... */) // wenn die erste Bedingung erfüllt ist
		->addConditionOn(/* ... */) // und die zweite Bedingung an einem anderen Element
			->addRule(/* ... */) // fordere diese Regel an
		->elseCondition() // wenn die zweite Bedingung nicht erfüllt ist
			->addRule(/* ... */) // fordere diese Regeln an
			->addRule(/* ... */)
		->endCondition() // wir kehren zur ersten Bedingung zurück
		->addRule(/* ... */);

In Nette ist es sehr einfach, auf die Erfüllung oder Nichterfüllung einer Bedingung auch auf der JavaScript-Seite mit der Methode toggle() zu reagieren, siehe Dynamisches JavaScript.

Referenz auf ein anderes Element

Als Argument einer Regel oder Bedingung kann auch ein anderes Formularelement übergeben werden. Die Regel verwendet dann den Wert, der später vom Benutzer im Browser eingegeben wird. So kann z. B. dynamisch validiert werden, dass das Element password denselben String enthält wie das Element password_confirm:

$form->addPassword('password', 'Passwort');
$form->addPassword('password_confirm', 'Passwort bestätigen')
    ->addRule($form::Equal, 'Die eingegebenen Passwörter stimmen nicht überein', $form['password']);

Benutzerdefinierte Regeln und Bedingungen

Manchmal geraten wir in eine Situation, in der die eingebauten Validierungsregeln in Nette nicht ausreichen und wir die Benutzerdaten auf unsere eigene Weise validieren müssen. In Nette ist das sehr einfach!

Den Methoden addRule() oder addCondition() kann als erster Parameter ein beliebiger Callback übergeben werden. Dieser erhält als ersten Parameter das Element selbst und gibt einen booleschen Wert zurück, der angibt, ob die Validierung erfolgreich war. Beim Hinzufügen einer Regel mit addRule() können auch weitere Argumente angegeben werden, die dann als zweiter Parameter übergeben werden.

Einen eigenen Satz von Validatoren können wir als Klasse mit statischen Methoden erstellen:

class MyValidators
{
	// testet, ob der Wert durch das Argument teilbar ist
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// weitere Validatoren
	}
}

Die Verwendung ist dann sehr einfach:

$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'Der Wert muss ein Vielfaches von %d sein',
		8,
	);

Benutzerdefinierte Validierungsregeln können auch zu JavaScript hinzugefügt werden. Voraussetzung ist, dass die Regel eine statische Methode ist. Ihr Name für den JavaScript-Validator wird durch Verkettung des Klassennamens ohne Backslashes \, eines Unterstrichs _ und des Methodennamens gebildet. Z. B. schreiben wir App\MyValidators::validateDivisibility als AppMyValidators_validateDivisibility und fügen es zum Objekt Nette.validators hinzu:

Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
	return val % args === 0;
};

Ereignis onValidate

Nach dem Absenden des Formulars wird die Validierung durchgeführt, bei der die einzelnen mit addRule() hinzugefügten Regeln überprüft werden und anschließend das Ereignis onValidate ausgelöst wird. Sein Handler kann für zusätzliche Validierungen verwendet werden, typischerweise zur Überprüfung der korrekten Kombination von Werten in mehreren Formularelementen.

Wenn ein Fehler entdeckt wird, übergeben wir ihn mit der Methode addError() an das Formular. Diese kann entweder auf einem bestimmten Element oder direkt auf dem Formular aufgerufen werden.

protected function createComponentSignInForm(): Form
{
	$form = new Form;
	// ...
	$form->onValidate[] = [$this, 'validateSignInForm'];
	return $form;
}

public function validateSignInForm(Form $form, \stdClass $data): void
{
	if ($data->foo > 1 && $data->bar > 5) {
		$form->addError('Diese Kombination ist nicht möglich.');
	}
}

Fehler bei der Verarbeitung

In vielen Fällen erfahren wir erst von einem Fehler, wenn wir das gültige Formular verarbeiten, z. B. wenn wir einen neuen Eintrag in die Datenbank schreiben und auf einen doppelten Schlüssel stoßen. In diesem Fall übergeben wir den Fehler erneut mit der Methode addError() an das Formular. Diese kann entweder auf einem bestimmten Element oder direkt auf dem Formular aufgerufen werden:

try {
	$data = $form->getValues();
	$this->user->login($data->username, $data->password);
	$this->redirect('Home:');

} catch (Nette\Security\AuthenticationException $e) {
	if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) {
		$form->addError('Ungültiges Passwort.');
	}
}

Wenn möglich, empfehlen wir, den Fehler direkt an das Formularelement anzuhängen, da er bei Verwendung des Standard-Renderers daneben angezeigt wird.

$form['date']->addError('Entschuldigung, aber dieses Datum ist bereits vergeben.');

Sie können addError() wiederholt aufrufen und so dem Formular oder Element mehrere Fehlermeldungen übergeben. Sie erhalten sie mit getErrors().

Achtung, $form->getErrors() gibt eine Zusammenfassung aller Fehlermeldungen zurück, auch derjenigen, die direkt an einzelne Elemente übergeben wurden, nicht nur direkt an das Formular. Fehlermeldungen, die nur an das Formular übergeben wurden, erhalten Sie über $form->getOwnErrors().

Anpassung der Eingabe

Mit der Methode addFilter() können wir den vom Benutzer eingegebenen Wert ändern. In diesem Beispiel tolerieren und entfernen wir Leerzeichen in der Postleitzahl:

$form->addText('zip', 'PLZ:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // entfernen Leerzeichen aus der PLZ
	})
	->addRule($form::Pattern, 'PLZ ist nicht im Format von fünf Ziffern', '\d{5}');

Der Filter wird zwischen Validierungsregeln und Bedingungen eingefügt, daher hängt es von der Reihenfolge der Methoden ab, d. h. Filter und Regel werden in der Reihenfolge aufgerufen, in der die Methoden addFilter() und addRule() stehen.

JavaScript-Validierung

Die Sprache zur Formulierung von Bedingungen und Regeln ist sehr mächtig. Alle Konstrukte funktionieren dabei sowohl serverseitig als auch clientseitig in JavaScript. Sie werden in HTML-Attributen data-nette-rules als JSON übertragen. Die eigentliche Validierung führt dann ein Skript durch, das das submit-Ereignis des Formulars abfängt, die einzelnen Elemente durchläuft und die entsprechende Validierung durchführt.

Dieses Skript ist netteForms.js und ist aus mehreren möglichen Quellen verfügbar:

Sie können das Skript direkt von einem CDN in die HTML-Seite einbinden:

<script src="https://unpkg.com/nette-forms@3"></script>

Oder lokal in den öffentlichen Ordner des Projekts kopieren (z. B. aus vendor/nette/forms/src/assets/netteForms.min.js):

<script src="/path/to/netteForms.min.js"></script>

Oder über npm installieren:

npm install nette-forms

Und anschließend laden und starten:

import netteForms from 'nette-forms';
netteForms.initOnLoad();

Alternativ können Sie es direkt aus dem vendor-Ordner laden:

import netteForms from '../path/to/vendor/nette/forms/src/assets/netteForms.js';
netteForms.initOnLoad();

Dynamisches JavaScript

Möchten Sie die Felder zur Eingabe der Adresse nur anzeigen, wenn der Benutzer den Versand per Post wählt? Kein Problem. Der Schlüssel ist das Methodenpaar addCondition() & toggle():

$form->addCheckbox('send_it')
	->addCondition($form::Equal, true)
		->toggle('#address-container');

Dieser Code besagt, dass, wenn die Bedingung erfüllt ist, d. h. wenn die Checkbox angekreuzt ist, das HTML-Element #address-container sichtbar sein wird. Und umgekehrt. Die Formularelemente mit der Empfängeradresse platzieren wir also in einem Container mit dieser ID, und beim Klicken auf die Checkbox werden sie ausgeblendet oder angezeigt. Dafür sorgt das Skript netteForms.js.

Als Argument der Methode toggle() kann ein beliebiger Selektor übergeben werden. Aus historischen Gründen wird ein alphanumerischer String ohne weitere Sonderzeichen als ID des Elements verstanden, also genauso, als ob ihm das Zeichen # vorangestellt wäre. Der zweite optionale Parameter ermöglicht es, das Verhalten umzukehren, d. h. wenn wir toggle('#address-container', false) verwenden würden, würde das Element umgekehrt nur dann angezeigt, wenn die Checkbox nicht angekreuzt wäre.

Die Standardimplementierung in JavaScript ändert die hidden-Eigenschaft der Elemente. Das Verhalten können wir jedoch leicht ändern, z. B. eine Animation hinzufügen. Es genügt, die Methode Nette.toggle in JavaScript durch eine eigene Lösung zu überschreiben:

Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// Blenden Sie 'el' entsprechend dem Wert von 'visible' ein oder aus
	});
};

Validierung deaktivieren

Manchmal kann es nützlich sein, die Validierung zu deaktivieren. Wenn das Drücken eines Sende-Buttons keine Validierung durchführen soll (geeignet für Abbrechen- oder Vorschau-Buttons), deaktivieren wir sie mit der Methode $submit->setValidationScope([]). Wenn nur eine teilweise Validierung durchgeführt werden soll, können wir festlegen, welche Felder oder Formularcontainer validiert werden sollen.

$form->addText('name')
	->setRequired();

$details = $form->addContainer('details');
$details->addInteger('age')
	->setRequired('age');
$details->addInteger('age2')
	->setRequired('age2');

$form->addSubmit('send1'); // Validiert das gesamte Formular
$form->addSubmit('send2')
	->setValidationScope([]); // Validiert überhaupt nicht
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Validiert nur das Element name
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Validiert nur das Element age
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Validiert den Container details

setValidationScope beeinflusst nicht das Ereignis onValidate des Formulars, das immer aufgerufen wird. Das onValidate-Ereignis eines Containers wird nur ausgelöst, wenn dieser Container für die Teilvalidierung markiert ist.

Version: 4.0