Nette Documentation Preview

syntax
Form per creare e modificare un record
**************************************

.[perex]
Come realizzare correttamente in Nette l'aggiunta e la modifica di un record usando per entrambe lo stesso form?

In molti casi i form per aggiungere e per modificare un record sono identici e si distinguono al massimo per l'etichetta del pulsante. Mostreremo esempi di presenter semplici in cui useremo il form prima per aggiungere un record, poi per modificarlo e infine uniremo le due soluzioni.


Aggiungere un record
--------------------

Esempio di presenter per aggiungere un record. Lasceremo il lavoro vero e proprio sul database alla classe `Facade`, il cui codice non è essenziale per questo esempio.


```php
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;

		// ... aggiunta dei campi del form ...

		$form->onSuccess[] = $this->recordFormSucceeded(...);
		return $form;
	}

	private function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // aggiunta del record al database
		$this->flashMessage('Aggiunto con successo');
		$this->redirect('...');
	}

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


Modificare un record
--------------------

Vediamo ora come apparirebbe un presenter per modificare un record:


```php
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 // verifica dell'esistenza del record
			|| !$this->facade->isEditAllowed(/*...*/) // controllo dei permessi
		) {
			$this->error(); // errore 404
		}

		$this->record = $record;
	}

	protected function createComponentRecordForm(): Form
	{
		// verifica che l'azione sia 'edit'
		if ($this->getAction() !== 'edit') {
			$this->error();
		}

		$form = new Form;

		// ... aggiunta dei campi del form ...

		$form->setDefaults($this->record); // impostazione dei valori predefiniti
		$form->onSuccess[] = $this->recordFormSucceeded(...);
		return $form;
	}

	private function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->update($this->record->id, $data); // aggiornamento del record
		$this->flashMessage('Aggiornato con successo');
		$this->redirect('...');
	}
}
```

Nel metodo *action*, che viene invocato all'inizio del [ciclo di vita del presenter |application:presenters#Ciclo di vita del presenter], verifichiamo l'esistenza del record e il permesso dell'utente di modificarlo.

Salviamo il record nella proprietà `$record`, così è disponibile nel metodo `createComponentRecordForm()` per impostare i valori predefiniti e in `recordFormSucceeded()` per accedere all'ID. Una soluzione alternativa è impostare i valori predefiniti direttamente in `actionEdit()` e ottenere il valore dell'ID (parte dell'URL) con `getParameter('id')`:


```php
	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			// verifica dell'esistenza e controllo dei permessi
		) {
			$this->error();
		}

		// impostazione dei valori predefiniti del form
		$this->getComponent('recordForm')
			->setDefaults($record);
	}

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

Però, e questa dovrebbe essere **la cosa più importante di tutto il codice**, quando creiamo il form dobbiamo assicurarci che l'azione sia davvero `edit`. Altrimenti la verifica nel metodo `actionEdit()` non avverrebbe affatto!


Lo stesso form per aggiungere e modificare
------------------------------------------

Uniamo ora i due presenter in uno solo. Potremmo distinguere l'azione nel metodo `createComponentRecordForm()` e configurare il form di conseguenza, oppure possiamo delegarlo direttamente ai metodi action ed eliminare il controllo condizionale:


```php
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 // verifica dell'esistenza del record
			|| !$this->facade->isEditAllowed(/*...*/) // controllo dei permessi
		) {
			$this->error(); // errore 404
		}

		$form = $this->getComponent('recordForm');
		$form->setDefaults($record); // impostazione dei valori predefiniti
		$form->onSuccess[] = $this->editingFormSucceeded(...);
	}

	protected function createComponentRecordForm(): Form
	{
		// verifica che l'azione sia 'add' oppure 'edit'
		if (!in_array($this->getAction(), ['add', 'edit'])) {
			$this->error();
		}

		$form = new Form;

		// ... aggiunta dei campi del form ...

		return $form;
	}

	private function addingFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // aggiunta del record al database
		$this->flashMessage('Aggiunto con successo');
		$this->redirect('...');
	}

	private function editingFormSucceeded(Form $form, array $data): void
	{
		$id = (int) $this->getParameter('id');
		$this->facade->update($id, $data); // aggiornamento del record
		$this->flashMessage('Aggiornato con successo');
		$this->redirect('...');
	}
}
```

