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 教程。