Obrazec za ustvarjanje in urejanje zapisa

Kako pravilno izvajati dodajanje in urejanje zapisa v Nette, pri čemer se za oboje uporablja isti obrazec?

V številnih primerih sta obrazca za dodajanje in urejanje zapisa enaka, razlikujeta se le po oznaki na gumbu. Prikazali bomo primere preprostih predstavnikov, kjer najprej uporabimo obrazec za dodajanje zapisa, nato za njegovo urejanje in na koncu obe rešitvi združimo.

Dodajanje zapisa

Primer predstavnika, ki se uporablja za dodajanje zapisa. Dejansko delo s podatkovno bazo bomo prepustili razredu Facade, katerega koda za primer ni pomembna.

use Nette\Application\UI\Form;

class RecordPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Facade $facade,
	) {
	}

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

		// ... dodajanje polj obrazca ...

		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // dodajanje zapisa v zbirko podatkov
		$this->flashMessage('Successfully added');
		$this->redirect('...');
	}

	public function renderAdd(): void
	{
		// ...
	}
}

Urejanje zapisa

Zdaj si oglejmo, kako je videti predstavitev, ki se uporablja za urejanje zapisov:

use Nette\Application\UI\Form;

class RecordPresenter extends Nette\Application\UI\Presenter
{
	private $record;

	public function __construct(
		private Facade $facade,
	) {
	}

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			!$record // preverjanje obstoja zapisa.
			|| !$this->facade->isEditAllowed(/*...*/) // preverjanje dovoljenj.
		) {
			$this->error(); // Napaka 404
		}

		$this->record = $record;
	}

	protected function createComponentRecordForm(): Form
	{
		// preverite, ali je dejanje "uredi".
		if ($this->getAction() !== 'edit') {
			$this->error();
		}

		$form = new Form;

		// ... dodajte polja obrazca ...

		$form->setDefaults($this->record); // nastavite privzete vrednosti
		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->update($this->record->id, $data); // posodobi zapis
		$this->flashMessage('Successfully updated');
		$this->redirect('...');
	}
}

V metodi action, ki se sproži na začetku življenjskega cikla predstavnika, preverimo obstoj zapisa in uporabnikovo dovoljenje za njegovo urejanje.

Zapis shranimo v lastnost $record, tako da je na voljo v metodi createComponentRecordForm() za nastavitev privzetih vrednosti in recordFormSucceeded() za ID. Alternativna rešitev bi bila, da bi privzete vrednosti nastavili neposredno v metodi actionEdit(), vrednost ID, ki je del naslova URL, pa bi pridobili z uporabo metode getParameter('id'):

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			// preverjanje obstoja in preverjanje dovoljenj
		) {
			$this->error();
		}

		// nastaviti privzete vrednosti obrazca.
		$this->getComponent('recordForm')
			->setDefaults($record);
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$id = (int) $this->getParameter('id');
		$this->facade->update($id, $data);
		// ...
	}
}

Vendar, in to bi morala biti najpomembnejša ugotovitev iz celotne kode, moramo pri ustvarjanju obrazca zagotoviti, da je dejanje res edit. Kajti v nasprotnem primeru se preverjanje v metodi actionEdit() sploh ne bi zgodilo!

Isti obrazec za dodajanje in urejanje

Zdaj bomo oba predstavnika združili v enega. Lahko razlikujemo, katero dejanje je vključeno v metodo createComponentRecordForm(), in ustrezno konfiguriramo obrazec ali pa to prepustimo neposredno metodam dejanj in se znebimo pogoja:

class RecordPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Facade $facade,
	) {
	}

	public function actionAdd(): void
	{
		$form = $this->getComponent('recordForm');
		$form->onSuccess[] = [$this, 'addingFormSucceeded'];
	}

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			!$record // preverjanje obstoja zapisa.
			|| !$this->facade->isEditAllowed(/*...*/) // preverjanje dovoljenj.
		) {
			$this->error(); // Napaka 404
		}

		$form = $this->getComponent('recordForm');
		$form->setDefaults($record); // nastavite privzete nastavitve
		$form->onSuccess[] = [$this, 'editingFormSucceeded'];
	}

	protected function createComponentRecordForm(): Form
	{
		// preveri, ali je dejanje "dodaj" ali "uredi".
		if (!in_array($this->getAction(), ['add', 'edit'])) {
			$this->error();
		}

		$form = new Form;

		// ... dodajte polja obrazca ...

		return $form;
	}

	public function addingFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // dodajanje zapisa v zbirko podatkov
		$this->flashMessage('Successfully added');
		$this->redirect('...');
	}

	public function editingFormSucceeded(Form $form, array $data): void
	{
		$id = (int) $this->getParameter('id');
		$this->facade->update($id, $data); // posodobitev zapisa
		$this->flashMessage('Successfully updated');
		$this->redirect('...');
	}
}