Formları Tek Başına Kullanma

Nette Forms, web formları oluşturmayı ve işlemeyi önemli ölçüde kolaylaştırır. Bunları, bu bölümde göstereceğimiz gibi, framework'ün geri kalanı olmadan uygulamalarınızda tamamen bağımsız olarak kullanabilirsiniz.

Ancak Nette Application ve presenter'ları kullanıyorsanız, presenter'larda kullanım kılavuzu sizin içindir.

İlk Form

Basit bir kayıt formu yazmaya çalışalım. Kodu aşağıdaki gibi olacaktır (tüm kod):

use Nette\Forms\Form;

$form = new Form;
$form->addText('name', 'İsim:');
$form->addPassword('password', 'Şifre:');
$form->addSubmit('send', 'Kayıt Ol');

Çok kolay bir şekilde oluşturabiliriz:

$form->render();

ve tarayıcıda şöyle görünecektir:

Form, Nette\Forms\Form sınıfının bir nesnesidir (Nette\Application\UI\Form sınıfı presenter'larda kullanılır). Buna isim, şifre ve gönderme düğmesi gibi öğeler ekledik.

Şimdi formu canlandıralım. $form->isSuccess() sorgusuyla formun gönderilip gönderilmediğini ve geçerli bir şekilde doldurulup doldurulmadığını öğreneceğiz. Eğer öyleyse, verileri yazdıracağız. Form tanımından sonra şunu ekleyeceğiz:

if ($form->isSuccess()) {
	echo 'Form doğru bir şekilde dolduruldu ve gönderildi';
	$data = $form->getValues();
	// $data->name ismi içerir
	// $data->password şifreyi içerir
	var_dump($data);
}

getValues() metodu, gönderilen verileri ArrayHash nesnesi biçiminde döndürür. Bunun nasıl değiştirileceğini daha sonra göstereceğiz. $data nesnesi, kullanıcının doldurduğu verilerle birlikte name ve password anahtarlarını içerir.

Genellikle verileri doğrudan daha fazla işleme göndeririz, bu da örneğin veritabanına ekleme olabilir. Ancak işleme sırasında bir hata oluşabilir, örneğin kullanıcı adı zaten alınmış olabilir. Bu durumda, hatayı addError() kullanarak forma geri iletiriz ve hata mesajıyla birlikte yeniden oluşturulmasını sağlarız.

$form->addError('Üzgünüz, bu kullanıcı adı zaten kullanılıyor.');

Formu işledikten sonra bir sonraki sayfaya yönlendiririz. Bu, yenile, geri düğmesiyle veya tarayıcı geçmişinde gezinerek formun istenmeyen şekilde yeniden gönderilmesini önler.

Form varsayılan olarak POST metoduyla aynı sayfaya gönderilir. Her ikisi de değiştirilebilir:

$form->setAction('/submit.php');
$form->setMethod('GET');

Ve aslında hepsi bu :-) İşlevsel ve mükemmel bir şekilde güvenli bir formumuz var.

Diğer form elemanları eklemeyi de deneyin.

Elemanlara Erişim

Formu ve tek tek elemanlarını bileşenler olarak adlandırırız. Kökü form olan bir bileşen ağacı oluştururlar. Formun tek tek elemanlarına şu şekilde erişebiliriz:

$input = $form->getComponent('name');
// alternatif sözdizimi: $input = $form['name'];

$button = $form->getComponent('send');
// alternatif sözdizimi: $button = $form['send'];

Elemanlar unset kullanılarak kaldırılır:

unset($form['name']);

Doğrulama Kuralları

Geçerli kelimesini kullandık, ancak formun henüz herhangi bir doğrulama kuralı yok. Bunu düzeltelim.

İsim zorunlu olacak, bu yüzden onu setRequired() metoduyla işaretleyeceğiz. Argümanı, kullanıcı ismi doldurmazsa görüntülenecek hata mesajının metnidir. Argüman belirtmezsek, varsayılan hata mesajı kullanılır.

