pbStudio: Чистый контроллер или FetchIt — два способа обработки форм

В предыдущей статьи мы создали модальное окно с формой.
В этом уроке я покажу, как можно обработать эту форму двумя способами.
- pbStudio: Создаём сайт с PageBlocks – настройка и главная страница
- pbStudio: Меню и страница «О нас»
- pbStudio: Чистый контроллер или FetchIt — два способа обработки форм
- pbStudio: Портфолио, Услуги и Контакты
- pbStudio: Подключаем мультиязычность
1. Обработка формы через чистый контроллер
В форме мы указали action="/form/submit", значит, нужно создать соответствующий маршрут для обработки:
1. Создаём новый маршрут в core/App/routes/web.php:
use PageBlocks\App\Http\Controllers\FormController;
Route::post('/form/submit', [FormController::class, 'handle']);
2. Создаём контроллер FormController:
core/App/Http/Controllers/FormController.php:
<?php
namespace PageBlocks\App\Http\Controllers;
use Boshnik\PageBlocks\Facades\Request;
use Boshnik\PageBlocks\Support\Mail;
class FormController extends Controller
{
public function handle(Request $request)
{
// Валидация формы
$validated = $request->validate([
'honeypot' => 'empty', // антиспам: поле должно быть пустым
'formname' => 'required|string',
'name' => 'required|string|max:255',
'email' => 'required|email',
'file' => 'required|file|mimes:pdf,doc,docx,jpg|min:100|max:2048',
'message' => 'nullable|string', // необязательное текстовое поле
]);
// Сохраняем файл в папку assets/images/files
$validated['file'] = $request->file('file')->store('assets/images/files');
// Добавим IP
$validated['ip'] = $request->ip();
// Сохраняем форму в таблицу значений
$form = $this->modx->newObject(\pbTableValue::class, [
'model_type' => 'pbMenu', // Тип модели
// 'model_type' => 'MODX\Revolution\modDocument', Если таблица на странице ресурса
// 'model_type' => 'MODX\Revolution\modUser', Если таблица на странице пользователя
'field_name' => 'forms', // Имя поля таблицы
'createdon' => time(),
'values' => json_encode($validated), // Сериализованные данные формы
]);
if (!$form->save()) {
$this->modx->log(modX::LOG_LEVEL_ERROR, 'Не удалось сохранить форму: ' . print_r($validated, true));
}
// Отправка письма менеджеру
Mail::to('pageblocks@boshnik.com')
->subject('Отправка резюме')
->view('chunks/email/manager', $validated)
->attach($validated['file'])
->send();
// Отправка письма пользователю
Mail::send($validated['email'], 'Ваше резюме принято', 'Мы скоро с вами свяжемся!');
// или через хелпер:
// mail($validated['email'], 'Ваше резюме принято', 'Мы скоро с вами свяжемся!');
// Если запрос был через AJAX — возвращаем JSON
if ($request->expectsJson()) {
return response()->json([
'success' => true,
'message' => 'Форма успешно отправлена',
// 'redirect' => '/contacts', Если нужен редирект
]);
}
// В противном случае — редирект
return response()->redirect('/contacts');
}
}
3. Создаём таблицу для хранения данных (core/App/Models/MainMenu.php):
Menu::make('Forms')
->title('Формы')
->description('Сохраненные формы')
->fields([
// Создаем таблицу с именем forms
Field::make('Forms')
->type('table')
->fields([
Field::make('formname')
->type('hidden'),
Field::make('createdon')
->label('Date')
->type('date')
->width(50)
->storage('timestamp')
->dateFormat('Y-m-d, H:i')
->readOnly(),
Field::make('IP')
->width(50)
->readOnly(),
Field::make('Name'),
Field::make('Email'),
Field::make('Message')
->type('textarea')
->height(50),
Field::make('File')->type('file'),
])
// Показываем колонки
->columns([
Column::make('Name'),
Column::make('Email'),
Column::make('Createdon')
->render('date')
->format('Y-m-d, H:i'),
Column::make('Editedon')
->render('date')
->format('Y-m-d, H:i'),
Column::make('IP')
])
// Добавляем фильтры
->filters([
Filter::make('formName')
->label('Form name')
->type('select')
->options([
'Job' => 'Job',
]),
Filter::make('createdon')
->label('Date'),
]),
])
4. Шаблон письма для менеджера (core/App/elements/chunks/email/manager.tpl):
<ul>
<li><b>Name</b>: {$name}</li>
<li><b>Email</b>: {$email}</li>
{if $message}
<li><b>Message</b>: {$message}</li>
{/if}
</ul>
5. Скрипты для обработки формы и уведомлений (в core/App/elements/chunks/head.tpl):
{*Form*}
<script src="/assets/components/pageblocks/js/web/pageblocks.js" defer></script>
{*SweetAlert2*}
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11" defer></script>
Для формы добавляем атрибут data-pbform.
6. Подключаем SweetAlert2 (core/App/elements/templates/base.tpl)
<script>
document.addEventListener('DOMContentLoaded', () => {
PageBlocks.Message = {
success(message) {
Swal.fire({
position: 'top-end',
title: message,
showConfirmButton: false,
timer: 1500
});
},
error(message) {
Swal.fire({
position: 'top-end',
title: message,
showConfirmButton: false,
timer: 1500
});
}
};
// Close modal
document.addEventListener('pageblocks:success', (e) => {
const modal = e.detail.form.closest('.modal');
if (modal) {
bootstrap.Modal.getInstance(modal)?.hide();
}
});
});
</script>
CSRF-токен
Так как любые POST-запросы проходят проверку CSRF, мы в нашу форму добавили скрытое поле:
<input type="hidden" name="_token" value="{csrf_token}">
Таблица в админке:

