Znovupoužití formulářů na více místech

Jak použít stejný formulář na více místech a neduplikovat kód? To je v Nette opravdu snadné a máte na výběr víc způsobů.

Továrna na formulář

Vytvoříme si třídu, která umí formulář vyrobit. Takové třídě se říká továrna. V místě, kde budeme chtít formulář použít (např. v presenteru), si továrnu vyžádáme jako závislosti.

Součástí továrny je i kód, který po úspěšném odeslaní formuláře předá data k dalšímu zpracování. Obvykle do modelové vrstvy. Zároveň zkontroluje, zda vše proběhlo v pořádku, a případné chyby předá zpět do formuláře. Model v následujícím příkladu reprezentuje třída Facade:

use Nette\Application\UI\Form;

class EditFormFactory
{
	private $facade;

	public function __construct(Facade $facade)
	{
		$this->facade = $facade;
	}

	public function create(/* parametry */): Form
	{
		$form = new Form;

		// přidáme prvky do formuláře

		$form->addSubmit('send', 'Odeslat');
		$form->onSuccess[] = [$this, 'processForm'];

		return $form;
	}

	public function processForm(Form $form, array $values): void
	{
		try {
			// zpracování formuláře
			$this->facade->process($values);

		} catch (AnyModelException $e) {
			$form->addError('...');
		}
	}
}

Továrna může být samozřejmě parametrická, tj. může přijímat parametery, které ovlivní podobu vytvářeného formuláře.

Nyní si ukážeme předání továrny do presenteru. Nejprve ji zapíšeme do konfiguračního souboru:

services:
	- EditFormFactory

A poté vyžádáme v presenteru. Tam také následuje další krok zpracování odeslaného formuláře a tím je přesměrování na další stránku:

class MyPresenter extends Nette\Application\UI\Presenter
{
	private $formFactory;

	public function __construct(EditFormFactory $formFactory)
	{
		$this->formFactory = $formFactory;
	}

	protected function createComponentEditForm(): Form
	{
		$form = $this->formFactory->create();

		$form->onSuccess[] = function (Form $form) {
			$this->redirect('this');
		};

		return $form;
	}
}

Tím, že přesměrování řeší až handler v presenteru, lze komponentu použít na více místech a na každém přesměrovat jinam.

Komponenta s formulářem

Další možností je vytvořit novou komponentu, jejíž součástí bude formulář. To nám dává možnost například renderovat formulář specifickým způsobem, neboť součástí komponenty je i šablona. Nebo lze využít signály pro AJAXovou komunikaci a donačítání informací do formuláře, například pro napovídání, atd.

use Nette\Application\UI\Form;

class EditControl extends Nette\Application\UI\Control
{
	public $onSave;

	private $facade;

	public function __construct(Facade $facade)
	{
		$this->facade = $facade;
	}

	protected function createComponentForm(): Form
	{
		$form = new Form;

		// přidáme prvky do formuláře

		$form->addSubmit('send', 'Odeslat');
		$form->onSuccess[] = [$this, 'processForm'];

		return $form;
	}

	public function processForm(Form $form, array $values): void
	{
		try {
			// zpracování formuláře
			$this->facade->process($values);

		} catch (AnyModelException $e) {
			$form->addError('...');
			return;
		}

		// vyvolání události
		$this->onSave($this, $values);
	}
}

Ještě vytvoříme továrnu, která bude tuto komponentu vyrábět. Stačí zapsat její rozhraní:

interface EditControlFactory
{
	function create(): EditControl;
}

A přidat do konfiguračního souboru:

services:
	- EditControlFactory

A nyní už můžeme továrnu vyžádat a použít v presenteru:

class MyPresenter extends Nette\Application\UI\Presenter
{
	private $controlFactory;

	public function __construct(EditControlFactory $controlFactory)
	{
		$this->controlFactory = $controlFactory;
	}

	protected function createComponentEditForm(): Form
	{
		$control = $this->controlFactory->create();

		$control->onSave[] = function (EditControl $control, $data) {
			$this->redirect('this');
			// nebo přesměrujeme na výsledek editace, např.:
			// $this->redirect('detail', ['id' => $data->id]);
		};

		return $control;
	}
}