$form->addText('name', 'İsim:')
	->setRequired('Lütfen bir isim girin');

Formu doldurulmuş bir isim olmadan göndermeyi deneyin ve bir hata mesajının görüntüleneceğini ve tarayıcının veya sunucunun alanı doldurana kadar reddedeceğini göreceksiniz.

Aynı zamanda, alana sadece boşluk yazarak sistemi aldatamazsınız. Hayır. Nette sol ve sağ boşlukları otomatik olarak kaldırır. Deneyin. Bu, her tek satırlık girişle her zaman yapmanız gereken bir şeydir, ancak genellikle unutulur. Nette bunu otomatik olarak yapar. (Formu aldatmayı deneyebilir ve isim olarak çok satırlı bir dize gönderebilirsiniz. Nette burada bile kafası karışmaz ve satır sonlarını boşluklara dönüştürür.)

Form her zaman sunucu tarafında doğrulanır, ancak aynı zamanda anında çalışan ve kullanıcının hatayı formu sunucuya göndermeye gerek kalmadan hemen öğrenmesini sağlayan JavaScript doğrulaması da oluşturulur. Bu, netteForms.js betiği tarafından halledilir. Sayfaya ekleyin:

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

Formu içeren sayfanın kaynak koduna bakarsanız, Nette'nin zorunlu elemanları required CSS sınıfına sahip elemanlara eklediğini fark edebilirsiniz. Şablona aşağıdaki stil sayfasını eklemeyi deneyin ve “İsim” etiketi kırmızı olacaktır. Bu şekilde, zorunlu elemanları kullanıcılara zarif bir şekilde işaretleriz:

<style>
.required label { color: maroon }
</style>

Diğer doğrulama kurallarını addRule() metoduyla ekleriz. İlk parametre kuraldır, ikincisi yine hata mesajının metnidir ve bunu doğrulama kuralının argümanı takip edebilir. Bu ne anlama geliyor?

Formu, tamsayı olması gereken (addInteger()) ve ayrıca izin verilen bir aralıkta olması gereken ($form::Range) yeni bir isteğe bağlı “yaş” alanı ile genişleteceğiz. Ve burada, doğrulayıcıya istenen aralığı [başlangıç, bitiş] çifti olarak ilettiğimiz addRule() metodunun üçüncü parametresini kullanacağız:

$form->addInteger('age', 'Yaş:')
	->addRule($form::Range, 'Yaş 18 ile 120 arasında olmalıdır', [18, 120]);

Kullanıcı alanı doldurmazsa, eleman isteğe bağlı olduğu için doğrulama kuralları kontrol edilmeyecektir.

Burada küçük bir yeniden düzenleme için yer var. Hata mesajında ve üçüncü parametrede sayılar yineleniyor, bu ideal değil. Sayıları içeren bir mesaj çok dilli formlar oluşturursak ve birden fazla dile çevrilirse, değerleri değiştirmek zorlaşır. Bu nedenle, %d yer tutucularını kullanmak mümkündür ve Nette değerleri tamamlayacaktır:

	->addRule($form::Range, 'Yaş %d ile %d arasında olmalıdır', [18, 120]);

password elemanına geri dönelim, onu da zorunlu hale getireceğiz ve ayrıca şifrenin minimum uzunluğunu ($form::MinLength) doğrulayacağız, yine yer tutucu kullanarak:

$form->addPassword('password', 'Şifre:')
	->setRequired('Bir şifre seçin')
	->addRule($form::MinLength, 'Şifre en az %d karakter uzunluğunda olmalıdır', 8);

Forma bir passwordVerify alanı daha ekleyelim, burada kullanıcı kontrol için şifreyi tekrar girecektir. Doğrulama kurallarını kullanarak her iki şifrenin de aynı olup olmadığını kontrol edeceğiz ($form::Equal). Ve parametre olarak, köşeli parantezler kullanarak ilk şifreye bir referans vereceğiz:

