Symfony2 数据转换器、验证器和错误消息

问题描述 投票:0回答:1

我问了这个问题,发现我们无法获取DataTransformer抛出的错误消息(根据唯一回答的用户的说法,也许有可能,我不知道)。

无论如何,现在我知道了,我陷入了验证问题。假设我的模型是这样的:我有包含多个参与者(用户)的线程。

<?php

class Thread
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\ManyToMany(targetEntity="My\UserBundle\Entity\User")
     * @ORM\JoinTable(name="messaging_thread_user")
     */
    private $participants;

    // other fields, getters, setters, etc
}

对于线程创建,我希望用户在文本区域中指定参与者用户名,用“分隔” ”。 我希望如果指定的一个或多个用户名不存在,则会显示一条消息用户名不存在。 例如,“用户 titi、tata 和 toto 不存在”。

为此,我创建了一个 DataTransformer,它将文本区域中的原始文本转换为包含用户实例的 ArrayCollection。由于我无法获取此 DataTransformer 提供的错误消息(太可惜了!真的不可能吗?),所以我不会在 DataTransformer 中检查每个用户名是否存在,而是在 Validator 中检查。

这里是转换的DataTransformer - 将用户列表分离到 ArrayCollection 中(这样数据绑定就可以了):

<?php

public function reverseTransform($val)
{
    if (empty($val)) {
        return null;
    }

    $return = new ArrayCollection();

    // Extract usernames in an array from the raw text
    $val = str_replace("\r\n", "\n", trim($val));
    $usernames = explode("\n", $val);
    array_map('trim', $usernames);

    foreach ($usernames as $username) {
        $user = new User();
        $user->setUsername($username);
        if (!$return->contains($user)) {
            $return->add($user);
        }
    }

    return $return;
}

这是我的验证器:

<?php

public function isValid($value, Constraint $constraint)
{
    $repo = $this->em->getRepository('MyUserBundle:User');
    $notValidUsernames = array();

    foreach ($value as $user) {
        $username = $user->getUsername();
        if (!($user = $repo->findOneByUsername($username))) {
            $notValidUsernames[] = $username;
        }
    }

    if (count($notValidUsernames) == 0) {
        return true;
    }

    // At least one username is not ok here

    // Create the list of usernames separated by commas
    $list = '';
    $i = 1;

    foreach ($notValidUsernames as $username) {
        if ($i < count($notValidUsernames)) {
            $list .= $username;
            if ($i < count($notValidUsernames) - 1) {
                $list .= ', ';
            }
        }
        $i++;
    }

    $this->setMessage(
            $this->translator->transChoice(
                'form.error.participant_not_found',
                count($notValidUsernames),
                array(
                    '%usernames%' => $list,
                    '%last_username%' => end($notValidUsernames)
                )
            )
    );

    return false;
}

当前的实现看起来很难看。我可以清楚地看到错误消息,但是DataTransformer返回的ArrayCollection中的用户与Doctrine不同步。

我有两个问题:

  • 我的验证器有什么方法可以修改参数中给出的值吗?这样我就可以将 DataTransformer 返回的 ArrayCollection 中的简单 User 实例替换为从数据库检索的实例?
  • 有没有一种简单而优雅的方法来完成我正在做的事情?

我想最简单的方法就是能够获取 DataTransformer 给出的错误消息。在食谱中,他们抛出了这个异常:

throw new TransformationFailedException(sprintf('An issue with number %s does not exist!', $val));
,如果我可以将不存在的用户名列表放入错误消息中,那就太酷了。

谢谢!

php symfony validation symfony-forms
1个回答
2
投票

我是回答你上一个帖子的人,所以也许其他人会跳到这里。

您的代码可以大大简化。您只处理用户名。无需使用对象或数组集合。

public function reverseTransform($val)
{
    if (empty($val)) { return null; }


    // Extract usernames in an array from the raw text
    // $val = str_replace("\r\n", "\n", trim($val));
    $usernames = explode("\n", $val);
    array_map('trim', $usernames);

    // No real need to check for dups here
    return $usernames;
}

验证者:

public function isValid($userNames, Constraint $constraint)
{
    $repo = $this->em->getRepository('SkepinUserBundle:User');
    $notValidUsernames = array();

    foreach ($userNames as $userName) 
    {
      if (!($user = $repo->findOneByUsername($username))) 
        {
            $notValidUsernames[$userName] = $userName; // Takes care of dups
        }
    }

    if (count($notValidUsernames) == 0) {
        return true;
    }

    // At least one username is not ok here
    $invalidNames = implode(' ,',$notValidUsernames);


$this->setMessage(
        $this->translator->transChoice(
            'form.error.participant_not_found',
            count($notValidUsernames),
            array(
                '%usernames%' => $invalidNames,
                '%last_username%' => end($notValidUsernames)
            )
        )
);

    return false;
}

====================================================== ========================

所以此时

  1. 我们使用 Transformer 从文本区域复制数据,并在 form->bind() 期间生成用户名数组。

  2. 然后我们使用验证器来确认每个用户名确实存在于数据库中。如果有任何不符合的情况,我们会生成一条错误消息,并且 form->isValid() 将失败。

  3. 现在我们回到了控制器,我们知道我们有一个有效的用户名列表(可能是逗号分隔或可能只是一个数组)。现在我们要将它们添加到我们的线程对象中。

一种方法是创建一个线程管理器服务并向其中添加此功能。所以在控制器中我们可能有:

$threadManager = $this->get('thread.manager');
$threadManager->addUsersToThread($thread,$users);

对于线程管理器,我们将注入实体管理器。在 add users 方法中,我们将获取每个用户的引用,验证线程是否还没有到该用户的链接,调用 $thread->addUser() 然后刷新。

我们已将此类功能包装到服务类中,这将使测试变得更容易,因为我们还可以创建命令对象并从命令行运行它。它还为我们提供了一个添加额外线程相关功能的好地方。我们甚至可以考虑将此管理器注入到用户名验证器中,并将一些 isValid 代码移至管理器。

© www.soinside.com 2019 - 2024. All rights reserved.