Formular pentru crearea și modificarea unei înregistrări

Cum se implementează în mod corespunzător adăugarea și editarea unei înregistrări în Nette, utilizând același formular pentru ambele?

În multe cazuri, formularele de adăugare și de editare a unei înregistrări sunt identice, diferind doar prin eticheta de pe buton. Vom prezenta exemple de prezentări simple în care folosim formularul mai întâi pentru a adăuga o înregistrare, apoi pentru a o edita și, în final, vom combina cele două soluții.

Adăugarea unei înregistrări

Un exemplu de prezentator utilizat pentru a adăuga o înregistrare. Vom lăsa activitatea efectivă a bazei de date pe seama clasei Facade, al cărei cod nu este relevant pentru acest exemplu.

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;

		// ... adăugați câmpuri de formular ...

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

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // adăugarea unei înregistrări în baza de date
		$this->flashMessage('Successfully added');
		$this->redirect('...');
	}

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

Editarea unei înregistrări

Acum să vedem cum ar arăta un prezentator utilizat pentru a edita o înregistrare:

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 // să verifice existența înregistrării
			|| !$this->facade->isEditAllowed(/*...*/) // verificarea permisiunilor
		) {
			$this->error(); // Eroare 404
		}

		$this->record = $record;
	}

	protected function createComponentRecordForm(): Form
	{
		// verificați dacă acțiunea este "edit" (editare)
		if ($this->getAction() !== 'edit') {
			$this->error();
		}

		$form = new Form;

		// ... adăugați câmpuri de formular ...

		$form->setDefaults($this->record); // setați valorile implicite
		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

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

În metoda action, care este invocată chiar la începutul ciclului de viață al prezentatorului, se verifică existența înregistrării și permisiunea utilizatorului de a o edita.

Stocăm înregistrarea în proprietatea $record, astfel încât să fie disponibilă în metoda createComponentRecordForm() pentru setarea valorilor implicite și recordFormSucceeded() pentru ID. O soluție alternativă ar fi să setați valorile implicite direct în actionEdit(), iar valoarea ID, care face parte din URL, este recuperată cu ajutorul getParameter('id'):

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			// verificarea existenței și verificarea permisiunilor
		) {
			$this->error();
		}

		// stabilirea valorilor implicite ale formularelor
		$this->getComponent('recordForm')
			->setDefaults($record);
	}

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

Cu toate acestea, și aceasta ar trebui să fie cea mai importantă concluzie din tot acest cod, trebuie să ne asigurăm că acțiunea este într-adevăr edit atunci când creăm formularul. Pentru că, altfel, validarea din metoda actionEdit() nu ar avea loc deloc!

Același formular pentru adăugare și editare

Și acum vom combina ambii prezentatori într-unul singur. Fie putem distinge ce acțiune este implicată în metoda createComponentRecordForm() și configura formularul în consecință, fie putem lăsa direct pe seama metodelor de acțiune și să scăpăm de condiție:

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 // să verifice existența înregistrării
			|| !$this->facade->isEditAllowed(/*...*/) // verificarea permisiunilor
		) {
			$this->error(); // Eroare 404
		}

		$form = $this->getComponent('recordForm');
		$form->setDefaults($record); // setați valorile implicite
		$form->onSuccess[] = [$this, 'editingFormSucceeded'];
	}

	protected function createComponentRecordForm(): Form
	{
		// verifică dacă acțiunea este "add" sau "edit".
		if (!in_array($this->getAction(), ['add', 'edit'])) {
			$this->error();
		}

		$form = new Form;

		// ... adaugă câmpuri de formular ...

		return $form;
	}

	public function addingFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // adăugarea unei înregistrări în baza de date
		$this->flashMessage('Successfully added');
		$this->redirect('...');
	}

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