Форма для создания и редактирования записи
Как правильно реализовать добавление и редактирование записи в 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('Успешно добавлено');
$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('Успешно обновлено');
$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()
и
настроить форму соответствующим образом, либо мы можем оставить это
непосредственно 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('Успешно добавлено');
$this->redirect('...');
}
public function editingFormSucceeded(Form $form, array $data): void
{
$id = (int) $this->getParameter('id');
$this->facade->update($id, $data); // обновляем запись
$this->flashMessage('Успешно обновлено');
$this->redirect('...');
}
}