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

Απαιτούμενοι έλεγχοι

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

$form->addText('name', 'Name:')
	->setRequired('Please fill your name.');

Κανόνες

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

$form->addPassword('password', 'Password:')
	->addRule($form::MinLength, 'Password must be at least %d characters', 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 έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου
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 μετατρέπουν αυτόματα την τιμή σε ακέραιο (ή float αντίστοιχα). Επιπλέον, ο κανόνας URL δέχεται επίσης μια διεύθυνση χωρίς σχήμα (π.χ. nette.org) και συμπληρώνει το σχήμα (https://nette.org). Οι εκφράσεις στους κανόνες Pattern και PatternInsensitive πρέπει να ισχύουν για ολόκληρη την τιμή, δηλαδή σαν να ήταν τυλιγμένη στους χαρακτήρες ^ and $.

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

Για τα στοιχεία 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
Pattern το όνομα του αρχείου ταιριάζει με κανονική έκφραση string
PatternInsensitive όπως το Pattern, αλλά χωρίς να λαμβάνεται υπόψη η πεζότητα string

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

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

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

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

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

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

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'at least %d and no more than %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'no more than %2$d and at least %1$d', [5, 10]);

Συνθήκες

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

$form->addPassword('password', 'Password:')
	// εάν ο κωδικός πρόσβασης δεν υπερβαίνει τους 8 χαρακτήρες ...
	->addCondition($form::MaxLength, 8)
		// ... τότε πρέπει να περιέχει έναν αριθμό
		->addRule($form::Pattern, 'Must contain number', '.*[0-9].*');

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

$form->addCheckbox('newsletters', 'send me newsletters');

$form->addEmail('email', 'Email:')
	// αν το πλαίσιο ελέγχου είναι επιλεγμένο ...
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// ... απαιτούν email
		->setRequired('Fill your email address');

Οι συνθήκες μπορούν να ομαδοποιηθούν σε σύνθετες δομές με τις μεθόδους elseCondition() και endCondition().

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

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

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

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

$form->addPassword('password', 'Password');
$form->addPassword('password_confirm', 'Confirm Password')
    ->addRule($form::Equal, 'The passwords do not match', $form['password']);

Προσαρμοσμένοι κανόνες και προϋποθέσεις

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

Μπορείτε να περάσετε οποιοδήποτε callback ως πρώτη παράμετρο στις μεθόδους addRule() ή addCondition(). Το callback δέχεται το ίδιο το στοιχείο ως πρώτη παράμετρο και επιστρέφει μια 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'],
		'The value must be a multiple of %d',
		8,
	);

Μπορούν επίσης να προστεθούν προσαρμοσμένοι κανόνες επικύρωσης σε JavaScript. Η μόνη απαίτηση είναι ότι ο κανόνας πρέπει να είναι μια στατική μέθοδος. Το όνομά του για τον επικυρωτή JavaScript δημιουργείται από τη συνένωση του ονόματος της κλάσης χωρίς κάθετους \, the underscore _, και του ονόματος της μεθόδου. Για παράδειγμα, γράψτε το 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('This combination is not possible.');
	}
}

Σφάλματα επεξεργασίας

Σε πολλές περιπτώσεις, ανακαλύπτουμε ένα σφάλμα όταν επεξεργαζόμαστε μια έγκυρη φόρμα, π.χ. όταν γράφουμε μια νέα εγγραφή στη βάση δεδομένων και συναντάμε ένα διπλότυπο κλειδί. Σε αυτή την περίπτωση, περνάμε το σφάλμα πίσω στη φόρμα χρησιμοποιώντας τη μέθοδο 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('Invalid password.');
	}
}

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

$form['date']->addError('Sorry, this date is already taken.');

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

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

Τροποποίηση τιμών εισόδου

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

$form->addText('zip', 'Postcode:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // αφαίρεση των κενών από τον ταχυδρομικό κώδικα
	})
	->addRule($form::Pattern, 'The postal code is not five digits', '\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 θα είναι ορατό. Και το αντίστροφο. Έτσι, τοποθετούμε τα στοιχεία της φόρμας με τη διεύθυνση του παραλήπτη σε ένα δοχείο με αυτό το ID, και όταν πατηθεί το πλαίσιο ελέγχου, αυτά αποκρύπτονται ή εμφανίζονται. Αυτό το χειρίζεται το σενάριο netteForms.js.

Οποιοσδήποτε επιλογέας μπορεί να περάσει ως όρισμα στη μέθοδο toggle(). Για ιστορικούς λόγους, μια αλφαριθμητική συμβολοσειρά χωρίς άλλους ειδικούς χαρακτήρες αντιμετωπίζεται ως αναγνωριστικό στοιχείου, το ίδιο όπως αν προηγούνταν το # character. The second optional parameter allows us to reverse the behavior, i.e. if we used toggle('#address-container', false), το στοιχείο θα εμφανιζόταν μόνο αν το πλαίσιο ελέγχου ήταν απενεργοποιημένο.

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

Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// hide or show 'el' according to the value of 'visible'
	});
};

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

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

$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']]); // Επικυρώνει μόνο το πεδίο 'όνομα'
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Επικυρώνει μόνο το πεδίο 'age'.
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Επικυρώνει το δοχείο 'details'.

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

έκδοση: 4.0