Ponowne wykorzystanie formularzy w wielu miejscach

Jak ponownie wykorzystać ten sam formularz w wielu miejscach i nie powielać kodu? W Nette jest to naprawdę proste i masz więcej sposobów do wyboru.

Fabryka Form

Stwórzmy klasę, która potrafi stworzyć formularz. Taka klasa nazywana jest fabryką. W miejscu, w którym chcemy użyć formularza (np. w prezenterze), żądamy fabryki jako zależności.

Część fabryki to kod, który przekazuje dane do dalszego przetwarzania, gdy formularz zostanie pomyślnie przesłany. Zazwyczaj do warstwy modelu. Sprawdza również, czy wszystko poszło dobrze i przekazuje wszelkie błędy z powrotem do formularza. Model w poniższym przykładzie jest reprezentowany przez klasę Facade:

use Nette\Application\UI\Form;

class EditFormFactory
{
	public function __construct(
		private 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('...');
		}
	}
}

Fabryka może być oczywiście parametryczna, tzn. może przyjmować parametry, które wpływają na tworzony przez nią formularz.

Teraz pokażemy, jak przekazać fabrykę do prezentera. Najpierw zapisujemy ją do pliku konfiguracyjnego:

services:
	- EditFormFactory

A następnie zażądać go w prezenterze. Kolejnym etapem przetwarzania przesłanego formularza jest przekierowanie na kolejną stronę:

class MyPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private EditFormFactory $formFactory,
	) {
	}

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

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

		return $form;
	}
}

Ponieważ przekierowanie jest obsługiwane przez handler prezentera, komponent może być używany w wielu miejscach i przekierowany do innego miejsca w każdym z nich.

Komponent z formą

Inną opcją jest stworzenie nowego komponentu, który zawiera formularz. Daje nam to możliwość np. renderowania formularza w określony sposób, ponieważ komponent zawiera szablon. Lub możemy użyć sygnałów do komunikacji AJAX i ładowania informacji do formularza, na przykład do podpowiedzi itp.

use Nette\Application\UI\Form;

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

	public function __construct(
		private 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);
	}
}

Stworzymy jeszcze fabrykę, która będzie produkowała ten komponent. Wystarczy napisać jego interfejs:

interface EditControlFactory
{
	function create(): EditControl;
}

I dodaj go do pliku konfiguracyjnego:

services:
	- EditControlFactory

A teraz możemy zażądać fabryki i użyć jej w prezenterze:

class MyPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private EditControlFactory $controlFactory,
	) {
	}

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

		$control->onSave[] = function (EditControl $control, $data) {
			$this->redirect('this');
			// lub przekierować na wynik edycji, np:
			// $this->redirect('detail', ['id' => $data->id]);
		};

		return $control;
	}
}