Form Doğrulama

Zorunlu Öğeler

Öğeler setRequired() metoduyla zorunlu işaretlenir. Argümanı, kullanıcı öğeyi doldurmazsa görüntülenecek hata mesajının metnidir. Argüman verilmezse varsayılan hata mesajı kullanılır.

$form->addText('name', 'Ad:')
	->setRequired('Lütfen adınızı girin.');

Kurallar

Öğelere doğrulama kurallarını addRule() metoduyla ekleriz. İlk parametre kural, ikincisi hata mesajı, üçüncüsü ise doğrulama kuralının argümanıdır.

$form->addPassword('password', 'Parola:')
	->addRule($form::MinLength, 'Parola en az %d karakter uzunluğunda olmalıdır', 8);

Doğrulama kuralları yalnızca kullanıcı öğeyi doldurduysa denetlenir.

Nette, adları Nette\Forms\Form sınıfının sabitleri olan birkaç önceden tanımlı kuralla gelir. Bu kuralları tüm öğelere uygulayabiliriz:

sabit açıklama argüman türü
Required zorunlu öğe, setRequired() için takma ad
Filled zorunlu öğe, setRequired() için takma ad
Blank öğe doldurulmamalı
Equal değer parametreye eşit olmalı mixed
NotEqual değer parametreye eşit olmamalı mixed
IsIn değer dizideki öğelerden biri olmalı array
IsNotIn değer dizideki öğelerden hiçbiri olmamalı array
Valid öğe doğru doldurulmuş mu? (yalnızca addConditionOn() içinde)

Metin girdileri

addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat() öğelerinde aşağıdaki kurallardan bazıları da uygulanabilir:

MinLength en az metin uzunluğu int
MaxLength en fazla metin uzunluğu int
Length aralıkta uzunluk ya da tam uzunluk [int, int] çifti ya da int
Email geçerli e-posta adresi
URL mutlak URL
Pattern düzenli ifadeyle eşleşir string
PatternInsensitive Pattern gibi, ama büyük/küçük harfe duyarsız string
Integer tam sayı değeri
Numeric negatif olmayan tam sayı (yalnızca rakamlar)
Float sayı
Min sayısal öğenin en küçük değeri int|float
Max sayısal öğenin en büyük değeri int|float
Range aralıkta değer [int|float, int|float] çifti

