Форма для создания и редактирования записи
Как правильно реализовать добавление и редактирование записи в 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;
}
private 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;
}
private function recordFormSucceeded(Form $form, array $data): void
{
$this->facade->update($this->record->id, $data); // обновляем запись
$this->flashMessage('Successfully updated');
$this->redirect('...');
}
}
В методе action, вызываемом в начале жизненного цикла презентера, мы проверяем существование записи и право пользователя её редактировать.
Мы сохраняем запись в свойстве $record, благодаря чему она
доступна в методе createComponentRecordForm() для задания значений по
умолчанию и в recordFormSucceeded() для доступа к идентификатору.
Альтернативное решение – задать значения по умолчанию прямо в
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() и настраивать форму
соответственно, либо переложить это на сами методы действий и
избавиться от условной проверки:
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;
}
private function addingFormSucceeded(Form $form, array $data): void
{
$this->facade->add($data); // добавляем запись в базу данных
$this->flashMessage('Successfully added');
$this->redirect('...');
}
private function editingFormSucceeded(Form $form, array $data): void
{
$id = (int) $this->getParameter('id');
$this->facade->update($id, $data); // обновляем запись
$this->flashMessage('Successfully updated');
$this->redirect('...');
}
}