{% raw %}

# Symfony 验证教程

> 原文： [http://zetcode.com/symfony/validation/](http://zetcode.com/symfony/validation/)

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

## Symfony

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

## Symfony 验证

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

验证器旨在针对约束验证对象。 约束是对条件为真的断言。 Symfony 具有许多内置约束，包括`NotBlank`，`Email`，`Length`和`Isbn`。 也可以创建自定义约束。

## Symfony 验证示例

在示例中，我们有一个简单的表单，其中包含两个字段：`name`和`email`。 提交表单后，我们使用 Symfony 的`Validator`手动验证字段。 在示例中，我们使用`Length`，`NotBlank`和`Email`约束。

### 安装组件

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

```

我们创建一个新的 Symfony 项目，然后转到项目目录。

```php
$ composer req maker server --dev

```

我们安装`symfony/maker-bundle`和`symfony/web-server-bundle`。 这些对于开发模式很有用。 请注意，我们正在使用包的别名。

```php
$ composer req twig annotations 

```

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

```php
$ composer req validator

```

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

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

```

跨站点请求伪造需要`symfony/security-csrf`包，`symfony/monolog-bundle`用于记录日志，`symfony/property-access`用于操纵 PHP 属性。

### 构建 Symfony 应用

```php
$ php bin/console make:controller HomeController

```

我们创建一个`HomeController`。

`src/Controller/HomeController.php`

```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
$ php bin/console make:controller FormController

```

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

`src/Controller/FormController.php`

```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 令牌，验证表单输入值，并将响应发送回客户端。

> **注意**：为简单起见，我们将验证代码放入控制器中。 在生产应用中，最好将此类代码放在单独的服务类中。

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

```

我们注入了请求对象，验证器对象和记录器对象。

```php
$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()`方法对其进行验证。

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

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

```

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

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

```

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

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

```

我们使用`validate()`方法对照约束条件验证输入数据。 该方法返回可能的违规。 Symfony 验证器返回`ConstraintViolationList`。 我们使用 Symfony 访问器来处理列表。

```php
if (count($violations) > 0) {

```

我们检查是否有违规行为。

```php
$accessor = PropertyAccess::createPropertyAccessor();

$errorMessages = [];

foreach ($violations as $violation) {

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

```

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

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

```

我们在单独的页面中呈现错误消息。 这通常是使用 Flash 消息完成的。 看看 [Symfony 保持表单值教程](/symfony/keepformvalues/)如何使用 Flash。

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

```

如果一切正常，我们将发送一条简单消息验证已通过。

`templates/home/index.html.twig`

```php
{% 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 %}

```

该表格包含两个字段：姓名和电子邮件。

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

```

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

`templates/form/violations.html.twig`

```php
{% 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 %}

```

在违规视图中，我们浏览违规并列出它们。

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

```

使用`for`指令，我们遍历错误消息并显示错误消息。

`templates/base.html.twig`

```php
<!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
$ php bin/console server:run

```

我们启动开发服务器。 然后找到`localhost:8000/home`网址以获取表格。

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

您可能也对以下相关教程感兴趣： [Symfony 简介](/symfony/intro/)， [Symfony DBAL 教程](/symfony/dbal/)， [Symfony 表单教程](/symfony/form/)， [PHP 教程](/lang/php/)， 或列出[所有 Symfony 教程](/all/#symfony)。

{% endraw %}