Fomulář ve factory – uložený záznam v presenteru
- motorcb
- Člen | 552
Zdravím,
mám fomuláře ve faktory, který uloží záznam do DB. To je OK:
public function create(callable $onSuccess): Form
{
$form = $this->factory->create();
$form->addText('text', 'Text');
$form->addSubmit('submit', 'Uložit');
$form->onSuccess[] = function (Form $form, \stdClass $values) use ($onSuccess): void {
$textId = $this->repository->insert($values->text);
$onSuccess();
};
return $form;
}
Jenže já bych potřeboval po uložení přesměrovat v presenteru na detail nově vytvořeného záznamu:
protected function createComponentSignInForm(): Form
{
return $this->textFormFactory->create(function (): void {
$this->redirect('Text:detail');//@TODO jak tady zjistim ID vytvoreneho zaznamu? $textId z metody create ve factory?
});
}
Jak zjistím v presenteru ID vytvořeného záznamu abych přesměroval na detail nově vytvořeného záznamu? Díky
- Kamil Valenta
- Člen | 822
public function create(callable $onSuccess): Form
{
$form = $this->factory->create();
$form->addText('text', 'Text');
$form->addSubmit('submit', 'Uložit');
$form->onSuccess[] = function (Form $form, \stdClass $values) use ($onSuccess): void {
$textId = $this->repository->insert($values->text);
$onSuccess($textId);
};
return $form;
}
protected function createComponentSignInForm(): Form
{
return $this->textFormFactory->create(function ($textId): void {
dump($textId);
$this->redirect('Text:detail');//@TODO jak tady zjistim ID vytvoreneho zaznamu? $textId z metody create ve factory?
});
}
Za předpokladu, že $this->repository->insert() vrací nově vytvořené id.
- m.brecher
- Generous Backer | 873
@motorcb
Ahoj, zrovna i já teď zkouším factory pro výrobu nette formulářů. Před pár dny mě @jiripudil, doporučil místo klasické factory, která vrací „holý“ Form z Nette použít „magickou“ factory ve formě „form factory interface“, která vrací Nette\Application\UI\Form obalený komponentou, která dědí z Nette\Application\UI\Control.
Po důkladném vyzkoušení tuto techniku VELMI doporučuji – nejlepší způsob jak s formuláři Nette inteligentně pracovat. Kdyby se to mělo použít ve Tvém příkladu, tak by to vypadalo nějak takto:
1. MyForm jako komponenta presenteru Form je schován uvnitř
use Nette\Application\UI\Control;
use Nette\Application\UI\Form;
use Nette\Utils\ArrayHash;
class MyForm extends Control // MyForm je komponenta, nikoliv služba DI
{
public function __construct(
private ParamType $param, // parametr předaný z presenteru
private Repository $repository, // registrovat Repository jako službu DI
)
{}
public function createComponentBaseForm(): Form // Control obsahuje Nette\Application\UI\Form
{
$form = new Form;
$form->addText('text', 'Text');
$form->addSubmit('submit', 'Uložit');
$form->onSuccess[] = function(ArrayHash $values) { // ArrayHash nebo \stdClass
$id = $this->repository->insert($values->text);
$this->presenter->redirect('Text:detail', ['id' => $id]); // Control umí presenter
};
return $form;
}
public function render(): void // nastavení vykreslení komponenty
{
$this['baseForm']->render(); // 1. varianta DefaultRenderer Nette
$this->template->render('..../myFormTemplate.latte'); // 2. varianta s vlastní šablonou
}
}
2. Factory která vytvoří MyForm, s využitím magie Nette přes interface factory
interface MyFormFactory // registrovat MyFormFactory jako službu DI
{
public function create(ParamType $param): MyForm; // parametr předaný z presenteru do formuláře
}
Poznámka: nelámat si hlavu s tím, že to není class, Nette class pro factory vyrobí automaticky !!
3. registrovat komponentu MyForm v presenteru pomocí MyFormFactory
use Nette\Application\UI\Presenter;
final class MyPresenter extends Presenter
{
public function __construct(
.......
private MyFormFactory $myFormFactory, // získání MyFormFactory autowiringem z DI služeb
)
{}
public function createComponentLoginForm(): MyForm // v presenteru není Form, ale MyForm
{
return $this->myFormFactory->create(ParamType $param); // předání parameru do MyForm
}
}
3. formulář správně vykreslit – vlastní šablonou, nebo default rendererem
šablona kam vykreslíme formulář
{control myForm} // 1. varianta - default renderer
{form baseForm} // 2. varianta - šablona myFormTemplate.latte
..... vykreslení prvků formuláře
{/form}
Snad jsem na nic nezapomněl. Funguje to výborně a má to řadu dalších výhod do budoucna – v komponentě lze např. použít persistentní parametry pro uložení stavu formuláře (složitější interaktivní formuláře), oddělené vykreslení od zbytku layoutu, možnost použít presenter uvnitř factory (není nutné předávat callback), a další a další výhody.
Stručně řečeno to spojuje výhody designu Factory s výhodami Nette komponentového konceptu.
Velký dík @jiripudil za propagaci tohoto designu, protože mě jako normálního člověka by nikdy nenapadlo řešit problémy balením formuláře do komponenty :).
Není žádný problém přesměrovat MyForm místo v komponentě v presenteru, ale pokud se má přesměrovat na id nového záznamu, jehož vytváření se volá v komponentě, tak absolutně nedává smysl předávat id do presenteru a teprve tam přesměrovat. A právě s klasickou factory jak zkoušíš tohle nejde :(.
Ale někdy se hodí přesměrovat až v presenteru – velmi snadné:
use Nette\Application\UI\Presenter;
final class MyPresenter extends Presenter
{
public function __construct(
.......
private MyFormFactory $myFormFactory,
)
{}
public function createComponentLoginForm(): MyForm
{
$form = $this->myFormFactory->create(ParamType $param);
$form->onSuccess[] = fn() => $this->redirect($destination); // callback arrow funkce
$form->onSuccess[] = function(){$this->redirect($destination)}; // callback anonymní funkce
}
}
Magické jsou dvě věci: a) vlastní parametr předaný v presenteru do metody create() se přes magicky vytvořenou factory předá do konstruktoru komponenty MyForm. b) Místo class Factory píšeme interface pro Factory.
Poznámka: do konstruktoru MyForm se vedle vlastních parametrů klasickým autowiringem předají i služby z DI.
Editoval m.brecher (13. 10. 2022 4:14)