$form->addPassword('passwordVerify', 'Kontrol için şifre:')
	->setRequired('Lütfen kontrol için şifreyi tekrar girin')
	->addRule($form::Equal, 'Şifreler eşleşmiyor', $form['password'])
	->setOmitted();

setOmitted() kullanarak, değerine aslında önem vermediğimiz ve yalnızca doğrulama amacıyla var olan bir elemanı işaretledik. Değer $data'ya iletilmeyecektir.

Bununla, PHP ve JavaScript'te doğrulamaya sahip tamamen işlevsel bir formumuz var. Nette'nin doğrulama yetenekleri çok daha geniştir, koşullar oluşturabilir, bunlara göre sayfanın bölümlerini gösterebilir ve gizleyebilir vb. Her şeyi form doğrulama bölümünde öğreneceksiniz.

Varsayılan Değerler

Form elemanlarına genellikle varsayılan değerler atarız:

$form->addEmail('email', 'E-posta')
	->setDefaultValue($lastUsedEmail);

Genellikle tüm elemanlara aynı anda varsayılan değerler atamak kullanışlıdır. Örneğin, form kayıtları düzenlemek için kullanıldığında. Veritabanından kaydı okur ve varsayılan değerleri ayarlarız:

//$row = ['name' => 'John', 'age' => '33', /* ... */];
$form->setDefaults($row);

Elemanları tanımladıktan sonra setDefaults() çağırın.

Formu Oluşturma

Varsayılan olarak, form bir tablo olarak oluşturulur. Tek tek elemanlar temel erişilebilirlik kuralını karşılar – tüm etiketler <label> olarak yazılır ve ilgili form elemanıyla ilişkilendirilir. Etikete tıklandığında, imleç otomatik olarak form alanında görünür.

Her elemana herhangi bir HTML niteliği atayabiliriz. Örneğin, bir yer tutucu ekleyin:

$form->addInteger('age', 'Yaş:')
	->setHtmlAttribute('placeholder', 'Lütfen yaşınızı girin');

Bir formu oluşturmanın gerçekten çok sayıda yolu vardır, bu yüzden rendering hakkında ayrı bölüm buna ayrılmıştır.

Sınıflara Eşleme

Form verilerinin işlenmesine geri dönelim. getValues() metodu bize gönderilen verileri ArrayHash nesnesi olarak döndürdü. Bu genel bir sınıf olduğundan, stdClass gibi bir şey olduğundan, onunla çalışırken belirli bir rahatlıktan yoksun olacağız, örneğin editörlerde özelliklerin otomatik tamamlanması veya statik kod analizi. Bu, her form için özelliklerinin tek tek elemanları temsil ettiği belirli bir sınıfa sahip olarak çözülebilir. Örneğin:

class RegistrationFormData
{
	public string $name;
	public ?int $age;
	public string $password;
}

Alternatif olarak, bir kurucu kullanabilirsiniz:

class RegistrationFormData
{
	public function __construct(
		public string $name,
		public ?int $age,
		public string $password,
	) {
	}
}

Veri sınıfının özellikleri enum'lar da olabilir ve bunlar otomatik olarak eşlenir.

Nette'ye verileri bu sınıfın nesneleri olarak döndürmesini nasıl söyleyebiliriz? Düşündüğünüzden daha kolay. Sınıf adını veya hidratlanacak nesneyi parametre olarak belirtmeniz yeterlidir:

$data = $form->getValues(RegistrationFormData::class);
$name = $data->name;

Parametre olarak 'array' de belirtilebilir ve ardından veriler dizi olarak döndürülür.

Formlar konteynerlerden oluşan çok seviyeli bir yapı oluşturuyorsa, her biri için ayrı bir sınıf oluşturun:

$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;
}

Eşleme daha sonra $person özelliğinin türünden konteyneri PersonFormData sınıfına eşlemesi gerektiğini anlar. Özellik bir konteyner dizisi içeriyorsa, array türünü belirtin ve eşlenecek sınıfı doğrudan konteynere iletin:

