Формы и проверка данных
Форме нужны два маршрута: GET показывает пустую форму, POST принимает отправленные данные. Данным от посетителя нельзя доверять, поэтому перед сохранением их проверяют. В Slim нет встроенной проверки — её пишут сами или подключают библиотеку.
Форма
{# templates/posts/new.twig #}
{% extends 'layout.twig' %}
{% block content %}
<h1>Новая статья</h1>
<form method="post" action="{{ url_for('posts.store') }}">
<input type="text" name="title" placeholder="Заголовок">
<textarea name="body" placeholder="Текст"></textarea>
<button type="submit">Сохранить</button>
</form>
{% endblock %}Действие, которое её показывает:
// src/Action/NewPostAction.php
final class NewPostAction
{
public function __construct(private Twig $twig) {}
public function __invoke(Request $request, Response $response): Response
{
return $this->twig->render($response, 'posts/new.twig');
}
}Строки namespace и use здесь и дальше опущены — они такие же, как в прежних действиях.
Маршруты
// config/routes.php
$app->get('/posts/new', NewPostAction::class)->setName('posts.new');
$app->post('/posts', CreatePostAction::class)->setName('posts.store');По адресу /posts/new откроется форма. Кнопка пока ведёт к ошибке: второго действия ещё нет.
Проверка
Правила проверки выносят в отдельный класс в папке src/Validation/. Он получает данные и возвращает массив ошибок — пустой, если всё в порядке:
// src/Validation/PostValidator.php
namespace App\Validation;
final class PostValidator
{
public function validate(array $data): array
{
$errors = [];
if (mb_strlen(trim($data['title'] ?? '')) < 5) {
$errors['title'] = 'Заголовок должен быть не короче 5 символов';
}
if (trim($data['body'] ?? '') === '') {
$errors['body'] = 'Введите текст статьи';
}
return $errors;
}
}Приём данных
// src/Action/CreatePostAction.php
final class CreatePostAction
{
public function __construct(
private PostRepository $posts,
private PostValidator $validator,
private Twig $twig,
) {}
public function __invoke(Request $request, Response $response): Response
{
$data = (array) $request->getParsedBody();
$errors = $this->validator->validate($data);
if ($errors !== []) {
return $this->twig->render($response->withStatus(422), 'posts/new.twig', [
'errors' => $errors,
'old' => $data,
]);
}
$this->posts->create(trim($data['title']), trim($data['body']));
$url = RouteContext::fromRequest($request)->getRouteParser()->urlFor('posts.index');
return $response->withHeader('Location', $url)->withStatus(302);
}
}Если ошибки есть, форма выводится заново с кодом 422 — «данные не прошли проверку». В шаблон передаются ошибки и введённые значения. Если ошибок нет, статья сохраняется, и браузер перенаправляется на список.
Вывод ошибок
Дополните шаблон: подставьте прежние значения и сообщения об ошибках.
{# templates/posts/new.twig #}
<input type="text" name="title" value="{{ old.title }}" placeholder="Заголовок">
{% if errors.title %}
<p class="error">{{ errors.title }}</p>
{% endif %}
<textarea name="body" placeholder="Текст">{{ old.body }}</textarea>
{% if errors.body %}
<p class="error">{{ errors.body }}</p>
{% endif %}Отправьте пустую форму — под полями появятся сообщения. При первом открытии переменных errors и old нет; Twig считает их пустыми и ничего не выводит.
Библиотеки проверки
Когда правил становится много, вместо своего класса подключают библиотеку, например respect/validation или symfony/validator. Место в проекте то же: класс в src/Validation/, который вызывается из действия.
Частая ошибка
Если после успешного сохранения не перенаправить браузер, а сразу вывести страницу, обновление этой страницы отправит форму повторно — в базе появится вторая такая же статья. После удачного POST всегда возвращайте перенаправление.