Formulaire de création et de modification d'un enregistrement

Comment mettre en œuvre correctement l'ajout et la modification d'un enregistrement dans Nette, en utilisant le même formulaire pour les deux ?

Dans de nombreux cas, les formulaires d'ajout et de modification d'un enregistrement sont identiques et ne diffèrent que par le libellé du bouton. Nous allons montrer des exemples de présentateurs simples où nous utilisons le formulaire d'abord pour ajouter un enregistrement, puis pour le modifier, et enfin combiner les deux solutions.

Ajout d'un enregistrement

Un exemple de présentateur utilisé pour ajouter un enregistrement. Nous laisserons le travail réel sur la base de données à la classe Facade, dont le code n'est pas pertinent pour cet exemple.

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;

		// ... ajouter des champs de formulaire ...

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

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // ajouter un enregistrement à la base de données
		$this->flashMessage('Successfully added');
		$this->redirect('...');
	}

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

Modification d'un enregistrement

Voyons maintenant à quoi ressemble un présentateur utilisé pour modifier un enregistrement :

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 // vérifier l'existence de l'enregistrement
			|| !$this->facade->isEditAllowed(/*...*/) // vérifier les permissions
		) {
			$this->error(); // erreur 404
		}

		$this->record = $record;
	}

	protected function createComponentRecordForm(): Form
	{
		// vérifiez que l'action est "modifier".
		if ($this->getAction() !== 'edit') {
			$this->error();
		}

		$form = new Form;

		// ... ajouter des champs de formulaire ...

		$form->setDefaults($this->record); // définir les valeurs par défaut
		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

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

Dans la méthode action, qui est invoquée dès le début du cycle de vie du présentateur, nous vérifions l'existence de l'enregistrement et l'autorisation de l'utilisateur à le modifier.

Nous stockons l'enregistrement dans la propriété $record afin qu'il soit disponible dans la méthode createComponentRecordForm() pour définir les valeurs par défaut, et recordFormSucceeded() pour l'ID. Une autre solution consisterait à définir les valeurs par défaut directement dans actionEdit() et la valeur de l'ID, qui fait partie de l'URL, est récupérée à l'aide de getParameter('id'):

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			// vérifier l'existence et contrôler les permissions
		) {
			$this->error();
		}

		// définir les valeurs par défaut des formulaires
		$this->getComponent('recordForm')
			->setDefaults($record);
	}

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

Cependant, et cela devrait être le plus important à retenir de tout ce code, nous devons nous assurer que l'action est bien edit lorsque nous créons le formulaire. Car sinon, la validation dans la méthode actionEdit() n'aurait pas lieu du tout !

Le même formulaire pour l'ajout et la modification

Et maintenant, nous allons combiner les deux présentateurs en un seul. Nous pouvons soit distinguer quelle action est impliquée dans la méthode createComponentRecordForm() et configurer le formulaire en conséquence, soit nous en remettre directement aux méthodes d'action et nous débarrasser de la condition :

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 // vérifier l'existence de l'enregistrement
			|| !$this->facade->isEditAllowed(/*...*/) // vérifier les permissions
		) {
			$this->error(); // erreur 404
		}

		$form = $this->getComponent('recordForm');
		$form->setDefaults($record); // définir les valeurs par défaut
		$form->onSuccess[] = [$this, 'editingFormSucceeded'];
	}

	protected function createComponentRecordForm(): Form
	{
		// vérifiez que l'action est "ajouter" ou "modifier".
		if (!in_array($this->getAction(), ['add', 'edit'])) {
			$this->error();
		}

		$form = new Form;

		// ... ajouter des champs de formulaire ...

		return $form;
	}

	public function addingFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // ajouter un enregistrement à la base de données
		$this->flashMessage('Successfully added');
		$this->redirect('...');
	}

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