$person->setMappedType(PersonFormData::class);

Form veri sınıfının tasarımını Nette\Forms\Blueprint::dataClass($form) metodunu kullanarak oluşturabilirsiniz, bu da onu tarayıcı sayfasına yazdırır. Ardından kodu tıklayarak işaretleyip projenize kopyalamanız yeterlidir.

Çoklu Düğmeler

Formun birden fazla düğmesi varsa, genellikle hangisinin basıldığını ayırt etmemiz gerekir. Düğmenin isSubmittedBy() metodu bize bu bilgiyi döndürür:

$form->addSubmit('save', 'Kaydet');
$form->addSubmit('delete', 'Sil');

if ($form->isSuccess()) {
	if ($form['save']->isSubmittedBy()) {
		// ...
	}

	if ($form['delete']->isSubmittedBy()) {
		// ...
	}
}

Verilerin geçerliliğini doğrulamak için $form->isSuccess() sorgusunu atlamayın.

Form Enter tuşuyla gönderildiğinde, ilk düğmeyle gönderilmiş gibi kabul edilir.

Güvenlik Açıklarına Karşı Koruma

Nette Framework güvenliğe büyük önem verir ve bu nedenle formların iyi bir şekilde güvence altına alınmasına özen gösterir.

Formları Cross Site Scripting (XSS) ve Cross-Site Request Forgery (CSRF) saldırılarına karşı korumanın yanı sıra, artık düşünmeniz gerekmeyen birçok küçük güvenlik önlemi alır.

Örneğin, girdilerden tüm kontrol karakterlerini filtreler ve UTF-8 kodlamasının geçerliliğini kontrol eder, böylece form verileri her zaman temiz olur. Seçim kutuları ve radyo listeleri için, seçilen öğelerin gerçekten sunulanlardan olduğunu ve sahtecilik yapılmadığını doğrular. Tek satırlık metin girişlerinde, bir saldırganın oraya göndermiş olabileceği satır sonu karakterlerini kaldırdığını zaten belirtmiştik. Çok satırlı girişler için ise satır sonu karakterlerini normalleştirir. Ve böyle devam eder.

Nette, birçok programcının varlığından bile haberdar olmadığı güvenlik risklerini sizin için çözer.

Bahsedilen CSRF saldırısı, bir saldırganın kurbanı, kurbanın oturum açtığı sunucuya fark ettirmeden bir istek gerçekleştiren bir sayfaya çekmesi ve sunucunun isteği kurbanın kendi isteğiyle gerçekleştirdiğini varsaymasıdır. Bu nedenle Nette, POST formunun başka bir alan adından gönderilmesini engeller. Herhangi bir nedenle korumayı kapatmak ve formun başka bir alan adından gönderilmesine izin vermek istiyorsanız, şunu kullanın:

$form->allowCrossOrigin(); // DİKKAT! Korumayı kapatır!

Bu koruma, _nss adlı SameSite çerezini kullanır. Bu nedenle, çerezin gönderilebilmesi için ilk çıktıyı göndermeden önce form nesnesini oluşturun.

SameSite çerezi ile koruma %100 güvenilir olmayabilir, bu nedenle token ile korumayı da etkinleştirmek iyi bir fikirdir:

$form->addProtection();

Uygulamadaki hassas verileri değiştiren web sitesinin yönetim bölümündeki formları bu şekilde korumanızı öneririz. Framework, oturumda saklanan bir yetkilendirme token'ı oluşturup doğrulayarak CSRF saldırısına karşı kendini savunur. Bu nedenle, formu görüntülemeden önce oturumun açık olması gerekir. Web sitesinin yönetim bölümünde, genellikle kullanıcının oturum açması nedeniyle oturum zaten başlatılmıştır. Aksi takdirde, oturumu Nette\Http\Session::start() metoduyla başlatın.

İşte Nette'deki formlara hızlı bir giriş yaptık. Daha fazla ilham almak için dağıtımdaki examples dizinine göz atmayı deneyin.

versiyon: 4.0