Повторное использование форм в нескольких местах
Как повторно использовать одну и ту же форму в нескольких местах и не дублировать код? Это очень легко сделать в Nette, и у вас есть несколько способов на выбор.
Фабрика форм
Давайте создадим класс, который может создавать форму. Такой класс называется фабрикой. В том месте, где мы хотим использовать форму (например, в презентере), мы запрашиваем фабрику как зависимость.
Часть фабрики — это код, который передает данные для дальнейшей
обработки, когда форма успешно отправлена. Обычно на слой модели. Он
также проверяет, всё ли прошло успешно, и передает обратно любые ошибки
в форму. Модель в следующем примере представлена классом Facade
:
use Nette\Application\UI\Form;
class EditFormFactory
{
public function __construct(
private Facade $facade,
) {
}
public function create(/* parameters */): Form
{
$form = new Form;
// добавляем элементы к форме
$form->addSubmit('send', 'Submit');
$form->onSuccess[] = [$this, 'processForm'];
return $form;
}
public function processForm(Form $form, array $values): void
{
try {
// обработка формы
$this->facade->process($values);
} catch (AnyModelException $e) {
$form->addError('...');
}
}
}
Конечно, фабрика может быть параметрической, т. е. он может принимать параметры, которые будут влиять на внешний вид создаваемой формы.
Теперь мы продемонстрируем передачу фабрики презентеру. Сначала мы записываем её в конфигурационный файл:
services:
- EditFormFactory
А затем запрашиваем в презентере. Последний шаг обработки отправленной формы — перенаправление на следующую страницу:
class MyPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private EditFormFactory $formFactory,
) {
}
protected function createComponentEditForm(): Form
{
$form = $this->formFactory->create();
$form->onSuccess[] = function (Form $form) {
$this->redirect('this');
};
return $form;
}
}
Поскольку перенаправление обрабатывается обработчиком презентера, компонент можно использовать в нескольких местах и в каждом из них перенаправлять в другое место.
Компонент с формой
Другой способ — создать новый компонент, содержащий форму. Это дает нам возможность визуализировать форму определенным образом, если компонент включает в себя шаблон. Или мы можем использовать сигналы для связи AJAX и загрузки информации в форму, например, для автозаполнения и т. д.
use Nette\Application\UI\Form;
class EditControl extends Nette\Application\UI\Control
{
public $onSave;
public function __construct(
private Facade $facade,
) {
}
protected function createComponentForm(): Form
{
$form = new Form;
// добавляем элементы к форме
$form->addSubmit('send', 'Submit');
$form->onSuccess[] = [$this, 'processForm'];
return $form;
}
public function processForm(Form $form, array $values): void
{
try {
// обработка формы
$this->facade->process($values);
} catch (AnyModelException $e) {
$form->addError('...');
return;
}
// вызов события
$this->onSave($this, $values);
}
}
Далее мы создадим фабрику, которая будет производить этот компонент. Просто напишите его интерфейс:
interface EditControlFactory
{
function create(): EditControl;
}
И добавьте в файл конфигурации:
services:
- EditControlFactory
И теперь мы можем потребовать фабрику и использовать её в презентере:
class MyPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private EditControlFactory $controlFactory,
) {
}
protected function createComponentEditForm(): Form
{
$control = $this->controlFactory->create();
$control->onSave[] = function (EditControl $control, $data) {
$this->redirect('this');
// или перенаправить на результат редактирования, например:
// $this->redirect('detail', ['id' => $data->id]);
};
return $control;
}
}