Integer ve Float doğrulama kuralları, değeri sırasıyla tam sayıya ya da kayan noktalı sayıya otomatik dönüştürür. Ayrıca URL kuralı şemasız bir adresi de (örneğin nette.org) kabul eder ve şemayı tamamlar (https://nette.org). Pattern ve PatternInsensitive içindeki ifade, değerin tamamı için geçerli olmalıdır; yani ^ ve $ karakterleriyle sarılmış gibi.

Öğe Sayısı

addMultiUpload(), addCheckboxList(), addMultiSelect() öğelerinde, seçilen öğelerin ya da yüklenen dosyaların sayısını sınırlamak için şu kuralları da kullanabilirsiniz:

MinLength en az sayı int
MaxLength en fazla sayı int
Length aralıkta sayı ya da tam sayı [int, int] çifti ya da int

Dosya Yükleme

addUpload(), addMultiUpload() öğelerinde şu kurallar da kullanılabilir:

MaxFileSize bayt cinsinden en fazla dosya boyutu int
MimeType MIME türü, joker karakterlere izin verilir ('video/*') string|string[]
Image JPEG, PNG, GIF, WebP, AVIF görseli
Pattern dosya adı düzenli ifadeyle eşleşir string
PatternInsensitive Pattern gibi, ama büyük/küçük harfe duyarsız string

MimeType ve Image, fileinfo PHP eklentisini gerektirir. Bir dosyanın ya da görselin gereken türde olup olmadığı imzasına göre saptanır ve dosyanın tamamının bütünlüğü denetlenmez. Bir görselin bozuk olup olmadığını örneğin onu yüklemeyi deneyerek belirleyebilirsiniz.

Hata Mesajları

Pattern ve PatternInsensitive dışındaki tüm önceden tanımlı kuralların varsayılan bir hata mesajı vardır, bu yüzden atlanabilirler. Ancak tüm özel mesajları ihtiyacınıza göre verip biçimlendirerek formu daha kullanıcı dostu kılarsınız.

Varsayılan mesajları yapılandırmada, Nette\Forms\Validator::$messages dizisindeki metinleri değiştirerek ya da bir çevirmen kullanarak değiştirebilirsiniz.

Hata mesajlarının metninde şu yer tutucu dizeleri kullanılabilir:

%d sırayla kural argümanlarıyla değiştirilir
%n$d n'inci kural argümanıyla değiştirilir
%label öğenin etiketiyle değiştirilir (iki nokta olmadan)
%name öğenin adıyla değiştirilir (örneğin name)
%value kullanıcının girdiği değerle değiştirilir
$form->addText('name', 'Ad:')
	->setRequired('Lütfen %label doldurun');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'en az %d ve en fazla %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'en fazla %2$d ve en az %1$d', [5, 10]);

Koşullar

Kuralların yanı sıra koşullar da eklenebilir. Kurallara benzer biçimde yazılırlar, ama addRule() yerine addCondition() metodunu kullanırız ve doğal olarak hata mesajı vermeyiz (koşul yalnızca sorar):

$form->addPassword('password', 'Parola:')
	// parolanın uzunluğu 8'den büyük değilse
	->addCondition($form::MaxLength, 8)
		// o zaman bir rakam içermeli
		->addRule($form::Pattern, 'Bir rakam içermelidir', '.*[0-9].*');

Koşul, addConditionOn() ile geçerli öğe dışındaki bir öğeye bağlanabilir. İlk parametre öğeye bir referanstır. Bu örnekte e-posta yalnızca onay kutusu işaretliyse (yani değeri true ise) zorunlu olacak:

$form->addCheckbox('newsletters', 'Bana bülten gönderin');

$form->addEmail('email', 'E-posta:')
	// onay kutusu işaretliyse
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// o zaman e-postayı zorunlu kıl
		->setRequired('E-posta adresinizi girin');

Koşullar, elseCondition() ve endCondition() ile karmaşık yapılara dönüştürülebilir:

$form->addText(/* ... */)
	->addCondition(/* ... */) // ilk koşul sağlanırsa
		->addConditionOn(/* ... */) // ve başka bir öğedeki ikinci koşul da sağlanırsa
			->addRule(/* ... */) // bu kuralı zorunlu kıl
		->elseCondition() // ikinci koşul sağlanmazsa
			->addRule(/* ... */) // bu kuralları zorunlu kıl
			->addRule(/* ... */)
		->endCondition() // ilk koşula geri döneriz
		->addRule(/* ... */);

addCondition() metodunun ilk argümanı boolean bir değer de olabilir. Bu, karar form kurulurken zaten belliyse işe yarar; örneğin bir kuralı yalnızca belirli koşullarda uygulamak için:

$form->addText('nickname')
	->addCondition($isRequired) // form kurulurken bilinen bir değer
		->setRequired();

Nette'te, bir koşulun sağlanmasına ya da sağlanmamasına JavaScript tarafında tepki vermek toggle() metoduyla çok kolaydır, bkz. Dinamik JavaScript.

Başka Bir Öğeye Referans

Bir kurala ya da koşula argüman olarak başka bir form öğesi de aktarabilirsiniz. Kural o zaman kullanıcının tarayıcıda sonradan girdiği değeri kullanır. Bu, örneğin password öğesinin password_confirm öğesiyle aynı dizeyi içerdiğini dinamik olarak doğrulamak için kullanılabilir:

$form->addPassword('password', 'Parola');
$form->addPassword('password_confirm', 'Parolayı doğrulayın')
    ->addRule($form::Equal, 'Parolalar eşleşmiyor', $form['password']);

Özel Kurallar ve Koşullar

Bazen Nette'in yerleşik doğrulama kurallarının yetmediği ve kullanıcı verisini kendi yolumuzla doğrulamamız gereken durumlarla karşılaşırız. Nette'te bu çok basittir!

addRule() ya da addCondition() metotlarına ilk parametre olarak herhangi bir callback aktarabilirsiniz. Callback, ilk parametre olarak öğenin kendisini alır ve doğrulamanın başarılı olup olmadığını gösteren boolean bir değer döndürür. addRule() ile kural eklerken ek argümanlar verilebilir; bunlar ikinci parametre olarak aktarılır.

Böylece özel bir doğrulayıcı kümesi, statik metotlar içeren bir sınıf olarak yazılabilir:

class MyValidators
{
	// değerin argümana bölünüp bölünmediğini sınar
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// diğer doğrulayıcılar
	}
}

