Nette Documentation Preview

syntax
Modulo per la creazione e la modifica di un record
**************************************************

.[perex]
Come implementare correttamente l'aggiunta e la modifica di un record in Nette, utilizzando lo stesso modulo per entrambi?

In molti casi, i moduli per l'aggiunta e la modifica di un record sono identici e si differenziano solo per l'etichetta del pulsante. Mostreremo esempi di semplici presentazioni in cui utilizziamo il modulo prima per aggiungere un record, poi per modificarlo e infine combiniamo le due soluzioni.


Aggiunta di un record .[#toc-adding-a-record]
---------------------------------------------

Un esempio di presentatore usato per aggiungere un record. Lasceremo il lavoro effettivo sul database alla classe `Facade`, il cui codice non è rilevante per l'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;

		// ... aggiungere campi al modulo ...

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

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

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


Modifica di un record .[#toc-editing-a-record]
----------------------------------------------

Vediamo ora come appare un presentatore utilizzato 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 // verificare l'esistenza del record
			|| !$this->facade->isEditAllowed(/*...*/) // verificare le autorizzazioni
		) {
			$this->error(); // errore 404
		}

		$this->record = $record;
	}

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

		$form = new Form;

		// ... aggiungere campi del modulo ...

		$form->setDefaults($this->record); // impostare i valori predefiniti
		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->update($this->record->id, $data); // aggiorna il record
		$this->flashMessage('Aggiornamento riuscito');
		$this->redirect('...');
	}
}
```

Nel metodo *action*, che viene invocato proprio all'inizio del [ciclo di vita |application:presenters#Life Cycle of Presenter] del [presentatore |application:presenters#Life Cycle of Presenter], si verifica l'esistenza del record e l'autorizzazione dell'utente a modificarlo.

Memorizziamo il record nella proprietà `$record`, in modo che sia disponibile nel metodo `createComponentRecordForm()` per l'impostazione dei valori predefiniti e `recordFormSucceeded()` per l'ID. Una soluzione alternativa sarebbe quella di impostare i valori predefiniti direttamente in `actionEdit()` e il valore dell'ID, che fa parte dell'URL, viene recuperato con `getParameter('id')`:


```php
	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			// verificare l'esistenza e controllare i permessi
		) {
			$this->error();
		}

		// impostare i valori predefiniti dei moduli
		$this->getComponent('recordForm')
			->setDefaults($record);
	}

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

Tuttavia, e questo dovrebbe essere **il risultato più importante di tutto il codice**, dobbiamo assicurarci che l'azione sia effettivamente `edit` quando creiamo il form. Perché altrimenti la validazione nel metodo `actionEdit()` non avverrebbe affatto!


Stesso modulo per aggiungere e modificare .[#toc-same-form-for-adding-and-editing]
----------------------------------------------------------------------------------

Ora combineremo entrambi i presentatori in uno solo. Si può distinguere quale azione è coinvolta nel metodo `createComponentRecordForm()` e configurare il modulo di conseguenza, oppure si può lasciare la scelta direttamente ai metodi di azione e sbarazzarsi della condizione:


```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 // verificare l'esistenza del record
			|| !$this->facade->isEditAllowed(/*...*/) // verificare le autorizzazioni
		) {
			$this->error(); // errore 404
		}

		$form = $this->getComponent('recordForm');
		$form->setDefaults($record); // impostare le impostazioni predefinite
		$form->onSuccess[] = [$this, 'editingFormSucceeded'];
	}

	protected function createComponentRecordForm(): Form
	{
		// verificare che l'azione sia "aggiungere" o "modificare".
		if (!in_array($this->getAction(), ['add', 'edit'])) {
			$this->error();
		}

		$form = new Form;

		// ... aggiungere i campi del modulo ...

		return $form;
	}

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

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

{{priority: -1}}
{{sitename: Migliori pratiche}}

Modulo per la creazione e la modifica di un record

Come implementare correttamente l'aggiunta e la modifica di un record in Nette, utilizzando lo stesso modulo per entrambi?

In molti casi, i moduli per l'aggiunta e la modifica di un record sono identici e si differenziano solo per l'etichetta del pulsante. Mostreremo esempi di semplici presentazioni in cui utilizziamo il modulo prima per aggiungere un record, poi per modificarlo e infine combiniamo le due soluzioni.

Aggiunta di un record

Un esempio di presentatore usato per aggiungere un record. Lasceremo il lavoro effettivo sul database alla classe Facade, il cui codice non è rilevante per l'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;

		// ... aggiungere campi al modulo ...

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

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

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

Modifica di un record

Vediamo ora come appare un presentatore utilizzato 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 // verificare l'esistenza del record
			|| !$this->facade->isEditAllowed(/*...*/) // verificare le autorizzazioni
		) {
			$this->error(); // errore 404
		}

		$this->record = $record;
	}

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

		$form = new Form;

		// ... aggiungere campi del modulo ...

		$form->setDefaults($this->record); // impostare i valori predefiniti
		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->update($this->record->id, $data); // aggiorna il record
		$this->flashMessage('Aggiornamento riuscito');
		$this->redirect('...');
	}
}

Nel metodo action, che viene invocato proprio all'inizio del ciclo di vita del presentatore, si verifica l'esistenza del record e l'autorizzazione dell'utente a modificarlo.

Memorizziamo il record nella proprietà $record, in modo che sia disponibile nel metodo createComponentRecordForm() per l'impostazione dei valori predefiniti e recordFormSucceeded() per l'ID. Una soluzione alternativa sarebbe quella di impostare i valori predefiniti direttamente in actionEdit() e il valore dell'ID, che fa parte dell'URL, viene recuperato con getParameter('id'):

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			// verificare l'esistenza e controllare i permessi
		) {
			$this->error();
		}

		// impostare i valori predefiniti dei moduli
		$this->getComponent('recordForm')
			->setDefaults($record);
	}

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

Tuttavia, e questo dovrebbe essere il risultato più importante di tutto il codice, dobbiamo assicurarci che l'azione sia effettivamente edit quando creiamo il form. Perché altrimenti la validazione nel metodo actionEdit() non avverrebbe affatto!

Stesso modulo per aggiungere e modificare

Ora combineremo entrambi i presentatori in uno solo. Si può distinguere quale azione è coinvolta nel metodo createComponentRecordForm() e configurare il modulo di conseguenza, oppure si può lasciare la scelta direttamente ai metodi di azione e sbarazzarsi della condizione:

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 // verificare l'esistenza del record
			|| !$this->facade->isEditAllowed(/*...*/) // verificare le autorizzazioni
		) {
			$this->error(); // errore 404
		}

		$form = $this->getComponent('recordForm');
		$form->setDefaults($record); // impostare le impostazioni predefinite
		$form->onSuccess[] = [$this, 'editingFormSucceeded'];
	}

	protected function createComponentRecordForm(): Form
	{
		// verificare che l'azione sia "aggiungere" o "modificare".
		if (!in_array($this->getAction(), ['add', 'edit'])) {
			$this->error();
		}

		$form = new Form;

		// ... aggiungere i campi del modulo ...

		return $form;
	}

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

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