ZetCode

Symfony 验证

最后修改于 2020 年 7 月 5 日

Symfony 验证教程展示了如何在 Symfony 应用程序中验证数据。在本教程中,我们使用手动验证。我们不使用 doctrine 注解。

Symfony

Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。Symfony 于 2005 年作为自由软件发布。Symfony 的原始作者是 Fabien Potencier。Symfony 的灵感来自 Ruby on Rails、Djanog 和 Spring Framework。

Symfony 验证

必须验证来自用户的输入。Symfony 提供了一个 Validator 组件来执行验证任务。该组件基于 Java 的 Bean Validation 规范。

Validator 旨在针对约束验证对象。约束是断言一个条件为真的断言。Symfony 包含许多内置约束,包括 NotBlankEmailLengthIsbn。也可以创建自定义约束。

Symfony 验证示例

在示例中,我们有一个包含两个字段的简单表单:nameemail。提交表单后,我们使用 Symfony 的 Validator 手动验证字段。在示例中,我们使用了 LengthNotBlankEmail 约束。

安装软件包

$ composer create-project symfony/skeleton myval
$ cd myval

我们创建一个新的 Symfony 项目并进入项目目录。

$ composer req maker server --dev

我们安装 symfony/maker-bundlesymfony/web-server-bundle。这些对于开发模式很有用。请注意,我们使用的是软件包的别名。

$ composer req twig annotations 

我们安装 twig-bundle 和注解。注解位于 sensio/framework-extra-bundle

$ composer req validator

symfony/validator 包包含 Symfony 验证工具。

$ composer req security-csrf
$ composer req monolog
$ composer req property-access

symfony/security-csrf 包对于防止跨站请求伪造是必需的,symfony/monolog-bundle 用于日志记录,symfony/property-access 用于操作 PHP 属性。

构建 Symfony 应用程序

$ php bin/console make:controller HomeController

我们创建一个 HomeController

src/Controller/HomeController.php
<?php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

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 FormController

我们创建一个 FormController 来响应表单提交。

src/Controller/FormController.php
<?php

namespace App\Controller;

use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class FormController extends AbstractController
{
    /**
     * @Route("/sendForm", name="form")
     */
    public function index(Request $request, ValidatorInterface $validator,
        LoggerInterface $logger): Response {

        $token = $request->request->get("token");

        if (!$this->isCsrfTokenValid('myform', $token)) {

            $logger->info("CSRF failure");

            return new Response("Operation not allowed", Response::HTTP_OK,
                ['content-type' => 'text/plain']);
        }

        $name = $request->request->get("name");
        $email = $request->request->get("email");

        $input = ['name' => $name, 'email' => $email];

        $constraints = new Assert\Collection([
            'name' => [new Assert\Length(['min' => 2]), new Assert\NotBlank],
            'email' => [new Assert\Email(), new Assert\notBlank],
        ]);

        $violations = $validator->validate($input, $constraints);

        if (count($violations) > 0) {

            $accessor = PropertyAccess::createPropertyAccessor();

            $errorMessages = [];

            foreach ($violations as $violation) {

                $accessor->setValue($errorMessages,
                    $violation->getPropertyPath(),
                    $violation->getMessage());
            }

            return $this->render('form/violations.html.twig',
                ['errorMessages' => $errorMessages]);
        } else {
            return new Response("Validation passed", Response::HTTP_OK,
                ['content-type' => 'text/plain']);
        }
    }
}

FormController 中,我们检查 CSRF 令牌,验证表单输入值,并将响应发送回客户端。

注意: 出于简单性考虑,我们将验证代码放在了控制器中。在生产应用程序中,最好将此类代码放在单独的服务类中。

public function index(Request $request, ValidatorInterface $validator, 
        LoggerInterface $logger)
{

我们注入请求对象、验证器对象和日志记录器对象。

$token = $request->request->get("token");

if (!$this->isCsrfTokenValid('myform', $token)) {
    
    $logger->info("CSRF failure");

    return new Response("Operation not allowed",  Response::HTTP_OK, 
        ['content-type' => 'text/plain']);
}

我们检索令牌并使用 isCsrfTokenValid() 方法进行验证。

$name = $request->request->get("name");
$email = $request->request->get("email");

$input = ['name' => $name, 'email' => $email];

我们获取请求输入参数并将其放入一个数组中。

$constraints = new Assert\Collection([
    'name' => [new Assert\Length(['min' => 2]), new Assert\NotBlank],
    'email' => [new Assert\Email(), new Assert\notBlank]
]);

我们创建一个约束集合。我们可以为单个值分配多个约束。

$violations = $validator->validate($input, $constraints);

我们使用 validate() 方法根据约束验证输入数据。该方法返回可能的违规。Symfony 验证器返回一个 ConstraintViolationList。我们使用 Symfony accessor 来处理列表。

if (count($violations) > 0) {

我们检查是否有一些违规。

$accessor = PropertyAccess::createPropertyAccessor();

$errorMessages = [];

foreach ($violations as $violation) {

    $accessor->setValue($errorMessages,
        $violation->getPropertyPath(),
        $violation->getMessage());
}

Symfony PropertyAccess 用于在将违反消息发送到模板之前进行处理。ConstraintViolationList 被转换为 PHP 数组,其中键是表单字段,值是错误消息。该数组稍后在 Twig 中使用 for 指令进行处理。

return $this->render('form/violations.html.twig',
    ['errorMessages' => $errorMessages]);

我们将错误消息渲染在单独的页面上。这通常使用 flash 消息完成。请参阅 Symfony 保留表单值教程 了解如何使用 flashes 实现。

} else {
    return new Response("Validation passed", Response::HTTP_OK,
        ['content-type' => 'text/plain']);
}

如果一切正常,我们发送一条纯消息验证通过

templates/home/index.html.twig
{% extends 'base.html.twig' %}

{% block title %}Home page{% endblock %}

{% block body %}

    <form action="sendForm" method="post">

        <input type="hidden" name="token" value="{{ csrf_token('myform') }}" />
        <div>
            <label>Name:</label>
            <input type="text" name="name">
        </div>

        <div>
            <label>Email</label>
            <input type="email" name="email">
        </div>

        <button type="submit">Send</button>

    </form>

{% endblock %}

表单包含两个字段:姓名和电子邮件。

<input type="hidden" name="token" value="{{ csrf_token('myform') }}" />

它还包含一个隐藏字段以防止跨站请求伪造。

templates/form/violations.html.twig
{% extends 'base.html.twig' %}

{% block title %}Violations{% endblock %}

{% block body %}
<h2>Validation failed</h2>

<ul>
{% for field, errorMessage in errorMessages %}
    <li>{{ field }}: {{ errorMessage }}</li>
{% endfor %}
</ul>
{% endblock %}

在 violations 视图中,我们遍历违规并列出它们。

{% for field, errorMessage in errorMessages %}
    <li>{{ field }}: {{ errorMessage }}</li>
{% endfor %}

使用 for 指令,我们遍历错误消息并在存在时显示它们。

templates/base.html.twig
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Welcome!{% endblock %}</title>
        {% block stylesheets %}{% endblock %}
    </head>
    <body>
        {% block body %}{% endblock %}
        {% block javascripts %}{% endblock %}
    </body>
</html>

这是基本的 Twig 模板。

$ php bin/console server:run

我们启动开发服务器。然后定位到 localhost:8000/home URL 来获取表单。

在本教程中,我们在 Symfony 应用程序中验证了一个简单的表单。我们使用了手动验证。

查看所有 Symfony 教程