Kullanımı ise çok dolaysızdır:

$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'Değer %d sayısının katı olmalıdır',
		8,
	);

Özel doğrulama kuralları JavaScript'e de eklenebilir. Koşul, kuralın statik bir metot olmasıdır. JavaScript doğrulayıcısı için adı; sınıf adının ters eğik çizgiler \ olmadan, bir alt çizgi _ ve metot adının birleştirilmesiyle oluşur. Örneğin App\MyValidators::validateDivisibility, AppMyValidators_validateDivisibility olarak yazılır ve Nette.validators nesnesine eklenir:

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

onValidate Olayı

Form gönderildikten sonra doğrulama yapılır, addRule() ile eklenen tek tek kurallar denetlenir ve ardından onValidate olayı tetiklenir. İşleyicisi ek doğrulama için kullanılabilir; tipik olarak birden çok form öğesindeki değerlerin doğru bileşimini doğrulamak için.

Bir hata saptanırsa, addError() metoduyla forma aktarılır. Bu metot ya belirli bir öğede ya da doğrudan formda çağrılabilir.

protected function createComponentSignInForm(): Form
{
	$form = new Form;
	// ...
	$form->onValidate[] = $this->validateSignInForm(...);
	return $form;
}

private function validateSignInForm(Form $form, \stdClass $data): void
{
	if ($data->foo > 1 && $data->bar > 5) {
		$form->addError('Bu bileşim olanaklı değil.');
	}
}

İşleme Hataları

Pek çok durumda bir hatayı ancak geçerli bir formu işlerken keşfederiz; örneğin veritabanına yeni bir kayıt yazarken yinelenen bir anahtarla karşılaşınca. Böyle bir durumda hatayı yine addError() metoduyla forma geri aktarırız. Bu metot ya belirli bir öğede ya da doğrudan formda çağrılabilir:

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('Geçersiz parola.');
	}
}

Olanaklıysa hatayı doğrudan form öğesine eklemenizi öneririz; çünkü varsayılan renderer kullanıldığında onun yanında görüntülenir.

$form['date']->addError('Üzgünüz, bu tarih zaten alınmış.');

Bir forma ya da öğeye birden çok hata mesajı aktarmak için addError() metodunu defalarca çağırabilirsiniz. Onları getErrors() ile alabilirsiniz.

$form->getErrors() metodunun, yalnızca doğrudan forma değil, tek tek öğelere aktarılanlar da dahil olmak üzere tüm hata mesajlarının bir özetini döndürdüğüne dikkat edin. Yalnızca forma aktarılan hata mesajları $form->getOwnErrors() ile alınabilir.

Girdi Değerlerini Değiştirme

addFilter() metoduyla, kullanıcının girdiği değeri değiştirebiliriz. Bu örnekte posta kodundaki boşluklara göz yumup onları kaldıracağız:

$form->addText('zip', 'Posta kodu:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // posta kodundaki boşlukları kaldır
	})
	->addRule($form::Pattern, 'Posta kodu beş rakam değil', '\d{5}');

Filtre, doğrulama kuralları ve koşulları arasına katılır, dolayısıyla metotların sırası önemlidir; yani filtre ve kural, addFilter() ile addRule() metotlarının sıralandığı düzende çağrılır.

JavaScript Doğrulaması

