Formların Birden Fazla Yerde Yeniden Kullanımı
Nette'de, aynı formu birden fazla yerde kullanmak ve kodu tekrarlamamak için çeşitli seçenekleriniz vardır. Bu makalede, kaçınmanız gerekenler de dahil olmak üzere farklı çözümleri göstereceğiz.
Form Fabrikası
Aynı bileşeni birden fazla yerde kullanmanın temel yaklaşımlarından biri, bu bileşeni üreten bir metot veya sınıf oluşturmak ve ardından bu metodu uygulamanın farklı yerlerinde çağırmaktır. Böyle bir metoda veya sınıfa fabrika denir. Lütfen fabrikaların belirli bir kullanım şeklini tanımlayan ve bu konuyla ilgili olmayan factory method tasarım deseniyle karıştırmayın.
Örnek olarak, bir düzenleme formu oluşturacak bir fabrika yaratacağız:
use Nette\Application\UI\Form;
class FormFactory
{
public function createEditForm(): Form
{
$form = new Form;
$form->addText('title', 'Başlık:');
// buraya diğer form alanları eklenir
$form->addSubmit('send', 'Gönder');
return $form;
}
}
Şimdi bu fabrikayı uygulamanızın farklı yerlerinde, örneğin presenter'larda veya bileşenlerde kullanabilirsiniz. Bunu bağımlılık olarak talep edeceğiz. Önce sınıfı yapılandırma dosyasına yazarız:
services:
- FormFactory
Ve sonra onu presenter'da kullanırız:
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;
}
}
Form fabrikasını, uygulamanızın ihtiyaçlarına göre diğer tür formları oluşturmak için ek metotlarla genişletebilirsiniz. Ve tabii ki, öğeler olmadan temel bir form oluşturan ve diğer metotların kullanacağı bir metot da ekleyebiliriz:
class FormFactory
{
public function createForm(): Form
{
$form = new Form;
return $form;
}
public function createEditForm(): Form
{
$form = $this->createForm();
$form->addText('title', 'Başlık:');
// buraya diğer form alanları eklenir
$form->addSubmit('send', 'Gönder');
return $form;
}
}
createForm()
metodu 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 formlara sözde çevirmeni ayarlamamız gerektiği anlamına gelir. Bu amaçla,
FormFactory
sınıfını yapıcıda Translator
nesnesini bir bağımlılık olarak kabul edecek şekilde
değiştiririz ve onu forma iletiriz:
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()
metodu diğer belirli formları oluşturan metotlar tarafından da çağrıldığı için, çevirmeni
yalnızca bu metotta ayarlamak yeterlidir. Ve işimiz bitti. Herhangi bir presenter veya bileşenin kodunu değiştirmeye gerek
yok, ki bu harika.
Birden 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, kod okunabilirliğini artırabilir ve form yönetimini kolaylaştırabilir. Orijinal FormFactory
'yi
yalnızca temel yapılandırmaya sahip (örneğin çeviri desteği ile) temiz bir form oluşturmak için bırakırız ve
düzenleme formu için yeni bir EditFormFactory
fabrikası oluştururuz.
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();
// buraya diğer form alanları eklenir
$form->addSubmit('send', 'Gönder');
return $form;
}
}
FormFactory
ve EditFormFactory
sınıfları arasındaki bağın nesne kalıtımı yerine kompozisyon ile gerçekleştirilmesi
çok önemlidir:
// ⛔ BU ŞEKİLDE DEĞİL! KALITIM BURAYA AİT DEĞİL
class EditFormFactory extends FormFactory
{
public function create(): Form
{
$form = parent::create();
$form->addText('title', 'Başlık:');
// buraya diğer form alanları eklenir
$form->addSubmit('send', 'Gönder');
return $form;
}
}
Bu durumda kalıtım kullanmak tamamen verimsiz olurdu. Çok hızlı bir şekilde sorunlarla karşılaşırdınız. Örneğin,
create()
metoduna parametreler eklemek istediğinizde; PHP, imzasının ebeveyninden farklı olduğuna dair bir hata
bildirirdi. Veya EditFormFactory
sınıfına yapıcı aracılığıyla bir bağımlılık iletirken. Yapıcı cehennemi dediğimiz
bir durum ortaya çıkardı.
Genel olarak, kalıtım yerine kompozisyonu tercih etmek daha iyidir.
Form İşleme
Başarılı bir gönderimden sonra çağrılan form işleyicisi de fabrika sınıfının bir parçası olabilir. Gönderilen
verileri işlenmek üzere modele ileterek çalışacaktır. Olası hataları forma geri iletir. Aşağıdaki örnekte 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', 'Başlık:');
// buraya diğer form alanları eklenir
$form->addSubmit('send', 'Gönder');
$form->onSuccess[] = [$this, 'processForm'];
return $form;
}
public function processForm(Form $form, array $data): void
{
try {
// gönderilen verilerin işlenmesi
$this->facade->process($data);
} catch (AnyModelException $e) {
$form->addError('...');
}
}
}
Ancak yönlendirmeyi presenter'a bırakacağız. onSuccess
olayına yönlendirmeyi gerçekleştirecek başka bir
işleyici ekleyecektir. Bu sayede formu farklı presenter'larda kullanmak ve her birinde farklı bir yere yönlendirmek mümkün
olacaktır.
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('Kayıt kaydedildi');
$this->redirect('Homepage:');
};
return $form;
}
}
Bu çözüm, form veya öğesi üzerinde addError()
çağrıldığında sonraki onSuccess
işleyicisinin çağrılmaması özelliğini kullanır.
Form Sınıfından Kalıtım Alma
Oluşturulan form, formun bir alt sınıfı olmamalıdır. Başka bir deyişle, bu çözümü kullanmayın:
// ⛔ BU ŞEKİLDE DEĞİL! KALITIM BURAYA AİT DEĞİL
class EditForm extends Form
{
public function __construct(Translator $translator)
{
parent::__construct();
$form->addText('title', 'Başlık:');
// buraya diğer form alanları eklenir
$form->addSubmit('send', 'Gönder');
$form->setTranslator($translator);
}
}
Formu yapıcıda oluşturmak yerine bir fabrika kullanın.
Form
sınıfının öncelikle bir form oluşturma aracı, yani bir form oluşturucu olduğu
unutulmamalıdır. Ve oluşturulan form, onun bir ü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 bir is a ilişkisi yoktur.
Form İçeren Bileşen
Tamamen farklı bir yaklaşım, form içeren bir bileşen oluşturmaktır. Bu, örneğin formu belirli bir şekilde oluşturmak gibi yeni olanaklar sunar, çünkü bileşenin bir parçası olarak bir şablon da bulunur. Veya AJAX iletişimi ve forma bilgi yükleme, örneğin öneriler için sinyaller kullanılabilir, vb.
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', 'Başlık:');
// buraya diğer form alanları eklenir
$form->addSubmit('send', 'Gönder');
$form->onSuccess[] = [$this, 'processForm'];
return $form;
}
public function processForm(Form $form, array $data): void
{
try {
// gönderilen verilerin işlenmesi
$this->facade->process($data);
} catch (AnyModelException $e) {
$form->addError('...');
return;
}
// olayın tetiklenmesi
$this->onSave($this, $data);
}
}
Bu bileşeni üretecek bir fabrika da oluşturacağız. Sadece arayüzünü yazmanız yeterlidir:
interface EditControlFactory
{
function create(): EditControl;
}
Ve yapılandırma dosyasına ekleyin:
services:
- EditControlFactory
Ve şimdi fabrikayı talep edebilir ve presenter'da 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önlendiririz, örn.:
// $this->redirect('detail', ['id' => $data->id]);
};
return $control;
}
}