Formularz tworzenia i edycji rekordu
Jak poprawnie zaimplementować dodawanie i edycję rekordu w Nette, używając tego samego formularza dla obu?
W wielu przypadkach formularze dodawania i edycji rekordu są takie same, różniąc się jedynie etykietą na przycisku. Pokażemy przykłady prostych prezenterów, w których wykorzystujemy formularz najpierw do dodania rekordu, potem do jego edycji, a na koniec łączymy te dwa rozwiązania.
Dodanie rekordu
Przykład prezentera używanego do dodania rekordu. Faktyczną pracę z bazą danych pozostawimy klasie Facade
,
której kod nie jest istotny dla tego przykładu.
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;
// ... dodać pola formularza ...
$form->onSuccess[] = [$this, 'recordFormSucceeded'];
return $form;
}
public function recordFormSucceeded(Form $form, array $data): void
{
$this->facade->add($data); // dodaj rekord do bazy danych
$this->flashMessage('Successfully added');
$this->redirect('...');
}
public function renderAdd(): void
{
// ...
}
}
Edycja zapisu
Teraz zobaczmy jak wyglądałby prezenter używany do edycji nagrania:
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 // sprawdzić istnienie rekordu
|| !$this->facade->isEditAllowed(/*...*/) // sprawdzić uprawnienia
) {
$this->error(); // błąd 404
}
$this->record = $record;
}
protected function createComponentRecordForm(): Form
{
// sprawdź, czy akcja to 'edit'
if ($this->getAction() !== 'edit') {
$this->error();
}
$form = new Form;
// dodaj pola formularza ...
$form->setDefaults($this->record); // ustaw wartości domyślne
$form->onSuccess[] = [$this, 'recordFormSucceeded'];
return $form;
}
public function recordFormSucceeded(Form $form, array $data): void
{
$this->facade->update($this->record->id, $data); // aktualizować rekord
$this->flashMessage('Pomyślnie zaktualizowano');
$this->redirect('...');
}
}
W metodzie action, która jest wywoływana zaraz na początku cyklu życia prezentera, sprawdzamy istnienie rekordu oraz uprawnienia użytkownika do jego edycji.
Rekord przechowujemy we właściwości $record
, tak aby był dostępny w metodzie
createComponentRecordForm()
do ustawiania wartości domyślnych, oraz recordFormSucceeded()
do
identyfikatora. Alternatywnym rozwiązaniem byłoby ustawienie wartości domyślnych bezpośrednio w actionEdit()
, a
wartość ID, która jest częścią adresu URL, jest pobierana za pomocą getParameter('id')
:
public function actionEdit(int $id): void
{
$record = $this->facade->get($id);
if (
// zweryfikować istnienie i sprawdzić uprawnienia
) {
$this->error();
}
// ustawić domyślne wartości formularza
$this->getComponent('recordForm')
->setDefaults($record);
}
public function recordFormSucceeded(Form $form, array $data): void
{
$id = (int) $this->getParameter('id');
$this->facade->update($id, $data);
// ...
}
}
Jednakże, i to powinno być najważniejsze spostrzeżenie z całego kodu, musimy upewnić się, kiedy tworzymy
formularz, że akcja jest faktycznie edit
. Ponieważ w przeciwnym razie walidacja w metodzie
actionEdit()
nie miałaby w ogóle miejsca!
Ten sam formularz do dodawania i edycji
A teraz połączymy obu prezenterów w jednego. Moglibyśmy albo rozróżnić, która akcja jest tą w metodzie
createComponentRecordForm()
i odpowiednio skonfigurować formularz, albo zostawić to bezpośrednio w metodach akcji
i pozbyć się warunku:
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 // sprawdzić istnienie rekordu
|| !$this->facade->isEditAllowed(/*...*/) // sprawdzić uprawnienia
) {
$this->error(); // błąd 404
}
$form = $this->getComponent('recordForm');
$form->setDefaults($record); // ustawić wartości domyślne
$form->onSuccess[] = [$this, 'editingFormSucceeded'];
}
protected function createComponentRecordForm(): Form
{
// sprawdź, czy akcja to "dodaj" lub "edytuj
if (!in_array($this->getAction(), ['add', 'edit'])) {
$this->error();
}
$form = new Form;
// dodaj pola formularza ...
return $form;
}
public function addingFormSucceeded(Form $form, array $data): void
{
$this->facade->add($data); // dodać rekord do bazy danych
$this->flashMessage('Successfully added');
$this->redirect('...');
}
public function editingFormSucceeded(Form $form, array $data): void
{
$id = (int) $this->getParameter('id');
$this->facade->update($id, $data); // aktualizować rekord
$this->flashMessage('Successfully updated');
$this->redirect('...');
}
}