Επικύρωση Φορμών

Υποχρεωτικά Στοιχεία

Επισημαίνουμε τα υποχρεωτικά στοιχεία με τη μέθοδο setRequired(). Το όρισμά της είναι το κείμενο του μηνύματος σφάλματος που θα εμφανιστεί εάν ο χρήστης δεν συμπληρώσει το στοιχείο. Εάν δεν παρέχουμε όρισμα, θα χρησιμοποιηθεί το προεπιλεγμένο μήνυμα σφάλματος.

$form->addText('name', 'Όνομα:')
	->setRequired('Παρακαλώ εισάγετε το όνομά σας.');

Κανόνες

Προσθέτουμε κανόνες επικύρωσης στα στοιχεία χρησιμοποιώντας τη μέθοδο addRule(). Η πρώτη παράμετρος είναι ο κανόνας, η δεύτερη είναι το κείμενο του μηνύματος σφάλματος και η τρίτη είναι το όρισμα του κανόνα επικύρωσης.

$form->addPassword('password', 'Κωδικός πρόσβασης:')
	->addRule($form::MinLength, 'Ο κωδικός πρόσβασης πρέπει να έχει μήκος τουλάχιστον %d χαρακτήρων.', 8);

Οι κανόνες επικύρωσης ελέγχονται μόνο εάν ο χρήστης συμπληρώσει το στοιχείο.

Το Nette έρχεται με μια σειρά προκαθορισμένων κανόνων, των οποίων τα ονόματα είναι σταθερές της κλάσης Nette\Forms\Form. Μπορούμε να χρησιμοποιήσουμε αυτούς τους κανόνες για όλα τα στοιχεία:

σταθερά περιγραφή τύπος ορίσματος
Required υποχρεωτικό στοιχείο, ψευδώνυμο για setRequired()
Filled υποχρεωτικό στοιχείο, ψευδώνυμο για setRequired()
Blank το στοιχείο δεν πρέπει να συμπληρωθεί
Equal η τιμή είναι ίση με την παράμετρο mixed
NotEqual η τιμή δεν είναι ίση με την παράμετρο mixed
IsIn η τιμή είναι ίση με ένα από τα στοιχεία του πίνακα array
IsNotIn η τιμή δεν είναι ίση με κανένα στοιχείο του πίνακα array
Valid είναι το στοιχείο συμπληρωμένο σωστά; (για συνθήκες)

Είσοδοι Κειμένου

Για τα στοιχεία addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat(), μπορούν επίσης να χρησιμοποιηθούν ορισμένοι από τους ακόλουθους κανόνες:

MinLength ελάχιστο μήκος κειμένου int
MaxLength μέγιστο μήκος κειμένου int
Length μήκος εντός εύρους ή ακριβές μήκος ζεύγος [int, int] ή int
Email έγκυρη διεύθυνση email
URL απόλυτο URL
Pattern ταιριάζει με την κανονική έκφραση string
PatternInsensitive όπως το Pattern, αλλά χωρίς διάκριση πεζών-κεφαλαίων string
Integer ακέραια τιμή
Numeric ψευδώνυμο για Integer
Float αριθμός
Min ελάχιστη τιμή αριθμητικού στοιχείου int|float
Max μέγιστη τιμή αριθμητικού στοιχείου int|float
Range τιμή εντός εύρους ζεύγος [int|float, int|float]

