Symfony 保留表单值
最后修改时间:2025年3月3日
Symfony 保留表单值教程演示了如何在表单提交失败后,在表单提交后保留表单值。在本教程中,我们进行传统的表单提交;我们不使用表单构建器。
Symfony
Symfony 是一套可重用的 PHP 组件,也是一个用于 Web 项目的 PHP 框架。Symfony 于 2005 年发布为自由软件。Symfony 的最初作者是 Fabien Potencier。Symfony 深受 Spring Framework 的启发。
保留表单值
当用户提交表单时,应用程序会对其进行验证。当验证失败时,应用程序会将用户重定向回表单,并显示验证错误。保留用户已输入的表单值是一个好习惯。
Symfony 保留表单值示例
在示例中,我们有一个带有两个字段的简单表单:name 和 email。提交表单后,我们会检查 CSRF 保护并使用 Symfony 的 Validator 验证输入值。我们将输入的值存储在会话中,以便在提交失败时将其检索回来。
设置应用程序
我们首先使用 composer 设置应用程序。
$ composer create-project symfony/skeleton formkeepvals "^7.2" $ cd formkeepvals
我们创建一个新的 Symfony 7.2 skeleton 项目,然后进入新创建的项目目录。
$ composer require symfony/twig-bundle symfony/validator symfony/annotations
我们安装三个基本的 Symfony 包:twig-bundle、validator 和 annotations。
$ composer require symfony/security-csrf symfony/monolog-bundle
security-csrf 包是防止跨站请求伪造所必需的,而 monolog-bundle 用于日志记录。
$ composer require symfony/property-access
我们安装 PropertyAccess 组件,它用于方便地读取和写入对象和数组的属性/键。
$ composer require symfony/maker-bundle symfony/web-server-bundle --dev
我们为 Symfony 7.2 安装 maker bundle 和开发服务器。
$ php bin/console make:controller HomeController
我们创建一个 HomeController。该控制器将表单发送到客户端。
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class HomeController extends AbstractController
{
#[Route('/home', name: 'home')]
public function index(): Response
{
return $this->render('home/index.html.twig');
}
}
这是一个简单的控制器,它将包含 Web 表单的视图发送给用户。
$ php bin/console make:controller MessageController
我们创建一个 MessageController 来响应表单提交。
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Service\ValidationService;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class MessageController extends AbstractController
{
#[Route('/message', name: 'message', methods: ['POST'])]
public function index(Request $request, ValidationService $validator): Response
{
$token = $request->request->get('token', '');
if (!$validator->validateToken($token)) {
return new Response(
'Operation not allowed',
Response::HTTP_BAD_REQUEST,
['content-type' => 'text/plain']
);
}
$name = $request->request->get('name', '');
$email = $request->request->get('email', '');
$input = ['name' => $name, 'email' => $email];
$errorMessages = $validator->validateInput($input);
if (count($errorMessages) > 0) {
$session = $request->getSession();
$session->set('name', $name);
$session->set('email', $email);
foreach ($errorMessages as $key => $val) {
$this->addFlash($key, $val);
}
return $this->redirectToRoute('home');
}
return new Response(
'User saved',
Response::HTTP_OK,
['content-type' => 'text/plain']
);
}
}
在 MessageController 中,我们检查 CSRF 令牌,验证表单输入值,并将响应发送回客户端。
<?php
declare(strict_types=1);
namespace App\Service;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
class ValidationService
{
public function __construct(
private readonly CsrfTokenManagerInterface $tokenManager,
private readonly ValidatorInterface $validator,
private readonly PropertyAccessorInterface $accessor,
private readonly LoggerInterface $logger
) {
}
public function validateToken(string $token): bool
{
$csrfToken = new CsrfToken('myform', $token);
$isValid = $this->tokenManager->isTokenValid($csrfToken);
if (!$isValid) {
$this->logger->error('CSRF failure');
}
return $isValid;
}
/** @return array<string, string> */
public function validateInput(array $input): array
{
$constraints = new Assert\Collection([
'name' => [
new Assert\Length(['min' => 2]),
new Assert\NotBlank(),
],
'email' => [
new Assert\Email(),
new Assert\NotBlank(),
],
]);
$violations = $this->validator->validate($input, $constraints);
if (count($violations) > 0) {
$this->logger->info('Validation failed');
$messages = [];
foreach ($violations as $violation) {
$this->accessor->setValue(
$messages,
$violation->getPropertyPath(),
$violation->getMessage()
);
}
return $messages;
}
return [];
}
}
ValidationService 检查 CSRF 令牌并验证输入。
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block stylesheets %}
<style>
.topmargin {
margin-top: 10px;
}
</style>
{% endblock %}
{% block body %}
<section class="ui container topmargin">
<form class="ui form" action="{{ path('message') }}" method="post">
<input type="hidden" name="token" value="{{ csrf_token('myform') }}" />
{% for msg in app.flashes('name') %}
<div class="ui small red message">
{{ msg }}
</div>
{% endfor %}
<div class="field">
<label>Name:</label>
<input type="text" name="name" value="{{ app.session.get('name')|default('') }}">
</div>
{% for msg in app.flashes('email') %}
<div class="ui small red message">
{{ msg }}
</div>
{% endfor %}
<div class="field">
<label>Email</label>
<input type="text" name="email" value="{{ app.session.get('email')|default('') }}">
</div>
<button class="ui button" type="submit">Send</button>
</form>
</section>
{% endblock %}
主页有一个表单。该表单包含两个字段:name 和 email。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"
rel="stylesheet">
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.js"></script>
{% block javascripts %}{% endblock %}
</body>
</html>
这是基础的 Twig 模板。它包含了 Semantic UI CSS 框架。
在本教程中,我们验证了一个 Symfony 7.2 应用程序中的简单表单。
列出 所有 Symfony 教程。