设置器中的 Symfony 验证器服务和 TypeErrors

Posted

技术标签:

【中文标题】设置器中的 Symfony 验证器服务和 TypeErrors【英文标题】:Symfony validator service and TypeErrors in setters 【发布时间】:2021-06-14 06:52:32 【问题描述】:

我目前对 Symfony 验证器服务的工作方式感到困惑。按照我目前的理解,如果在设置值时发生 TypeError,它甚至可能在验证实体之前完全失败并且不会报告错误。

Symfony 文档在这样的实体中使用约束:

namespace App\Entity;

// ...
use Symfony\Component\Validator\Constraints as Assert;

class Author

    /**
     * @Assert\NotBlank
     */
    private $name;

然后在控制器中像这样使用:

public function author(ValidatorInterface $validator)

    $author = new Author();
    
    // ... do something to the $author object

    $author->setBirthDate('this will fail and not report'); // I added this line in myself, see rest of question.
  

    $errors = $validator->validate($author);

    if (count($errors) > 0) 
        /*
         * Uses a __toString method on the $errors variable which is a
         * ConstraintViolationList object. This gives us a nice string
         * for debugging.
         */
        $errorsString = (string) $errors;

        return new Response($errorsString);
    

    return new Response('The author is valid! Yes!');

但是,当错误类型的参数被传递给实体变量的设置器时,这不会很好地捕获将引发的异常。例如,实体可以有一个字段“birthDate”,它是一个 DateTime,并且有一个 setter setBirthDate(DateTime $foo)。在能够调用 validate() 函数之前构建对象时,可能会传递一个不正确类型的参数 - 例如,用户提交了一个字符串或根本没有提交 - 这显然会引发异常。

假设验证器服务应该这样使用,我的问题如下:如何干净地处理可能在设置器中引发 TypeErrors 的数据?

我是否没有在我的设置器中进行任何类型提示(接受所有内容),然后稍后使用验证器验证它是否为 DateTime?设置实体时是否使用 try/catch 块?在调用 setter 之前,我是否要手动检查用户输入的类型?如果最后两个中的任何一个,我将如何干净地向用户报告错误?即便如此,我认为在你也在验证器服务中进行手动验证时感觉不对。

我了解 Forms,并且我认为在使用它们时这不是问题,但我仍然发现验证器无论哪种方式都令人困惑。

【问题讨论】:

【参考方案1】:

如果您指定设置器将接受 DateTime 对象和 DateTime 对象,那么您错过了代码应该如何工作的要点,只有 php 本身会确保接受的参数是 DateTime 对象的实例。解决这个问题的一种方法是创建一个 DTO(数据传输对象),例如。 CreateAuthorRequest 并创建一个 symfony 表单类型,您可以在其中接受字符串/日期时间(最好是字符串,它有一个正则表达式,确保字符串是有效的日期时间字符串)。然后在您调用 $form->isValid() 之后,您可以从 DTO 读取数据并确保它是有效的。如果您仍然难以正确实现这一点,请在答案下方评论,我将提供代码示例 此外,我不建议在您的实体中放置那么多注释,它会很快变得混乱,更喜欢 XML 配置而不是注释。

【讨论】:

"如果您指定 setter 将接受 DateTime 对象和 DateTime 对象,则只有 PHP 本身会确保接受的参数是 DateTime 对象的实例。"我知道这一点,我不想做这样的事情。 DateTime 对象的验证将使用 Symfony 约束 (symfony.com/doc/current/reference/constraints/DateTime.html) 完成。但是,问题是在检查约束之前调用了类型提示的 setter。我会尝试使用表单,但它仍然让我想知道为什么要使用验证器服务和实体约束。 问题是首先设置然后验证值,当我第一次尝试类型化属性和类型化设置器时,我遇到了类似的困难。最好的解决方法是没有类型提示的 DTO,尽管这种糟糕的最佳选择可能是验证字符串是否是有效的日期时间格式,然后将其映射到 DateTime 对象并在实际实体中设置 我觉得你会在没有 DTO 的情况下在两个位置进行验证。不仅是 DateTimes,而且还有需要整数的 setter 等等。基本上每个领域。您还将失去方便的表单/验证器服务错误报告,并且必须单独处理这些,不是吗?虽然表单也接受数组而不是实体,但也许您可以跳过 DTO 并直接输入请求正文?它实际上不是一样的吗(在一个小应用程序中)?感谢您指出了这一点。虽然我仍然不明白验证器服务背后的想法,但它看起来并不实用。

以上是关于设置器中的 Symfony 验证器服务和 TypeErrors的主要内容,如果未能解决你的问题,请参考以下文章

在 Symfony 中的第一个错误上停止对象属性验证有多漂亮?

禁用 Symfony 2 类型中选择字段的后端验证

使用Symfony框架在生产服务器上登录失败(由于...无法处理身份验证请求)

解析器中的 AppSync GraphQL 变异服务器逻辑

如何使用 LoginFormAuthenticator 和 Login Link Authenticator 在 Symfony 5.2 中为特定路径设置默认身份验证器

Symfony 2中的kernel.request事件中的身份验证令牌始终为空?