Formları Birden Fazla Yerde Yeniden Kullanma
Nette, aynı formu kod kopyalamadan birden fazla yerde yeniden kullanmak için çeşitli seçeneklere sahipsiniz. Bu makalede, kaçınmanız gerekenler de dahil olmak üzere farklı çözümlerin üzerinden geçeceğiz.
Form Fabrikası
Aynı bileşeni birden fazla yerde kullanmaya yönelik temel yaklaşımlardan biri, bileşeni oluşturan bir yöntem veya sınıf oluşturmak ve daha sonra bu yöntemi uygulamanın farklı yerlerinde çağırmaktır. Böyle bir yöntem veya sınıf factory olarak adlandırılır. Lütfen fabrikaları kullanmanın belirli bir yolunu açıklayan ve bu konuyla ilgili olmayan factory method tasarım modeliyle karıştırmayın.
Örnek olarak, bir düzenleme formu oluşturacak bir fabrika oluşturalım:
use Nette\Application\UI\Form;
class FormFactory
{
public function createEditForm(): Form
{
$form = new Form;
$form->addText('title', 'Title:');
// ek form alanları buraya eklenir
$form->addSubmit('send', 'Save');
return $form;
}
}
Artık bu fabrikayı uygulamanızın farklı yerlerinde, örneğin sunumlarda veya bileşenlerde kullanabilirsiniz. Ve bunu bir bağımlılık olarak talep ederek yapıyoruz. Bu yüzden önce sınıfı yapılandırma dosyasına yazacağız:
services:
- FormFactory
Ve sonra bunu sunumda kullanıyoruz:
class MyPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private FormFactory $formFactory,
) {
}
protected function createComponentEditForm(): Form
{
$form = $this->formFactory->createEditForm();
$form->onSuccess[] = function () {
// gönderilen verilerin işlenmesi
};
return $form;
}
}
Uygulamanıza uygun diğer form türlerini oluşturmak için form fabrikasını ek yöntemlerle genişletebilirsiniz. Ve elbette, diğer yöntemlerin kullanacağı öğeler olmadan temel bir form oluşturan bir yöntem ekleyebilirsiniz:
class FormFactory
{
public function createForm(): Form
{
$form = new Form;
return $form;
}
public function createEditForm(): Form
{
$form = $this->createForm();
$form->addText('title', 'Title:');
// ek form alanları buraya eklenir
$form->addSubmit('send', 'Save');
return $form;
}
}
createForm()
yöntemi henüz yararlı bir şey yapmıyor, ancak bu hızla değişecek.
Fabrika Bağımlılıkları
Zamanla, formların çok dilli olması gerektiği ortaya çıkacaktır. Bu, tüm formlar için bir çevirmen ayarlamamız gerektiği anlamına gelir. Bunu yapmak
için FormFactory
sınıfını, Translator
nesnesini kurucuda bir bağımlılık olarak kabul edecek ve
forma aktaracak şekilde değiştiriyoruz:
use Nette\Localization\Translator;
class FormFactory
{
public function __construct(
private Translator $translator,
) {
}
public function createForm(): Form
{
$form = new Form;
$form->setTranslator($this->translator);
return $form;
}
//...
}
createForm()
yöntemi, belirli formları oluşturan diğer yöntemler tarafından da çağrıldığından,
çevirmeni yalnızca bu yöntemde ayarlamamız gerekir. Ve işimiz bitti. Herhangi bir sunumcu veya bileşen kodunu değiştirmeye
gerek yok, ki bu harika.
Daha Fazla Fabrika Sınıfı
Alternatif olarak, uygulamanızda kullanmak istediğiniz her form için birden fazla sınıf oluşturabilirsiniz. Bu yaklaşım
kodun okunabilirliğini artırabilir ve formların yönetimini kolaylaştırabilir. Temel yapılandırmaya sahip (örneğin
çeviri destekli) saf bir form oluşturmak için orijinal FormFactory
adresini bırakın ve düzenleme formu için
yeni bir fabrika EditFormFactory
oluşturun.
class FormFactory
{
public function __construct(
private Translator $translator,
) {
}
public function create(): Form
{
$form = new Form;
$form->setTranslator($this->translator);
return $form;
}
}
// ✅ kompozisyon kullanımı
class EditFormFactory
{
public function __construct(
private FormFactory $formFactory,
) {
}
public function create(): Form
{
$form = $this->formFactory->create();
// ek form alanları buraya eklenir
$form->addSubmit('send', 'Save');
return $form;
}
}
FormFactory
ve EditFormFactory
sınıfları arasındaki bağın nesne kalıtımı ile değil, bileşim ile uygulanması çok
önemlidir:
// ⛔ HAYIR! MİRAS BURAYA AİT DEĞİL
class EditFormFactory extends FormFactory
{
public function create(): Form
{
$form = parent::create();
$form->addText('title', 'Title:');
// ek form alanları buraya eklenir
$form->addSubmit('send', 'Save');
return $form;
}
}
Bu durumda kalıtım kullanmak tamamen ters etki yaratacaktır. Çok hızlı bir şekilde sorunlarla karşılaşırsınız.
Örneğin, create()
yöntemine parametre eklemek isterseniz; PHP, imzasının ebeveynden farklı olduğuna dair bir
hata bildirecektir. Ya da EditFormFactory
sınıfına yapıcı aracılığıyla bir bağımlılık aktarırken. Bu,
yapıcı cehennemi
dediğimiz şeye neden olur.
Genellikle kalıtım yerine bileşimi tercih etmek daha iyidir.
Form İşleme
Başarılı bir gönderim sonrasında çağrılan form işleyici de bir fabrika sınıfının parçası olabilir. Gönderilen
verileri işlenmek üzere modele aktararak çalışacaktır. Herhangi bir hatayı forma geri iletecektir. Aşağıdaki örnekteki model
Facade
sınıfı tarafından temsil edilmektedir:
class EditFormFactory
{
public function __construct(
private FormFactory $formFactory,
private Facade $facade,
) {
}
public function create(): Form
{
$form = $this->formFactory->create();
$form->addText('title', 'Title:');
// ek form alanları buraya eklenir
$form->addSubmit('send', 'Save');
$form->onSuccess[] = [$this, 'processForm'];
return $form;
}
public function processForm(Form $form, array $data): void
{
try {
// sunulan veri̇leri̇n i̇şlenmesi̇
$this->facade->process($data);
} catch (AnyModelException $e) {
$form->addError('...');
}
}
}
Sunucunun yeniden yönlendirmeyi kendisinin yapmasına izin verin. Yeniden yönlendirmeyi gerçekleştirecek olan
onSuccess
olayına başka bir işleyici ekleyecektir. Bu, formun farklı sunumcularda kullanılmasına olanak
tanıyacak ve her biri farklı bir konuma yönlendirebilecektir.
class MyPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private EditFormFactory $formFactory,
) {
}
protected function createComponentEditForm(): Form
{
$form = $this->formFactory->create();
$form->onSuccess[] = function () {
$this->flashMessage('Záznam byl uložen');
$this->redirect('Homepage:');
};
return $form;
}
}
Bu çözüm, bir form veya öğesi üzerinde addError()
çağrıldığında, bir sonraki onSuccess
işleyicisinin çağrılmaması şeklindeki form özelliğinden yararlanır.
Form Sınıfından Devralma
Oluşturulan bir formun bir formun çocuğu olmaması gerekir. Başka bir deyişle, bu çözümü kullanmayın:
// ⛔ HAYIR! MİRAS BURAYA AİT DEĞİL
class EditForm extends Form
{
public function __construct(Translator $translator)
{
parent::__construct();
$form->addText('title', 'Title:');
// ek form alanları buraya eklenir
$form->addSubmit('send', 'Save');
$form->setTranslator($translator);
}
}
Formu yapıcıda oluşturmak yerine fabrikayı kullanın.
Form
sınıfının öncelikle bir formu bir araya getirmek için bir araç, yani bir form oluşturucu olduğunu
anlamak önemlidir. Ve birleştirilmiş form onun ürünü olarak düşünülebilir. Ancak, ürün oluşturucunun özel bir durumu
değildir; aralarında kalıtımın temelini oluşturan is a ilişkisi yoktur.
Form Bileşeni
Tamamen farklı bir yaklaşım, bir form içeren bir bileşen oluşturmaktır. Bu, örneğin bileşen bir şablon içerdiğinden formu belirli bir şekilde oluşturmak gibi yeni olanaklar sağlar. Veya sinyaller AJAX iletişimi ve forma bilgi yüklemek için kullanılabilir, örneğin ipucu vb. için.
use Nette\Application\UI\Form;
class EditControl extends Nette\Application\UI\Control
{
public array $onSave = [];
public function __construct(
private Facade $facade,
) {
}
protected function createComponentForm(): Form
{
$form = new Form;
$form->addText('title', 'Title:');
// ek form alanları buraya eklenir
$form->addSubmit('send', 'Save');
$form->onSuccess[] = [$this, 'processForm'];
return $form;
}
public function processForm(Form $form, array $data): void
{
try {
// sunulan veri̇leri̇n i̇şlenmesi̇
$this->facade->process($data);
} catch (AnyModelException $e) {
$form->addError('...');
return;
}
// olay çağrısı
$this->onSave($this, $data);
}
}
Bu bileşeni üretecek bir fabrika oluşturalım. Arayüzünü yazmak yeterli:
interface EditControlFactory
{
function create(): EditControl;
}
Ve yapılandırma dosyasına ekleyin:
services:
- EditControlFactory
Ve şimdi fabrikayı talep edebilir ve sunumda kullanabiliriz:
class MyPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private EditControlFactory $controlFactory,
) {
}
protected function createComponentEditForm(): EditControl
{
$control = $this->controlFactory->create();
$control->onSave[] = function (EditControl $control, $data) {
$this->redirect('this');
// veya düzenleme sonucuna yönlendirin, örn:
// $this->redirect('detail', ['id' => $data->id]);
};
return $control;
}
}