Önállóan használt űrlapok
A Nette Forms jelentősen megkönnyíti a webes űrlapok létrehozását és feldolgozását. Alkalmazásaiban teljesen önállóan, a keretrendszer többi része nélkül is használhatja őket, amit ebben a fejezetben bemutatunk.
Ha azonban Nette alkalmazást és prezentereket használ, akkor van egy útmutató az Ön számára: Forms in presenters.
Első űrlap
Megpróbálunk írni egy egyszerű regisztrációs űrlapot. A kódja így fog kinézni (teljes kód):
use Nette\Forms\Form;
$form = new Form;
$form->addText('name', 'Name:');
$form->addPassword('password', 'Password:');
$form->addSubmit('send', 'Sign up');
És rendereljük le:
$form->render();
Az eredménynek így kell kinéznie:
Az űrlap a Nette\Forms\Form
osztály objektuma (a Nette\Application\UI\Form
osztály a
prezenterekben használatos). Hozzáadtuk a vezérlőelemeket név, jelszó és küldés gomb.
Most újraélesztjük az űrlapot. A $form->isSuccess()
megkérdezésével megtudjuk, hogy az űrlapot
elküldtük-e, és hogy érvényesen töltöttük-e ki. Ha igen, akkor ki fogjuk dobni az adatokat. Az űrlap definíciója után
hozzáadjuk:
if ($form->isSuccess()) {
echo 'Az űrlapot helyesen töltöttük ki és küldtük el';
$data = $form->getValues();
// $data->name tartalmazza a nevet
// $data->password tartalmazza a jelszót
var_dump($data);
}
A getValues()
metódus az elküldött adatokat egy ArrayHash objektum formájában adja vissza. Később megmutatjuk, hogyan lehet ezt megváltoztatni. A $data
változó
tartalmazza a name
és password
kulcsokat a felhasználó által megadott adatokkal.
Általában az adatokat közvetlenül küldjük el további feldolgozásra, ami lehet például az adatbázisba való
beillesztés. A feldolgozás során azonban előfordulhat hiba, például a felhasználónév már foglalt. Ebben az esetben a
hibát a addError()
segítségével visszaadjuk az űrlapnak, és hagyjuk, hogy újra kirajzolódjon,
hibaüzenettel:
$form->addError('Sorry, username is already in use.');
Az űrlap feldolgozása után átirányítjuk a következő oldalra. Ez megakadályozza, hogy az űrlapot véletlenül újra elküldjék a frissítés, vissza gombra kattintva, vagy a böngésző előzményeit mozgatva.
Alapértelmezés szerint az űrlapot POST módszerrel küldjük el ugyanarra az oldalra. Mindkettő megváltoztatható:
$form->setAction('/submit.php');
$form->setMethod('GET');
És ez minden :-) Van egy működőképes és tökéletesen biztosított űrlapunk.
Próbáljon meg több űrlapvezérlőt hozzáadni.
Hozzáférés a vezérlőkhöz
Az űrlapot és az egyes vezérlőelemeket komponenseknek nevezzük. Ezek egy komponensfát hoznak létre, amelynek gyökere az űrlap. Az egyes vezérlőelemeket a következőképpen érheti el:
$input = $form->getComponent('name');
// alternatív szintaxis: $input = $form['name'];
$button = $form->getComponent('send');
// alternatív szintaxis: $button = $form['send'];
A vezérlőelemek eltávolítása az unset használatával történik:
unset($form['name']);
Érvényesítési szabályok
Itt az érvényes szót használták, de az űrlapnak még nincsenek érvényesítési szabályai. Javítsuk ki.
A név kötelező lesz, ezért jelöljük meg a setRequired()
metódussal, amelynek argumentuma a hibaüzenet
szövege, amely akkor jelenik meg, ha a felhasználó nem tölti ki. Ha nem adunk meg argumentumot, akkor az alapértelmezett
hibaüzenetet használjuk.
$form->addText('name', 'Name:')
->setRequired('Please enter a name.');
Próbáljuk meg elküldeni az űrlapot a név kitöltése nélkül, és látni fogjuk, hogy hibaüzenet jelenik meg, és a böngésző vagy a szerver elutasítja a kitöltésig.
Ugyanakkor nem fogja tudni becsapni a rendszert azzal, hogy például csak szóközöket ír be a beviteli mezőbe. Szó sem lehet róla. A Nette automatikusan levágja a bal és jobb oldali szóközöket. Próbálja ki! Ezt mindig meg kellene tennie minden egysoros bevitelnél, de gyakran elfelejtik. A Nette automatikusan elvégzi. (Megpróbálhatja becsapni az űrlapokat, és többsoros karakterláncot küldhet névként. A Nette még ebben az esetben sem fog becsapni, és a sortörések szóközökre változnak).
Az űrlap mindig a szerveroldalon validálódik, de a JavaScript validáció is generálódik, ami gyors, és a felhasználó
azonnal értesül a hibáról, anélkül, hogy az űrlapot a szerverre kellene küldeni. Ezt a netteForms.js
szkript
kezeli. Adja hozzá az oldalhoz:
<script src="https://unpkg.com/nette-forms@3"></script>
Ha megnézi az űrlapot tartalmazó oldal forráskódját, észreveheti, hogy a Nette a szükséges mezőket a
required
CSS osztályú elemekbe illeszti be. Próbálja meg hozzáadni a következő stílust a sablonhoz, és a
„Név“ felirat piros lesz. Elegánsan jelöljük a kötelező mezőket a felhasználók számára:
<style>
.required label { color: maroon }
</style>
További érvényesítési szabályokat a addRule()
módszerrel adunk hozzá. Az első paraméter a szabály, a
második ismét a hibaüzenet szövege, és az opcionális érvényesítési szabály argumentum következhet. Mit jelent ez?
Az űrlap kap egy másik opcionális bemenetet életkor azzal a feltétellel, hogy annak számnak kell lennie
(addInteger()
) és bizonyos határok között ($form::Range
). És itt fogjuk használni a
addRule()
harmadik argumentumát , magát a tartományt:
$form->addInteger('age', 'Age:')
->addRule($form::Range, 'You must be older 18 years and be under 120.', [18, 120]);
Ha a felhasználó nem tölti ki a mezőt, akkor az érvényesítési szabályok nem lesznek ellenőrizve, mivel a mező opcionális.
Nyilvánvalóan van hely egy kis refaktorálásra. A hibaüzenetben és a harmadik paraméterben a számok duplikáltan
szerepelnek, ami nem ideális. Ha többnyelvű űrlapot hoznánk létre, és a
számokat tartalmazó üzenetet több nyelvre kellene lefordítani, ez megnehezítené az értékek módosítását. Emiatt a
%d
helyettesítő karakterek használhatók:
->addRule($form::Range, 'You must be older %d years and be under %d.', [18, 120]);
Térjünk vissza a jelszó mezőhöz, tegyük követelményessé, és ellenőrizzük a minimális
jelszóhosszúságot ($form::MinLength
), ismét az üzenetben szereplő helyettesítő karakterek
segítségével:
$form->addPassword('password', 'Password:')
->setRequired('Pick a password')
->addRule($form::MinLength, 'Your password has to be at least %d long', 8);
Adjunk hozzá egy passwordVerify
mezőt az űrlaphoz, ahol a felhasználó újra beírja a jelszót, az
ellenőrzéshez. Érvényesítési szabályok segítségével ellenőrizzük, hogy mindkét jelszó azonos-e
($form::Equal
). Érvként pedig szögletes zárójelek segítségével megadjuk
az első jelszóra való hivatkozást:
$form->addPassword('passwordVerify', 'Password again:')
->setRequired('Fill your password again to check for typo')
->addRule($form::Equal, 'Password mismatch', $form['password'])
->setOmitted();
A setOmitted()
segítségével egy olyan elemet jelöltünk meg, amelynek értéke nem igazán érdekel minket,
és amely csak az érvényesítés miatt létezik. Az értékét nem adjuk át a $data
.
Egy teljesen működőképes űrlapunk van, PHP és JavaScript validációval. A Nette validálási lehetőségei sokkal szélesebb körűek, létrehozhatunk feltételeket, megjeleníthetjük és elrejthetjük az oldal egyes részeit ezek alapján, stb. Mindent megtudhat az űrlapok validálásáról szóló fejezetben.
Alapértelmezett értékek
Gyakran állítunk be alapértelmezett értékeket űrlapvezérlőkhöz:
$form->addEmail('email', 'Email')
->setDefaultValue($lastUsedEmail);
Gyakran hasznos, ha egyszerre állítjuk be az összes vezérlőelem alapértelmezett értékeit. Például amikor az űrlapot rekordok szerkesztésére használjuk. Beolvassuk a rekordot az adatbázisból, és beállítjuk az alapértelmezett értékeket:
//$row = ['name' => 'John', 'age' => '33', /* ... */];
$form->setDefaults($row);
A vezérlőelemek definiálása után hívjuk meg a setDefaults()
címet.
Az űrlap megjelenítése
Az űrlap alapértelmezés szerint táblázatként jelenik meg. Az egyes vezérlőelemek az alapvető webes
hozzáférhetőségi irányelveket követik. Minden címke <label>
elemként jön létre, és a bemenetükhöz
kapcsolódik, a címkére kattintva a kurzor a bemenetre kerül.
Az egyes elemekhez bármilyen HTML-attribútumot beállíthatunk. Például adjunk hozzá egy helyőrzőt:
$form->addInteger('age', 'Age:')
->setHtmlAttribute('placeholder', 'Please fill in the age');
Valóban sokféleképpen lehet megjeleníteni egy űrlapot, ezért ez egy külön fejezet a megjelenítésről.
Osztályok leképezése
Térjünk vissza az űrlapadatok feldolgozásához. A getValues()
metódus a ArrayHash
objektumként
adta vissza a beküldött adatokat. Mivel ez egy általános osztály, valami olyasmi, mint a stdClass
, hiányozni
fog néhány kényelmi funkció a vele való munka során, például a tulajdonságok kódkiegészítése a szerkesztőkben vagy a
statikus kódelemzésben. Ezt úgy lehetne megoldani, ha minden űrlaphoz külön osztály lenne, amelynek tulajdonságai az egyes
vezérlőelemeket képviselik. Pl:
class RegistrationFormData
{
public string $name;
public int $age;
public string $password;
}
Alternatívaként használhatja a konstruktort is:
class RegistrationFormData
{
public function __construct(
public string $name,
public int $age,
public string $password,
) {
}
}
Az adatosztály tulajdonságai lehetnek enumok is, és automatikusan le lesznek képezve.
Hogyan mondjuk meg a Nette-nek, hogy az adatokat ennek az osztálynak az objektumaként adja vissza nekünk? Egyszerűbb, mint gondolnád. Mindössze annyit kell tennünk, hogy paraméterként megadjuk a hidratálandó osztály nevét vagy objektumát:
$data = $form->getValues(RegistrationFormData::class);
$name = $data->name;
A 'array'
is megadható paraméterként, és akkor az adatok tömbként térnek vissza.
Ha az űrlapok konténerekből álló többszintű struktúrából állnak, hozzon létre mindegyikhez külön osztályt:
$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;
}
A leképezés ekkor a $person
tulajdonságtípusból tudja, hogy a konténert a PersonFormData
osztályhoz kell leképeznie. Ha a tulajdonság tárolók tömbjét tartalmazná, adja meg a array
típust, és adja
át a leképezendő osztályt közvetlenül a tárolóhoz:
$person->setMappedType(PersonFormData::class);
Egy űrlap adatosztályának javaslatát a Nette\Forms\Blueprint::dataClass($form)
metódussal generálhatja, amely a böngészőoldalra nyomtatja ki. Ezután egyszerűen rákattintva kiválaszthatja és
bemásolhatja a kódot a projektjébe.
Többszörös beküldőgombok
Ha az űrlapon egynél több gomb van, általában meg kell különböztetnünk, hogy melyiket nyomta meg. A gomb
isSubmittedBy()
metódusa adja vissza nekünk ezt az információt:
$form->addSubmit('save', 'Mentés');
$form->addSubmit('delete', 'Törlés');
if ($form->isSuccess()) {
if ($form['save']->isSubmittedBy()) {
// ...
}
if ($form['delete']->isSubmittedBy()) {
// ...
}
}
Ne hagyjuk ki a $form->isSuccess()
, hogy ellenőrizzük az adatok érvényességét.
Ha egy űrlapot az Enter billentyűvel küldünk be, azt úgy kezeljük, mintha az első gombbal küldtük volna be.
Sebezhetőségi védelem
A Nette Framework nagy erőfeszítéseket tesz a biztonság érdekében, és mivel az űrlapok a leggyakoribb felhasználói bevitel, a Nette űrlapok szinte áthatolhatatlanok.
Amellett, hogy védi az űrlapokat az olyan jól ismert sebezhetőségek támadása ellen, mint a Cross-Site Scripting (XSS ) és a Cross-Site Request Forgery (CSRF ), sok apró biztonsági feladatot is elvégez, amelyekre már nem kell gondolnia.
Például kiszűri az összes vezérlő karaktert a bemenetekből, és ellenőrzi az UTF-8 kódolás érvényességét, így az űrlapból származó adatok mindig tiszták lesznek. A kiválasztó dobozok és rádiólisták esetében ellenőrzi, hogy a kiválasztott elemek valóban a felkínáltak közül kerültek-e ki, és nem történt-e hamisítás. Már említettük, hogy az egysoros szövegbevitel esetében eltávolítja a sor végi karaktereket, amelyeket egy támadó oda küldhet. Többsoros bevitel esetén normalizálja a sor végi karaktereket. És így tovább.
A Nette olyan biztonsági réseket is kijavít az Ön számára, amelyekről a legtöbb programozónak fogalma sincs, hogy léteznek.
Az említett CSRF-támadás lényege, hogy a támadó egy olyan oldal meglátogatására csábítja az áldozatot, amely némán végrehajt egy kérést az áldozat böngészőjében a szerver felé, ahová az áldozat éppen be van jelentkezve, és a szerver azt hiszi, hogy a kérést az áldozat akaratából tette. Ezért a Nette megakadályozza, hogy az űrlapot egy másik tartományból POST-on keresztül küldjék el. Ha valamilyen oknál fogva ki akarja kapcsolni a védelmet, és engedélyezni szeretné, hogy az űrlapot egy másik tartományból küldjék el, használja a következőt:
$form->allowCrossOrigin(); // FIGYELEM! Kikapcsolja a védelmet!
Ez a védelem a _nss
nevű SameSite cookie-t használja. Ezért hozzon létre egy űrlapot az első kimenet
kiürítése előtt, hogy a cookie-t el lehessen küldeni.
A SameSite cookie-védelem nem biztos, hogy 100%-ig megbízható, ezért érdemes bekapcsolni a token-védelmet:
$form->addProtection();
Erősen ajánlott ezt a védelmet alkalmazni az alkalmazás adminisztrációs részében lévő, érzékeny adatokat
módosító űrlapokra. A keretrendszer a CSRF-támadás ellen a munkamenetben tárolt hitelesítési token generálásával és
érvényesítésével védekezik (az érv a token lejárta esetén megjelenő hibaüzenet). Ezért szükséges, hogy az űrlap
megjelenítése előtt elinduljon egy munkamenet. A webhely adminisztrációs részében a munkamenet általában már elindult,
a felhasználó bejelentkezése miatt. Ellenkező esetben a munkamenetet a Nette\Http\Session::start()
metódussal
kell elindítani.
Tehát van egy gyors bevezetésünk a Nette űrlapokba. További inspirációért próbálja megkeresni a disztribúcióban található példák könyvtárát.