Οι κανόνες επικύρωσης Integer, Numeric και Float μετατρέπουν αμέσως την τιμή σε ακέραιο ή δεκαδικό, αντίστοιχα. Επιπλέον, ο κανόνας URL δέχεται επίσης μια διεύθυνση χωρίς σχήμα (π.χ. nette.org) και προσθέτει το σχήμα (https://nette.org). Η έκφραση στο Pattern και το PatternIcase πρέπει να ισχύει για ολόκληρη την τιμή, δηλαδή σαν να ήταν περικλεισμένη από τους χαρακτήρες ^ και $.

Αριθμός Στοιχείων

Για τα στοιχεία addMultiUpload(), addCheckboxList(), addMultiSelect(), μπορούν επίσης να χρησιμοποιηθούν οι ακόλουθοι κανόνες για τον περιορισμό του αριθμού των επιλεγμένων στοιχείων ή των ανεβασμένων αρχείων, αντίστοιχα:

MinLength ελάχιστος αριθμός int
MaxLength μέγιστος αριθμός int
Length αριθμός εντός εύρους ή ακριβής αριθμός ζεύγος [int, int] ή int

Ανέβασμα Αρχείων

Για τα στοιχεία addUpload(), addMultiUpload(), μπορούν επίσης να χρησιμοποιηθούν οι ακόλουθοι κανόνες:

MaxFileSize μέγιστο μέγεθος αρχείου σε bytes int
MimeType Τύπος MIME, επιτρέπονται χαρακτήρες μπαλαντέρ ('video/*') string|string[]
Image εικόνα JPEG, PNG, GIF, WebP, AVIF
Pattern το όνομα αρχείου ταιριάζει με την κανονική έκφραση string
PatternInsensitive όπως το Pattern, αλλά χωρίς διάκριση πεζών-κεφαλαίων string

Τα MimeType και Image απαιτούν την επέκταση PHP fileinfo. Το αν ένα αρχείο ή μια εικόνα είναι του απαιτούμενου τύπου ανιχνεύεται με βάση την υπογραφή του και δεν επαληθεύει την ακεραιότητα ολόκληρου του αρχείου. Το αν μια εικόνα είναι κατεστραμμένη μπορεί να προσδιοριστεί, για παράδειγμα, προσπαθώντας να την φορτώσετε.

Μηνύματα Σφάλματος

Όλοι οι προκαθορισμένοι κανόνες, εκτός από τα Pattern και PatternInsensitive, έχουν ένα προεπιλεγμένο μήνυμα σφάλματος, οπότε μπορεί να παραλειφθεί. Ωστόσο, καθορίζοντας και διατυπώνοντας όλα τα μηνύματα κατά παραγγελία, θα κάνετε τη φόρμα πιο φιλική προς τον χρήστη.

Μπορείτε να αλλάξετε τα προεπιλεγμένα μηνύματα στην διαμόρφωση, επεξεργαζόμενοι τα κείμενα στον πίνακα Nette\Forms\Validator::$messages, ή χρησιμοποιώντας έναν μεταφραστή.

Οι ακόλουθες συμβολοσειρές κράτησης θέσης μπορούν να χρησιμοποιηθούν στο κείμενο των μηνυμάτων σφάλματος:

%d αντικαθίσταται διαδοχικά από τα ορίσματα του κανόνα
%n$d αντικαθίσταται από το n-οστό όρισμα του κανόνα
%label αντικαθίσταται από την ετικέτα του στοιχείου (χωρίς την άνω και κάτω τελεία)
%name αντικαθίσταται από το όνομα του στοιχείου (π.χ. name)
%value αντικαθίσταται από την τιμή που εισήγαγε ο χρήστης
$form->addText('name', 'Όνομα:')
	->setRequired('Παρακαλώ συμπληρώστε το %label');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'τουλάχιστον %d και το πολύ %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'το πολύ %2$d και τουλάχιστον %1$d', [5, 10]);

Συνθήκες

Εκτός από τους κανόνες, μπορούν επίσης να προστεθούν συνθήκες. Γράφονται παρόμοια με τους κανόνες, αλλά αντί για addRule(), χρησιμοποιούμε τη μέθοδο addCondition(), και φυσικά, δεν παρέχουμε κανένα μήνυμα σφάλματος (η συνθήκη απλώς ρωτά):

$form->addPassword('password', 'Κωδικός πρόσβασης:')
	// εάν ο κωδικός πρόσβασης δεν είναι μεγαλύτερος από 8 χαρακτήρες
	->addCondition($form::MaxLength, 8)
		// τότε πρέπει να περιέχει ένα ψηφίο
		->addRule($form::Pattern, 'Πρέπει να περιέχει ένα ψηφίο.', '.*[0-9].*');

Η συνθήκη μπορεί επίσης να συνδεθεί με ένα στοιχείο διαφορετικό από το τρέχον χρησιμοποιώντας το addConditionOn(). Ως πρώτη παράμετρο, παρέχουμε μια αναφορά στο στοιχείο. Σε αυτό το παράδειγμα, το email θα είναι υποχρεωτικό μόνο εάν το πλαίσιο ελέγχου είναι επιλεγμένο (η τιμή του θα είναι true):

$form->addCheckbox('newsletters', 'στείλτε μου ενημερωτικά δελτία');

$form->addEmail('email', 'E-mail:')
	// εάν το πλαίσιο ελέγχου είναι επιλεγμένο
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// τότε απαιτήστε το email
		->setRequired('Παρακαλώ εισάγετε τη διεύθυνση email σας.');

Μπορείτε να δημιουργήσετε σύνθετες δομές από συνθήκες χρησιμοποιώντας τα elseCondition() και endCondition():

$form->addText(/* ... */)
	->addCondition(/* ... */) // εάν η πρώτη συνθήκη πληρούται
		->addConditionOn(/* ... */) // και η δεύτερη συνθήκη σε άλλο στοιχείο
			->addRule(/* ... */) // απαιτήστε αυτόν τον κανόνα
		->elseCondition() // εάν η δεύτερη συνθήκη δεν πληρούται
			->addRule(/* ... */) // απαιτήστε αυτούς τους κανόνες
			->addRule(/* ... */)
		->endCondition() // επιστρέφουμε στην πρώτη συνθήκη
		->addRule(/* ... */);

Στο Nette, είναι πολύ εύκολο να αντιδράσετε στην εκπλήρωση ή μη εκπλήρωση μιας συνθήκης επίσης στην πλευρά του JavaScript χρησιμοποιώντας τη μέθοδο toggle(), δείτε δυναμικό-javascript.

Αναφορά σε Άλλο Στοιχείο

Ένα άλλο στοιχείο φόρμας μπορεί επίσης να περάσει ως όρισμα σε έναν κανόνα ή μια συνθήκη. Ο κανόνας θα χρησιμοποιήσει τότε την τιμή που εισήγαγε αργότερα ο χρήστης στο πρόγραμμα περιήγησης. Με αυτόν τον τρόπο, μπορείτε, για παράδειγμα, να επικυρώσετε δυναμικά ότι το στοιχείο password περιέχει την ίδια συμβολοσειρά με το στοιχείο password_confirm:

$form->addPassword('password', 'Κωδικός πρόσβασης');
$form->addPassword('password_confirm', 'Επιβεβαιώστε τον κωδικό πρόσβασης')
    ->addRule($form::Equal, 'Οι κωδικοί πρόσβασης δεν ταιριάζουν.', $form['password']);

Προσαρμοσμένοι Κανόνες και Συνθήκες

Μερικές φορές βρισκόμαστε σε μια κατάσταση όπου οι ενσωματωμένοι κανόνες επικύρωσης στο Nette δεν είναι αρκετοί και πρέπει να επικυρώσουμε τα δεδομένα του χρήστη με τον δικό μας τρόπο. Στο Nette, αυτό είναι πολύ εύκολο!

Μπορείτε να περάσετε οποιαδήποτε επανάκληση ως πρώτη παράμετρο στις μεθόδους addRule() ή addCondition(). Η επανάκληση δέχεται το ίδιο το στοιχείο ως πρώτη παράμετρο και επιστρέφει μια boolean τιμή που υποδεικνύει εάν η επικύρωση ήταν επιτυχής. Κατά την προσθήκη ενός κανόνα χρησιμοποιώντας το addRule(), είναι επίσης δυνατό να καθοριστούν πρόσθετα ορίσματα, τα οποία στη συνέχεια περνούν ως δεύτερη παράμετρος.

Μπορούμε να δημιουργήσουμε το δικό μας σύνολο επικυρωτών ως κλάση με στατικές μεθόδους:

class MyValidators
{
	// ελέγχει εάν η τιμή είναι διαιρετή από το όρισμα
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// άλλοι επικυρωτές
	}
}

