Formularz do tworzenia i edycji rekordu
Jak poprawnie zaimplementować w Nette dodawanie i edycję rekordu, wykorzystując ten sam formularz do obu operacji?
W wielu przypadkach formularze do dodawania i edycji rekordu są takie same, różnią się np. tylko etykietą na przycisku. Pokażemy przykłady prostych presenterów, gdzie formularz użyjemy najpierw do dodawania rekordu, potem do edycji, a na końcu połączymy oba rozwiązania.
Dodawanie rekordu
Przykład presentera służącego do dodawania rekordu. Samą pracę z bazą danych pozostawimy klasie Facade
,
której kod nie jest istotny dla 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;
// ... dodajemy pola formularza ...
$form->onSuccess[] = [$this, 'recordFormSucceeded'];
return $form;
}
public 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
Teraz pokażemy, 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 // weryfikacja istnienia rekordu
|| !$this->facade->isEditAllowed(/*...*/) // kontrola uprawnień
) {
$this->error(); // błąd 404
}
$this->record = $record;
}
protected function createComponentRecordForm(): Form
{
// sprawdzamy, czy akcja to 'edit'
if ($this->getAction() !== 'edit') {
$this->error();
}
$form = new Form;
// ... dodajemy pola formularza ...
$form->setDefaults($this->record); // ustawienie wartości domyślnych
$form->onSuccess[] = [$this, 'recordFormSucceeded'];
return $form;
}
public 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 actionEdit()
, która uruchamia się na samym początku cyklu życia presentera, weryfikujemy
istnienie rekordu i uprawnienia użytkownika do jego edycji.
Rekord zapisujemy do właściwości $record
, aby mieć go dostępnego w metodzie
createComponentRecordForm()
w celu ustawienia wartości domyślnych, oraz w recordFormSucceeded()
ze
względu na ID. Alternatywnym rozwiązaniem byłoby ustawienie wartości domyślnych bezpośrednio w actionEdit()
i pobranie wartości ID, która jest częścią URL, za pomocą getParameter('id')
:
public function actionEdit(int $id): void
{
$record = $this->facade->get($id);
if (
// weryfikacja istnienia i kontrola uprawnień
) {
$this->error();
}
// ustawienie wartości domyślnych 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żniejszą lekcją całego kodu, musimy podczas tworzenia formularza upewnić się,
że akcja to rzeczywiście edit
. W przeciwnym razie weryfikacja w metodzie actionEdit()
w ogóle by nie
została przeprowadzona!
Ten sam formularz do dodawania i edycji
A teraz połączymy oba presentery w jeden. Albo moglibyśmy w metodzie createComponentRecordForm()
rozróżnić,
o którą akcję chodzi i na tej podstawie skonfigurować formularz, albo możemy to zostawić bezpośrednio metodom 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 // weryfikacja istnienia rekordu
|| !$this->facade->isEditAllowed(/*...*/) // kontrola uprawnień
) {
$this->error(); // błąd 404
}
$form = $this->getComponent('recordForm');
$form->setDefaults($record); // ustawienie wartości domyślnych
$form->onSuccess[] = [$this, 'editingFormSucceeded'];
}
protected function createComponentRecordForm(): Form
{
// sprawdzamy, czy akcja to 'add' lub 'edit'
if (!in_array($this->getAction(), ['add', 'edit'])) {
$this->error();
}
$form = new Form;
// ... dodajemy pola formularza ...
return $form;
}
public function addingFormSucceeded(Form $form, array $data): void
{
$this->facade->add($data); // dodanie rekordu do bazy danych
$this->flashMessage('Pomyślnie dodano');
$this->redirect('...');
}
public 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('...');
}
}