Formulaire pour la création et l'édition d'enregistrements
Comment implémenter correctement l'ajout et l'édition d'enregistrements dans Nette, en utilisant le même formulaire pour les deux ?
Dans de nombreux cas, les formulaires pour ajouter et éditer des enregistrements sont identiques, ne différant peut-être que par l'étiquette du bouton. Nous montrerons des exemples de presenters simples où nous utiliserons d'abord le formulaire pour ajouter un enregistrement, puis pour l'éditer, et enfin combinerons les deux solutions.
Ajout d'un enregistrement
Exemple de presenter servant à ajouter un enregistrement. Nous laisserons le travail réel avec la base de données à la
classe Facade
, dont le code n'est pas essentiel pour la démonstration.
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 les champs du formulaire ...
$form->onSuccess[] = [$this, 'recordFormSucceeded'];
return $form;
}
public function recordFormSucceeded(Form $form, array $data): void
{
$this->facade->add($data); // ajout de l'enregistrement à la base de données
$this->flashMessage('Ajouté avec succès');
$this->redirect('...');
}
public function renderAdd(): void
{
// ...
}
}
Édition d'un enregistrement
Maintenant, montrons à quoi ressemblerait un presenter servant à éditer 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érification de l'existence de l'enregistrement
|| !$this->facade->isEditAllowed(/*...*/) // contrôle des permissions
) {
$this->error(); // erreur 404
}
$this->record = $record;
}
protected function createComponentRecordForm(): Form
{
// vérifions que l'action est 'edit'
if ($this->getAction() !== 'edit') {
$this->error();
}
$form = new Form;
// ... ajouter les champs du formulaire ...
$form->setDefaults($this->record); // définition des 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); // mise à jour de l'enregistrement
$this->flashMessage('Mis à jour avec succès');
$this->redirect('...');
}
}
Dans la méthode action, qui s'exécute au tout début du cycle de vie du presenter, nous vérifions l'existence de l'enregistrement et les permissions de l'utilisateur pour l'éditer.
Nous sauvegardons l'enregistrement dans la propriété $record
pour l'avoir disponible dans la méthode
createComponentRecordForm()
pour définir les valeurs par défaut, et dans recordFormSucceeded()
pour
l'ID. Une solution alternative serait de définir les valeurs par défaut directement dans actionEdit()
et d'obtenir
la valeur de l'ID, qui fait partie de l'URL, en utilisant getParameter('id')
:
public function actionEdit(int $id): void
{
$record = $this->facade->get($id);
if (
// vérification de l'existence et contrôle des permissions
) {
$this->error();
}
// définition des valeurs par défaut du formulaire
$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 la leçon la plus importante de tout le code, nous devons nous assurer lors de la
création du formulaire que l'action est bien edit
. Sinon, la vérification dans la méthode
actionEdit()
n'aurait pas lieu du tout !
Même formulaire pour l'ajout et l'édition
Et maintenant, combinons les deux presenters en un seul. Soit nous pourrions distinguer dans la méthode
createComponentRecordForm()
de quelle action il s'agit et configurer le formulaire en conséquence, soit nous pouvons
laisser cela 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érification de l'existence de l'enregistrement
|| !$this->facade->isEditAllowed(/*...*/) // contrôle des permissions
) {
$this->error(); // erreur 404
}
$form = $this->getComponent('recordForm');
$form->setDefaults($record); // définition des valeurs par défaut
$form->onSuccess[] = [$this, 'editingFormSucceeded'];
}
protected function createComponentRecordForm(): Form
{
// vérifions que l'action est 'add' ou 'edit'
if (!in_array($this->getAction(), ['add', 'edit'])) {
$this->error();
}
$form = new Form;
// ... ajouter les champs du formulaire ...
return $form;
}
public function addingFormSucceeded(Form $form, array $data): void
{
$this->facade->add($data); // ajout de l'enregistrement à la base de données
$this->flashMessage('Ajouté avec succès');
$this->redirect('...');
}
public function editingFormSucceeded(Form $form, array $data): void
{
$id = (int) $this->getParameter('id');
$this->facade->update($id, $data); // mise à jour de l'enregistrement
$this->flashMessage('Mis à jour avec succès');
$this->redirect('...');
}
}