2. Обработка формы через FetchIt
1. Устанавливаем компоненты FormIt и FetchIt.
2. Подключаем в head.tpl необходимые скрипты:
{*FetchIt*}
<script src="/assets/components/fetchit/js/fetchit.min.js"></script>
<script async src="/assets/components/fetchit/lib/notyf.min.js"></script>
{*Notyf*}
<link rel="preload" href="/assets/components/fetchit/lib/notyf.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/assets/components/fetchit/lib/notyf.min.css"></noscript>
3. Роут для FetchIt
Так как у нас включён полный контроль над маршрутами, запрос, который делает FetchIt к /assets/components/fetchit/action.php, будет блокироваться.
Поэтому создаём соответствующий маршрут в файле core/App/routes/web.php:
use PageBlocks\App\Http\Controllers\FetchItController;
Route::post('/assets/components/fetchit/action.php', [FetchItController::class, 'action']);
4. Создаем контроллер FetchItController
<?php
namespace PageBlocks\App\Http\Controllers;
class FetchItController extends Controller
{
public function action()
{
$FetchIt = $this->modx->services->get('FetchIt');
if (!isset($_POST)) {
$site_start = $this->modx->makeUrl($this->modx->getOption('site_start'), '', '', 'full');
redirect($site_start);
} elseif (empty($_SERVER['HTTP_X_FETCHIT_ACTION'])) {
return $FetchIt->error('fetchit_err_action_ns');
} else {
return $FetchIt->process($_SERVER['HTTP_X_FETCHIT_ACTION'], array_merge($_FILES, $_REQUEST));
}
}
}
5. Обновляем чанк modal.tpl:
<div class="modal fade" id="jobModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5">Присоединяйся к команде PageBlocks</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
{'!FetchIt' | snippet : [
'snippet' => 'FormIt',
'form' => 'jobForm',
'hooks' => 'FormItSaveForm,email',
'emailTpl' => 'jobEmail',
'emailSubject' => 'Тема письма',
'emailTo' => 'Superboshnik@gmail.com',
'formName' => 'Job',
'formFields' => 'name,email,message',
'fieldNames' => 'name==Name,email==Email,message==Message',
'validate' => 'name:required,email:required,file:required',
'validationErrorMessage' => 'В форме содержатся ошибки!',
'successMessage' => 'Сообщение успешно отправлено',
]}
</div>
</div>
</div>
Саму форму помещаем в чанк jobForm, а шаблон письма в jobEmail (аналогичный нашему файловому manager.tpl)Заключение:
Теперь у вас есть 2 способа обработки форм:
- Собственный контроллер — максимум гибкости, отправка писем, валидация, сохранение файлов и данных в таблицу PageBlocks.
- FetchIt + FormIt — быстро и просто, если нужно использовать уже знакомые компоненты.
Поблагодарить автора
Отправить деньги
Комментарии: 1
Жаль, что редко стал заходить на форум и не успел поставить лайк и поблагодарить. Отличные уроки!
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.