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.