{{priority: -1}}

Form per creare e modificare un record

Come realizzare correttamente in Nette l'aggiunta e la modifica di un record usando per entrambe lo stesso form?

In molti casi i form per aggiungere e per modificare un record sono identici e si distinguono al massimo per l'etichetta del pulsante. Mostreremo esempi di presenter semplici in cui useremo il form prima per aggiungere un record, poi per modificarlo e infine uniremo le due soluzioni.

Aggiungere un record

Esempio di presenter per aggiungere un record. Lasceremo il lavoro vero e proprio sul database alla classe Facade, il cui codice non è essenziale per questo esempio.

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;

		// ... aggiunta dei campi del form ...

		$form->onSuccess[] = $this->recordFormSucceeded(...);
		return $form;
	}

	private function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // aggiunta del record al database
		$this->flashMessage('Aggiunto con successo');
		$this->redirect('...');
	}

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

Modificare un record

Vediamo ora come apparirebbe un presenter per modificare un record:

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 // verifica dell'esistenza del record
			|| !$this->facade->isEditAllowed(/*...*/) // controllo dei permessi
		) {
			$this->error(); // errore 404
		}

		$this->record = $record;
	}

	protected function createComponentRecordForm(): Form
	{
		// verifica che l'azione sia 'edit'
		if ($this->getAction() !== 'edit') {
			$this->error();
		}

		$form = new Form;

		// ... aggiunta dei campi del form ...

		$form->setDefaults($this->record); // impostazione dei valori predefiniti
		$form->onSuccess[] = $this->recordFormSucceeded(...);
		return $form;
	}

	private function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->update($this->record->id, $data); // aggiornamento del record
		$this->flashMessage('Aggiornato con successo');
		$this->redirect('...');
	}
}

Nel metodo action, che viene invocato all'inizio del ciclo di vita del presenter, verifichiamo l'esistenza del record e il permesso dell'utente di modificarlo.

Salviamo il record nella proprietà $record, così è disponibile nel metodo createComponentRecordForm() per impostare i valori predefiniti e in recordFormSucceeded() per accedere all'ID. Una soluzione alternativa è impostare i valori predefiniti direttamente in actionEdit() e ottenere il valore dell'ID (parte dell'URL) con getParameter('id'):

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			// verifica dell'esistenza e controllo dei permessi
		) {
			$this->error();
		}

		// impostazione dei valori predefiniti del form
		$this->getComponent('recordForm')
			->setDefaults($record);
	}

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

Però, e questa dovrebbe essere la cosa più importante di tutto il codice, quando creiamo il form dobbiamo assicurarci che l'azione sia davvero edit. Altrimenti la verifica nel metodo actionEdit() non avverrebbe affatto!

Lo stesso form per aggiungere e modificare

Uniamo ora i due presenter in uno solo. Potremmo distinguere l'azione nel metodo createComponentRecordForm() e configurare il form di conseguenza, oppure possiamo delegarlo direttamente ai metodi action ed eliminare il controllo condizionale:

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 // verifica dell'esistenza del record
			|| !$this->facade->isEditAllowed(/*...*/) // controllo dei permessi
		) {
			$this->error(); // errore 404
		}

		$form = $this->getComponent('recordForm');
		$form->setDefaults($record); // impostazione dei valori predefiniti
		$form->onSuccess[] = $this->editingFormSucceeded(...);
	}

	protected function createComponentRecordForm(): Form
	{
		// verifica che l'azione sia 'add' oppure 'edit'
		if (!in_array($this->getAction(), ['add', 'edit'])) {
			$this->error();
		}

		$form = new Form;

		// ... aggiunta dei campi del form ...

		return $form;
	}

	private function addingFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // aggiunta del record al database
		$this->flashMessage('Aggiunto con successo');
		$this->redirect('...');
	}

	private function editingFormSucceeded(Form $form, array $data): void
	{
		$id = (int) $this->getParameter('id');
		$this->facade->update($id, $data); // aggiornamento del record
		$this->flashMessage('Aggiornato con successo');
		$this->redirect('...');
	}
}