Formularz do tworzenia i edycji rekordu
Jak w Nette poprawnie zrealizować dodawanie i edycję rekordu, używając do obu tego samego formularza?
W wielu przypadkach formularze do dodawania i edycji rekordu są takie same, różnią się może tylko etykietą przycisku. Pokażemy przykłady prostych presenterów, w których użyjemy formularza najpierw do dodania rekordu, potem do jego edycji, a na koniec połączymy oba rozwiązania.
Dodawanie rekordu
Przykład presentera służącego do dodania rekordu. Samą pracę z bazą danych zostawimy klasie Facade,
której kod nie jest dla tego przykładu istotny.
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;
// ... dodajemy pola formularza ...
$form->onSuccess[] = $this->recordFormSucceeded(...);
return $form;
}
private function recordFormSucceeded(Form $form, array $data): void
{
$this->facade->add($data); // dodanie rekordu do bazy danych
$this->flashMessage('Pomyślnie dodano');
$this->redirect('...');
}
public function renderAdd(): void
{
// ...
}
}
Edycja rekordu
Zobaczmy teraz, jak wyglądałby presenter służący do edycji rekordu:
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 // weryfikujemy istnienie rekordu
|| !$this->facade->isEditAllowed(/*...*/) // sprawdzamy uprawnienia
) {
$this->error(); // błąd 404
}
$this->record = $record;
}
protected function createComponentRecordForm(): Form
{
// weryfikujemy, że akcja to 'edit'
if ($this->getAction() !== 'edit') {
$this->error();
}
$form = new Form;
// ... dodajemy pola formularza ...
$form->setDefaults($this->record); // ustawiamy wartości domyślne
$form->onSuccess[] = $this->recordFormSucceeded(...);
return $form;
}
private function recordFormSucceeded(Form $form, array $data): void
{
$this->facade->update($this->record->id, $data); // aktualizacja rekordu
$this->flashMessage('Pomyślnie zaktualizowano');
$this->redirect('...');
}
}
W metodzie action, wywoływanej na początku cyklu życia presentera, weryfikujemy istnienie rekordu i uprawnienia użytkownika do jego edycji.
Rekord zapisujemy do właściwości $record, dzięki czemu jest dostępny w metodzie
createComponentRecordForm() do ustawienia wartości domyślnych oraz w recordFormSucceeded() do odczytu
ID. Alternatywnym rozwiązaniem jest ustawienie wartości domyślnych bezpośrednio w actionEdit() i odczytanie
wartości ID (będącej częścią URL) za pomocą getParameter('id'):
public function actionEdit(int $id): void
{
$record = $this->facade->get($id);
if (
// weryfikujemy istnienie i sprawdzamy uprawnienia
) {
$this->error();
}
// ustawiamy wartości domyślne formularza
$this->getComponent('recordForm')
->setDefaults($record);
}
public function recordFormSucceeded(Form $form, array $data): void
{
$id = (int) $this->getParameter('id');
$this->facade->update($id, $data);
// ...
}
Ale, i to powinno być najważniejszym wnioskiem z całego kodu, przy tworzeniu formularza musimy upewnić się, że
akcja rzeczywiście jest edit. W przeciwnym razie weryfikacja w metodzie actionEdit() w ogóle by się
nie odbyła!
Ten sam formularz do dodawania i edycji
Połączmy teraz oba presentery w jeden. Moglibyśmy albo rozróżniać akcję w metodzie
createComponentRecordForm() i odpowiednio konfigurować formularz, albo możemy to zostawić bezpośrednio metodom
action 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 // weryfikujemy istnienie rekordu
|| !$this->facade->isEditAllowed(/*...*/) // sprawdzamy uprawnienia
) {
$this->error(); // błąd 404
}
$form = $this->getComponent('recordForm');
$form->setDefaults($record); // ustawiamy wartości domyślne
$form->onSuccess[] = $this->editingFormSucceeded(...);
}
protected function createComponentRecordForm(): Form
{
// weryfikujemy, że akcja to 'add' albo 'edit'
if (!in_array($this->getAction(), ['add', 'edit'])) {
$this->error();
}
$form = new Form;
// ... dodajemy pola formularza ...
return $form;
}
private function addingFormSucceeded(Form $form, array $data): void
{
$this->facade->add($data); // dodanie rekordu do bazy danych
$this->flashMessage('Pomyślnie dodano');
$this->redirect('...');
}
private function editingFormSucceeded(Form $form, array $data): void
{
$id = (int) $this->getParameter('id');
$this->facade->update($id, $data); // aktualizacja rekordu
$this->flashMessage('Pomyślnie zaktualizowano');
$this->redirect('...');
}
}