Eigenständig verwendete Formulare
Nette Forms vereinfacht die Erstellung und Verarbeitung von Webformularen erheblich. Sie können sie in Ihren Anwendungen völlig eigenständig ohne den Rest des Frameworks verwenden, was wir in diesem Kapitel demonstrieren werden.
Wenn Sie jedoch Nette Application und Presenter verwenden, gibt es eine Anleitung für Sie: Formulare in Presentern.
Erstes Formular
Wir werden versuchen, ein einfaches Registrierungsformular zu schreiben. Sein Code wird wie folgt aussehen (vollständiger Code):
use Nette\Forms\Form;
$form = new Form;
$form->addText('name', 'Name:');
$form->addPassword('password', 'Password:');
$form->addSubmit('send', 'Sign up');
Und nun wollen wir es rendern:
$form->render();
und das Ergebnis sollte so aussehen:
Das Formular ist ein Objekt der Klasse Nette\Forms\Form
(die Klasse Nette\Application\UI\Form
wird in
Presentern verwendet). Wir haben die Steuerelemente Name, Kennwort und Schaltfläche „Senden“ hinzugefügt.
Nun werden wir das Formular erneut aufrufen. Indem wir $form->isSuccess()
befragen, finden wir heraus, ob das
Formular abgeschickt wurde und ob es gültig ausgefüllt wurde. Wenn ja, werden wir die Daten ausgeben. Nach der Definition des
Formulars werden wir hinzufügen:
if ($form->isSuccess()) {
echo 'Das Formular wurde korrekt ausgefüllt und übermittelt';
$data = $form->getValues();
// $data->name enthält Name
// $data->password enthält das Passwort
var_dump($data);
}
Die Methode getValues()
gibt die gesendeten Daten in Form eines Objekts ArrayHash zurück. Wir werden später zeigen, wie man dies ändern kann. Die Variable $data
enthält die
Schlüssel name
und password
mit den vom Benutzer eingegebenen Daten.
Normalerweise senden wir die Daten direkt zur weiteren Verarbeitung, die z.B. das Einfügen in die Datenbank sein kann. Bei der
Verarbeitung kann jedoch ein Fehler auftreten, z. B. wenn der Benutzername bereits vergeben ist. In diesem Fall geben wir den
Fehler mit addError()
an das Formular zurück und lassen es neu zeichnen, mit einer Fehlermeldung:
$form->addError('Sorry, username is already in use.');
Nach der Verarbeitung des Formulars leiten wir auf die nächste Seite weiter. Dadurch wird verhindert, dass das Formular durch Klicken auf die Schaltfläche Aktualisieren oder Zurück oder durch Verschieben des Browserverlaufs unbeabsichtigt erneut gesendet wird.
Standardmäßig wird das Formular mit der POST-Methode an dieselbe Seite gesendet. Beides kann geändert werden:
$form->setAction('/submit.php');
$form->setMethod('GET');
Und das ist alles :-) Wir haben ein funktionierendes und perfekt gesichertes Formular.
Versuchen Sie, weitere Formularsteuerelemente hinzuzufügen.
Zugang zu Steuerelementen
Das Formular und seine einzelnen Steuerelemente werden als Komponenten bezeichnet. Sie bilden einen Komponentenbaum, dessen Wurzel das Formular ist. Sie können auf die einzelnen Steuerelemente wie folgt zugreifen:
$input = $form->getComponent('name');
// alternative Syntax: $input = $form['name'];
$button = $form->getComponent('send');
// alternative Syntax: $button = $form['send'];
Steuerelemente werden mit unset entfernt:
unset($form['name']);
Überprüfungsregeln
Das Wort valid wurde hier verwendet, aber das Formular hat noch keine Validierungsregeln. Das müssen wir ändern.
Der Name wird obligatorisch sein, also werden wir ihn mit der Methode setRequired()
markieren, deren Argument der
Text der Fehlermeldung ist, die angezeigt wird, wenn der Benutzer ihn nicht ausfüllt. Wenn kein Argument angegeben wird, wird die
Standard-Fehlermeldung verwendet.
$form->addText('name', 'Name:')
->setRequired('Please enter a name.');
Versuchen Sie, das Formular abzuschicken, ohne den Namen auszufüllen, und Sie werden sehen, dass eine Fehlermeldung angezeigt wird und der Browser oder Server das Formular ablehnt, bis Sie es ausgefüllt haben.
Gleichzeitig können Sie das System nicht austricksen, indem Sie z. B. nur Leerzeichen in die Eingabe eintippen. Das geht nicht. Nette schneidet automatisch linke und rechte Leerzeichen ab. Probieren Sie es aus. Das sollten Sie bei jeder einzeiligen Eingabe immer tun, aber das wird oft vergessen. Nette macht es automatisch. (Sie können versuchen, die Formulare zu täuschen und eine mehrzeilige Zeichenkette als Namen zu senden. Auch hier lässt sich Nette nicht täuschen und die Zeilenumbrüche werden in Leerzeichen umgewandelt.)
Das Formular wird immer serverseitig validiert, aber es wird auch eine JavaScript-Validierung generiert, die schnell ist und
dem Benutzer den Fehler sofort anzeigt, ohne dass das Formular an den Server gesendet werden muss. Dies wird durch das Skript
netteForms.js
erledigt. Fügen Sie es in die Seite ein:
<script src="https://unpkg.com/nette-forms@3"></script>
Wenn Sie sich den Quellcode der Seite mit dem Formular ansehen, werden Sie feststellen, dass Nette die erforderlichen Felder in
Elemente mit einer CSS-Klasse required
einfügt. Versuchen Sie, der Vorlage den folgenden Stil hinzuzufügen, und die
Bezeichnung „Name“ wird rot sein. Auf elegante Weise markieren wir die erforderlichen Felder für die Benutzer:
<style>
.required label { color: maroon }
</style>
Zusätzliche Validierungsregeln werden mit der Methode addRule()
hinzugefügt. Der erste Parameter ist rule, der
zweite ist wieder der Text der Fehlermeldung, und das optionale Argument validation rule kann folgen. Was bedeutet das?
Das Formular erhält eine weitere optionale Eingabe Alter mit der Bedingung, dass es eine Zahl sein muss
(addInteger()
) und in bestimmten Grenzen ($form::Range
). Und hier werden wir das dritte Argument von
addRule()
verwenden, den Bereich selbst:
$form->addInteger('age', 'Age:')
->addRule($form::Range, 'You must be older 18 years and be under 120.', [18, 120]);
Wenn der Benutzer das Feld nicht ausfüllt, werden die Validierungsregeln nicht überprüft, da das Feld optional ist.
Offensichtlich ist Raum für ein kleines Refactoring vorhanden. In der Fehlermeldung und im dritten Parameter sind die Zahlen
doppelt aufgeführt, was nicht ideal ist. Wenn wir ein mehrsprachiges Formular
erstellen würden und die Meldung mit den Zahlen in mehrere Sprachen übersetzt werden müsste, würde dies die Änderung der
Werte erschweren. Aus diesem Grund können die Ersatzzeichen %d
verwendet werden:
->addRule($form::Range, 'You must be older %d years and be under %d.', [18, 120]);
Kehren wir zum Feld Kennwort zurück, machen es erforderlich und überprüfen die Mindestlänge des Kennworts
($form::MinLength
), wiederum unter Verwendung der Ersatzzeichen in der Nachricht:
$form->addPassword('password', 'Password:')
->setRequired('Pick a password')
->addRule($form::MinLength, 'Your password has to be at least %d long', 8);
Wir fügen dem Formular ein Feld passwordVerify
hinzu, in das der Benutzer das Kennwort erneut eingibt, um es zu
überprüfen. Mit Hilfe von Validierungsregeln überprüfen wir, ob beide Passwörter gleich sind ($form::Equal
). Und
als Argument geben wir in eckigen Klammern einen Verweis auf das erste Kennwort an:
$form->addPassword('passwordVerify', 'Password again:')
->setRequired('Fill your password again to check for typo')
->addRule($form::Equal, 'Password mismatch', $form['password'])
->setOmitted();
Mit setOmitted()
markieren wir ein Element, dessen Wert uns nicht wirklich interessiert und das nur zur
Validierung existiert. Sein Wert wird nicht an $data
übergeben.
Wir haben ein voll funktionsfähiges Formular mit Validierung in PHP und JavaScript. Die Validierungsmöglichkeiten von Nette sind viel umfangreicher, Sie können Bedingungen erstellen, Teile einer Seite entsprechend anzeigen und ausblenden usw. Sie können alles im Kapitel über Formularvalidierung nachlesen.
Standardwerte
Wir setzen oft Standardwerte für Formularsteuerelemente:
$form->addEmail('email', 'Email')
->setDefaultValue($lastUsedEmail);
Oft ist es sinnvoll, Standardwerte für alle Steuerelemente gleichzeitig festzulegen. Zum Beispiel, wenn das Formular verwendet wird, um Datensätze zu bearbeiten. Wir lesen den Datensatz aus der Datenbank und legen ihn als Standardwerte fest:
//$row = ['name' => 'John', 'age' => '33', /* ... */];
$form->setDefaults($row);
Rufen Sie setDefaults()
auf, nachdem Sie die Steuerelemente definiert haben.
Rendering des Formulars
Standardmäßig wird das Formular als Tabelle gerendert. Die einzelnen Steuerelemente entsprechen den grundlegenden Richtlinien
für die Barrierefreiheit im Web. Alle Beschriftungen werden als <label>
Elemente generiert und sind mit ihren
Eingaben verknüpft; ein Klick auf die Beschriftung bewegt den Cursor auf die Eingabe.
Wir können für jedes Element beliebige HTML-Attribute festlegen. Fügen Sie zum Beispiel einen Platzhalter hinzu:
$form->addInteger('age', 'Age:')
->setHtmlAttribute('placeholder', 'Please fill in the age');
Es gibt wirklich viele Möglichkeiten, ein Formular zu rendern, daher ist dieses Kapitel dem Rendering gewidmet.
Mapping auf Klassen
Kehren wir zur Verarbeitung der Formulardaten zurück. Die Methode getValues()
gibt die übermittelten Daten als
ArrayHash
Objekt zurück. Da es sich hierbei um eine generische Klasse handelt, ähnlich wie stdClass
,
fehlen uns einige Annehmlichkeiten bei der Arbeit damit, wie z. B. die Codevervollständigung für Eigenschaften in Editoren oder
die statische Codeanalyse. Dies könnte durch eine spezifische Klasse für jedes Formular gelöst werden, deren Eigenschaften die
einzelnen Steuerelemente darstellen. Z.B.:
class RegistrationFormData
{
public string $name;
public int $age;
public string $password;
}
Alternativ können Sie auch den Konstruktor verwenden:
class RegistrationFormData
{
public function __construct(
public string $name,
public int $age,
public string $password,
) {
}
}
Eigenschaften der Datenklasse können auch Enums sein und werden automatisch zugeordnet.
Wie kann man Nette anweisen, Daten als Objekte dieser Klasse zurückzugeben? Einfacher als Sie denken. Alles, was Sie tun müssen, ist, den Namen der Klasse oder des zu hydrierenden Objekts als Parameter anzugeben:
$data = $form->getValues(RegistrationFormData::class);
$name = $data->name;
Ein 'array'
kann ebenfalls als Parameter angegeben werden, und die Daten werden dann als Array zurückgegeben.
Wenn die Formulare aus einer mehrstufigen Struktur bestehen, die sich aus Containern zusammensetzt, erstellen Sie für jeden Container eine eigene Klasse:
$form = new Form;
$person = $form->addContainer('person');
$person->addText('firstName');
/* ... */
class PersonFormData
{
public string $firstName;
public string $lastName;
}
class RegistrationFormData
{
public PersonFormData $person;
public int $age;
public string $password;
}
Das Mapping weiß dann anhand des Eigenschaftstyps $person
, dass es den Container auf die Klasse
PersonFormData
abbilden soll. Wenn die Eigenschaft ein Array von Containern enthalten würde, geben Sie den Typ
array
an und übergeben Sie die Klasse, die direkt auf den Container abgebildet werden soll:
$person->setMappedType(PersonFormData::class);
Mit der Methode Nette\Forms\Blueprint::dataClass($form)
können Sie einen Vorschlag für die
Datenklasse eines Formulars erzeugen, der auf der Browserseite ausgedruckt wird. Sie können dann einfach auf den Code klicken, um
ihn auszuwählen und in Ihr Projekt zu kopieren.
Mehrere Submit-Buttons
Wenn das Formular mehr als eine Schaltfläche hat, müssen wir normalerweise unterscheiden, welche Schaltfläche gedrückt
wurde. Die Methode isSubmittedBy()
der Schaltfläche gibt uns diese Information zurück:
$form->addSubmit('save', 'Speichern');
$form->addSubmit('löschen', 'Löschen');
if ($form->isSuccess()) {
if ($form['save']->isSubmittedBy()) {
// ...
}
if ($form['delete']->isSubmittedBy()) {
// ...
}
}
Lassen Sie die $form->isSuccess()
nicht aus, um die Gültigkeit der Daten zu überprüfen.
Wenn ein Formular mit der Eingabetaste abgeschickt wird, wird es so behandelt, als ob es mit der ersten Schaltfläche abgeschickt worden wäre.
Schutz vor Schwachstellen
Nette Framework gibt sich große Mühe, sicher zu sein, und da Formulare die häufigste Benutzereingabe sind, sind Nette-Formulare so gut wie unangreifbar.
Zusätzlich zum Schutz der Formulare gegen bekannte Schwachstellen wie Cross-Site Scripting (XSS) und Cross-Site Request Forgery (CSRF ) übernimmt es viele kleine Sicherheitsaufgaben, an die Sie nicht mehr denken müssen.
So filtert es beispielsweise alle Steuerzeichen aus den Eingaben heraus und überprüft die Gültigkeit der UTF-8-Kodierung, so dass die Daten aus dem Formular immer sauber sind. Bei Auswahlfeldern und Auswahllisten wird überprüft, ob die ausgewählten Elemente tatsächlich aus den angebotenen ausgewählt wurden und keine Fälschung vorliegt. Wie bereits erwähnt, werden bei einzeiligen Texteingaben Zeichen am Zeilenende entfernt, die ein Angreifer dort einfügen könnte. Bei mehrzeiligen Eingaben werden die Zeichen am Zeilenende normalisiert. Und so weiter.
Nette behebt für Sie Sicherheitslücken, von deren Existenz die meisten Programmierer keine Ahnung haben.
Der erwähnte CSRF-Angriff besteht darin, dass ein Angreifer das Opfer dazu verleitet, eine Seite zu besuchen, die im Browser des Opfers unbemerkt eine Anfrage an den Server ausführt, bei dem das Opfer gerade angemeldet ist, und der Server glaubt, dass die Anfrage vom Opfer absichtlich gestellt wurde. Daher verhindert Nette, dass das Formular per POST von einer anderen Domäne übermittelt wird. Wenn Sie aus irgendeinem Grund den Schutz ausschalten und die Übermittlung des Formulars von einer anderen Domäne aus zulassen möchten, verwenden Sie:
$form->allowCrossOrigin(); // ACHTUNG! Schaltet den Schutz aus!
Dieser Schutz verwendet ein SameSite-Cookie namens _nss
. Erstellen Sie daher ein Formular, bevor Sie die erste
Ausgabe spülen, damit das Cookie gesendet werden kann.
Der SameSite-Cookie-Schutz ist möglicherweise nicht zu 100 % zuverlässig, weshalb es ratsam ist, den Token-Schutz zu aktivieren:
$form->addProtection();
Es wird dringend empfohlen, diesen Schutz auf die Formulare in einem administrativen Teil Ihrer Anwendung anzuwenden, in dem
sensible Daten geändert werden. Das Framework schützt vor einem CSRF-Angriff, indem es ein Authentifizierungs-Token generiert
und validiert, das in einer Session gespeichert wird (das Argument ist die Fehlermeldung, die angezeigt wird, wenn das Token
abgelaufen ist). Aus diesem Grund muss vor der Anzeige des Formulars eine Session gestartet werden. Im Verwaltungsteil der Website
ist die Session in der Regel bereits durch die Anmeldung des Benutzers gestartet. Ansonsten starten Sie die Session mit der
Methode Nette\Http\Session::start()
.
So, das war eine kurze Einführung in Formulare in Nette. Schauen Sie in das Verzeichnis der Beispiele in der Distribution, um weitere Anregungen zu erhalten.