Koşulları ve kuralları ifade etme dili çok güçlüdür. Tüm yapılar hem sunucu tarafında hem de istemci tarafında JavaScript'te çalışır. data-nette-rules HTML niteliklerinde JSON olarak aktarılırlar. Doğrulamanın kendisini, formun submit olayını yakalayan, tek tek öğeleri dolaşan ve ilgili doğrulamayı yapan bir betik üstlenir.

Bu betik netteForms.js'tir ve birkaç olası kaynaktan edinilebilir:

Betiği bir CDN'den doğrudan HTML sayfasına gömebilirsiniz:

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

Ya da onu projenizin genel klasörüne yerel olarak kopyalayabilirsiniz (örneğin vendor/nette/forms/src/assets/netteForms.min.js dosyasından):

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

Ya da onu npm ile kurabilirsiniz:

npm install nette-forms

Ve sonra yükleyip çalıştırabilirsiniz:

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

Alternatif olarak onu doğrudan vendor klasöründen yükleyebilirsiniz:

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

Forma novalidate niteliğini ekleyerek istemci tarafı doğrulamayı tümüyle kapatabilirsiniz. netteForms.js betiği o zaman gönderimde formu doğrulamayı atlar, dolayısıyla doğrulama yalnızca sunucuda yapılır:

$form->setHtmlAttribute('novalidate');

Dinamik JavaScript

Adres alanlarını yalnızca kullanıcı malların postayla gönderilmesini seçtiğinde göstermek mi istiyorsunuz? Sorun değil. Anahtar, addCondition() ve toggle() metot çiftidir:

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

Bu kod, koşul sağlandığında (yani onay kutusu işaretlendiğinde) #address-container HTML elemanının görünür olacağını, tersi durumda ise görünmeyeceğini belirtir. Yani alıcının adresini içeren form öğelerini bu ID'ye sahip bir container'a koyarız ve onay kutusuna tıklandığında gizlenip görünürler. Bunu netteForms.js betiği üstlenir.

toggle() metoduna argüman olarak herhangi bir seçici aktarılabilir. Tarihsel nedenlerle, harf, rakam ya da alt çizgiyle başlayan ve yalnızca harf, rakam, alt çizgi, tire, nokta ve iki nokta içeren bir dize, sanki başında # karakteri varmış gibi eleman ID'si sayılır. İsteğe bağlı ikinci parametre davranışı tersine çevirmeye olanak tanır; örneğin toggle('#address-container', false) kullansaydık, eleman yalnızca onay kutusu işaretli değilse görüntülenirdi.

Varsayılan JavaScript gerçekleştirimi, elemanların hidden özelliğini değiştirir. Ancak davranışı, örneğin bir animasyon ekleyerek kolayca değiştirebiliriz. Yalnızca JavaScript'te Nette.toggle metodunu kendi çözümünüzle geçersiz kılın:

Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// 'visible' değerine göre 'el' elemanını gizle ya da göster
	});
};

Doğrulamayı Kapatma

Bazen doğrulamayı kapatmak yararlı olabilir. Bir gönder düğmesine basmak doğrulama yapmamalıysa (İptal ya da Önizleme düğmeleri için uygundur), onu $submit->setValidationScope([]) metoduyla kapatırız. Yalnızca kısmi doğrulama yapmalıysa, hangi alanların ya da form container'larının doğrulanacağını belirtebiliriz.

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

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

$form->addSubmit('send1'); // Formun tamamını doğrular
$form->addSubmit('send2')
	->setValidationScope([]); // Hiçbir şeyi doğrulamaz
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Yalnızca 'name' öğesini doğrular
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Yalnızca 'age' öğesini doğrular
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // 'details' container'ını doğrular

setValidationScope, formdaki onValidate Olayı olayını etkilemez; o her zaman çağrılır. Bir container'daki onValidate olayı yalnızca o container kısmi doğrulama için işaretlenmişse tetiklenir.

Kısmi doğrulama, getValues() metodunun döndürdüğü değerleri de etkiler: sonuç yalnızca doğrulama kapsamına giren öğelerin değerlerini içerir. Bu kapsamın dışındaki öğelerin değerleri atlanır.

versiyon: 4.x