Η χρήση είναι τότε πολύ απλή:

$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'Η τιμή πρέπει να είναι πολλαπλάσιο του %d.',
		8,
	);

Προσαρμοσμένοι κανόνες επικύρωσης μπορούν επίσης να προστεθούν στο JavaScript. Η προϋπόθεση είναι ότι ο κανόνας είναι μια στατική μέθοδος. Το όνομά του για τον επικυρωτή JavaScript δημιουργείται συνδυάζοντας το όνομα της κλάσης χωρίς ανάστροφες καθέτους \, μια κάτω παύλα _ και το όνομα της μεθόδου. Για παράδειγμα, το App\MyValidators::validateDivisibility γράφεται ως AppMyValidators_validateDivisibility και προστίθεται στο αντικείμενο Nette.validators:

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

Συμβάν onValidate

Μετά την υποβολή της φόρμας, πραγματοποιείται επικύρωση, όπου ελέγχονται οι μεμονωμένοι κανόνες που προστέθηκαν χρησιμοποιώντας το addRule(), και στη συνέχεια ενεργοποιείται το συμβάν onValidate. Ο χειριστής του μπορεί να χρησιμοποιηθεί για συμπληρωματική επικύρωση, συνήθως για την επαλήθευση του σωστού συνδυασμού τιμών σε πολλαπλά στοιχεία της φόρμας.

