我问了这个问题,发现我们无法获取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 给出的错误消息。在食谱中,他们抛出了这个异常:
throw new TransformationFailedException(sprintf('An issue with number %s does not exist!', $val));
,如果我可以将不存在的用户名列表放入错误消息中,那就太酷了。
谢谢!
我是回答你上一个帖子的人,所以也许其他人会跳到这里。
您的代码可以大大简化。您只处理用户名。无需使用对象或数组集合。
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;
}
====================================================== ========================
所以此时
我们使用 Transformer 从文本区域复制数据,并在 form->bind() 期间生成用户名数组。
然后我们使用验证器来确认每个用户名确实存在于数据库中。如果有任何不符合的情况,我们会生成一条错误消息,并且 form->isValid() 将失败。
现在我们回到了控制器,我们知道我们有一个有效的用户名列表(可能是逗号分隔或可能只是一个数组)。现在我们要将它们添加到我们的线程对象中。
一种方法是创建一个线程管理器服务并向其中添加此功能。所以在控制器中我们可能有:
$threadManager = $this->get('thread.manager');
$threadManager->addUsersToThread($thread,$users);
对于线程管理器,我们将注入实体管理器。在 add users 方法中,我们将获取每个用户的引用,验证线程是否还没有到该用户的链接,调用 $thread->addUser() 然后刷新。
我们已将此类功能包装到服务类中,这将使测试变得更容易,因为我们还可以创建命令对象并从命令行运行它。它还为我们提供了一个添加额外线程相关功能的好地方。我们甚至可以考虑将此管理器注入到用户名验证器中,并将一些 isValid 代码移至管理器。