Форма для створення та редагування запису
Як правильно реалізувати в Nette додавання та редагування запису, використовуючи для обох операцій одну й ту саму форму?
У багатьох випадках форми для додавання та редагування запису однакові, відрізняючись, наприклад, лише написом на кнопці. Ми покажемо приклади простих презентерів, де форму спочатку використаємо для додавання запису, потім для редагування, і нарешті об'єднаємо обидва рішення.
Додавання запису
Приклад презентера, що служить для додавання запису. Саму роботу з
базою даних залишимо класу Facade
, код якого не є суттєвим для
прикладу.
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;
// ... додамо поля форми ...
$form->onSuccess[] = [$this, 'recordFormSucceeded'];
return $form;
}
public function recordFormSucceeded(Form $form, array $data): void
{
$this->facade->add($data); // додавання запису до бази даних
$this->flashMessage('Successfully added');
$this->redirect('...');
}
public function renderAdd(): void
{
// ...
}
}
Редагування запису
Тепер покажемо, як виглядав би презентер, що служить для редагування запису:
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 // перевірка існування запису
|| !$this->facade->isEditAllowed(/*...*/) // перевірка прав доступу
) {
$this->error(); // помилка 404
}
$this->record = $record;
}
protected function createComponentRecordForm(): Form
{
// перевіримо, що дія є 'edit'
if ($this->getAction() !== 'edit') {
$this->error();
}
$form = new Form;
// ... додамо поля форми ...
$form->setDefaults($this->record); // встановлення значень за замовчуванням
$form->onSuccess[] = [$this, 'recordFormSucceeded'];
return $form;
}
public function recordFormSucceeded(Form $form, array $data): void
{
$this->facade->update($this->record->id, $data); // оновлення запису
$this->flashMessage('Successfully updated');
$this->redirect('...');
}
}
У методі action, який запускається на самому початку життєвого циклу презентера, ми перевіряємо існування запису та права користувача на його редагування.
Запис ми зберігаємо у властивості $record
, щоб мати до нього
доступ у методі createComponentRecordForm()
для встановлення значень за
замовчуванням, та в recordFormSucceeded()
для отримання ID. Альтернативним
рішенням було б встановити значення за замовчуванням безпосередньо в
actionEdit()
та отримати значення ID, яке є частиною URL, за допомогою
getParameter('id')
:
public function actionEdit(int $id): void
{
$record = $this->facade->get($id);
if (
// перевірка існування та прав доступу
) {
$this->error();
}
// встановлення значень за замовчуванням форми
$this->getComponent('recordForm')
->setDefaults($record);
}
public function recordFormSucceeded(Form $form, array $data): void
{
$id = (int) $this->getParameter('id');
$this->facade->update($id, $data);
// ...
}
}
Однак, і це має бути найважливішим висновком усього коду, ми
повинні при створенні форми переконатися, що дія дійсно є edit
. Бо
інакше перевірка в методі actionEdit()
взагалі не відбудеться!
Однакова форма для додавання та редагування
А тепер об'єднаємо обидва презентери в один. Ми могли б у методі
createComponentRecordForm()
розрізняти, про яку дію йдеться, і відповідно
конфігурувати форму, або ж можемо залишити це безпосередньо для
action-методів і позбутися умови:
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 // перевірка існування запису
|| !$this->facade->isEditAllowed(/*...*/) // перевірка прав доступу
) {
$this->error(); // помилка 404
}
$form = $this->getComponent('recordForm');
$form->setDefaults($record); // встановлення значень за замовчуванням
$form->onSuccess[] = [$this, 'editingFormSucceeded'];
}
protected function createComponentRecordForm(): Form
{
// перевіримо, що дія є 'add' або 'edit'
if (!in_array($this->getAction(), ['add', 'edit'])) {
$this->error();
}
$form = new Form;
// ... додамо поля форми ...
return $form;
}
public function addingFormSucceeded(Form $form, array $data): void
{
$this->facade->add($data); // додавання запису до бази даних
$this->flashMessage('Successfully added');
$this->redirect('...');
}
public function editingFormSucceeded(Form $form, array $data): void
{
$id = (int) $this->getParameter('id');
$this->facade->update($id, $data); // оновлення запису
$this->flashMessage('Successfully updated');
$this->redirect('...');
}
}