Εάν εντοπιστεί σφάλμα, το περνάμε στη φόρμα χρησιμοποιώντας τη μέθοδο addError(). Αυτή μπορεί να κληθεί είτε σε ένα συγκεκριμένο στοιχείο είτε απευθείας στη φόρμα.

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('Αυτός ο συνδυασμός δεν είναι δυνατός.');
	}
}

Σφάλματα κατά την Επεξεργασία

Σε πολλές περιπτώσεις, μαθαίνουμε για ένα σφάλμα μόνο όταν επεξεργαζόμαστε μια έγκυρη φόρμα, για παράδειγμα, όταν γράφουμε ένα νέο στοιχείο στη βάση δεδομένων και συναντάμε διπλότυπα κλειδιά. Σε αυτή την περίπτωση, περνάμε ξανά το σφάλμα στη φόρμα χρησιμοποιώντας τη μέθοδο addError(). Αυτή μπορεί να κληθεί είτε σε ένα συγκεκριμένο στοιχείο είτε απευθείας στη φόρμα:

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('Μη έγκυρος κωδικός πρόσβασης.');
	}
}

Εάν είναι δυνατόν, συνιστούμε να επισυνάψετε το σφάλμα απευθείας στο στοιχείο της φόρμας, καθώς θα εμφανιστεί δίπλα του όταν χρησιμοποιείτε τον προεπιλεγμένο renderer.

$form['date']->addError('Συγγνώμη, αλλά αυτή η ημερομηνία είναι ήδη κατειλημμένη.');

Μπορείτε να καλέσετε το addError() επανειλημμένα για να περάσετε πολλαπλά μηνύματα σφάλματος στη φόρμα ή στο στοιχείο. Μπορείτε να τα λάβετε χρησιμοποιώντας το getErrors().

Προσοχή, το $form->getErrors() επιστρέφει μια σύνοψη όλων των μηνυμάτων σφάλματος, συμπεριλαμβανομένων εκείνων που παραδόθηκαν απευθείας σε μεμονωμένα στοιχεία, όχι μόνο απευθείας στη φόρμα. Μπορείτε να λάβετε τα μηνύματα σφάλματος που παραδόθηκαν μόνο στη φόρμα μέσω του $form->getOwnErrors().

Τροποποίηση Εισόδου

Χρησιμοποιώντας τη μέθοδο addFilter(), μπορούμε να τροποποιήσουμε την τιμή που εισήγαγε ο χρήστης. Σε αυτό το παράδειγμα, θα ανεχτούμε και θα αφαιρέσουμε κενά στον ταχυδρομικό κώδικα:

$form->addText('zip', 'Τ.Κ.:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // αφαιρούμε τα κενά από τον Τ.Κ.
	})
	->addRule($form::Pattern, 'Ο Τ.Κ. δεν είναι στη μορφή πέντε ψηφίων.', '\d{5}');

Το φίλτρο ενσωματώνεται μεταξύ των κανόνων επικύρωσης και των συνθηκών, οπότε η σειρά των μεθόδων έχει σημασία, δηλαδή το φίλτρο και ο κανόνας καλούνται με την ίδια σειρά όπως οι μέθοδοι addFilter() και addRule().

Επικύρωση JavaScript

Η γλώσσα για τη διατύπωση συνθηκών και κανόνων είναι πολύ ισχυρή. Όλες οι κατασκευές λειτουργούν τόσο στην πλευρά του διακομιστή όσο και στην πλευρά του JavaScript. Μεταφέρονται σε χαρακτηριστικά HTML data-nette-rules ως JSON. Η ίδια η επικύρωση εκτελείται από ένα σενάριο που παρακολουθεί το συμβάν submit της φόρμας, διατρέχει τα μεμονωμένα στοιχεία και εκτελεί την αντίστοιχη επικύρωση.

Αυτό το σενάριο είναι το netteForms.js και είναι διαθέσιμο από πολλές πιθανές πηγές:

Μπορείτε να εισαγάγετε το σενάριο απευθείας στη σελίδα HTML από ένα CDN:

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

Ή να το αντιγράψετε τοπικά στον δημόσιο φάκελο του έργου (π.χ. από το vendor/nette/forms/src/assets/netteForms.min.js):

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

Ή να το εγκαταστήσετε μέσω npm:

npm install nette-forms

Και στη συνέχεια να το φορτώσετε και να το εκτελέσετε:

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

Εναλλακτικά, μπορείτε να το φορτώσετε απευθείας από τον φάκελο vendor:

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

Δυναμικό JavaScript

Θέλετε να εμφανίσετε τα πεδία για την εισαγωγή της διεύθυνσης μόνο εάν ο χρήστης επιλέξει την αποστολή των αγαθών ταχυδρομικώς; Κανένα πρόβλημα. Το κλειδί είναι το ζεύγος μεθόδων addCondition() & toggle():

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

Αυτός ο κώδικας λέει ότι όταν η συνθήκη πληρούται, δηλαδή όταν το πλαίσιο ελέγχου είναι επιλεγμένο, το στοιχείο HTML #address-container θα είναι ορατό. Και αντίστροφα. Έτσι, τοποθετούμε τα στοιχεία της φόρμας με τη διεύθυνση του παραλήπτη σε ένα container με αυτό το ID, και όταν κάνουμε κλικ στο πλαίσιο ελέγχου, θα αποκρυφθούν ή θα εμφανιστούν. Αυτό διασφαλίζεται από το σενάριο netteForms.js.

Οποιοσδήποτε επιλογέας μπορεί να περάσει ως όρισμα στη μέθοδο toggle(). Για ιστορικούς λόγους, μια αλφαριθμητική συμβολοσειρά χωρίς άλλους ειδικούς χαρακτήρες νοείται ως το ID του στοιχείου, δηλαδή σαν να προηγείται ο χαρακτήρας #. Η δεύτερη προαιρετική παράμετρος επιτρέπει την αντιστροφή της συμπεριφοράς, δηλαδή αν χρησιμοποιούσαμε toggle('#address-container', false), το στοιχείο θα εμφανιζόταν μόνο εάν το πλαίσιο ελέγχου δεν ήταν επιλεγμένο.

Η προεπιλεγμένη υλοποίηση στο JavaScript αλλάζει την ιδιότητα hidden των στοιχείων. Ωστόσο, μπορούμε εύκολα να αλλάξουμε τη συμπεριφορά, για παράδειγμα, προσθέτοντας μια κίνηση. Απλά αντικαταστήστε τη μέθοδο Nette.toggle στο JavaScript με τη δική σας λύση:

Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// απόκρυψη ή εμφάνιση του 'el' βάσει της τιμής 'visible'
	});
};

Απενεργοποίηση Επικύρωσης

Μερικές φορές μπορεί να είναι χρήσιμο να απενεργοποιήσετε την επικύρωση. Εάν το πάτημα ενός κουμπιού υποβολής δεν πρέπει να εκτελεί επικύρωση (κατάλληλο για κουμπιά Ακύρωση ή Προεπισκόπηση), μπορούμε να την απενεργοποιήσουμε χρησιμοποιώντας τη μέθοδο $submit->setValidationScope([]). Εάν πρέπει να εκτελεί μόνο μερική επικύρωση, μπορούμε να καθορίσουμε ποια πεδία ή containers φόρμας πρέπει να επικυρωθούν.

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

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

$form->addSubmit('send1'); // Επικυρώνει ολόκληρη τη φόρμα
$form->addSubmit('send2')
	->setValidationScope([]); // Δεν επικυρώνει καθόλου
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Επικυρώνει μόνο το στοιχείο name
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Επικυρώνει μόνο το στοιχείο age
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Επικυρώνει το container details

Το setValidationScope δεν επηρεάζει το συμβάν onValidate στη φόρμα, το οποίο θα καλείται πάντα. Το συμβάν onValidate σε ένα container θα ενεργοποιείται μόνο εάν αυτό το container έχει επισημανθεί για μερική επικύρωση.

έκδοση: 4.0