我问this question并发现我们无法获取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
}
对于线程创建,我希望用户在textarea中指定参与者用户名,以“\ n”分隔。 我希望如果指定的一个或多个用户名不存在,则会显示一条消息,其中包含不存在的用户名。 例如,“用户titi,tata和toto不存在”。
为此,我创建了一个DataTransformer,它将textarea中的原始文本转换为包含用户实例的ArrayCollection。由于我无法获得此DataTransformer提供的错误消息(这太可惜了!这真的不可能吗?),我不会检查DataTransformer中的每个用户名是否存在,而是在Validator中。
这是DataTransformer,它将\ n分离的用户列表转换为ArrayCollection(以便DataBinding正常):
<?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给出的错误消息。在cookbook中,他们抛出了这个异常:throw new TransformationFailedException(sprintf('An issue with number %s does not exist!', $val));
,如果我可以在错误消息中放入不存在的用户名列表,那就太酷了。
谢谢!
答案 0 :(得分: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;
}
=============================================== ==========================
所以此时
我们使用变换器从文本区域复制数据,并在form-&gt; bind()期间生成一组用户名。
然后我们使用验证器确认每个用户名实际存在于数据库中。如果有任何不这样做,那么我们会生成错误消息,而form-&gt; isValid()将失败。
所以现在我们回到控制器中,我们知道我们有一个有效用户名列表(可能是逗号分隔或可能只是一个数组)。现在我们要将这些添加到我们的线程对象中。
一种方法是创建线程管理器服务并向其添加此功能。所以在控制器中我们可能有:
$threadManager = $this->get('thread.manager');
$threadManager->addUsersToThread($thread,$users);
对于线程管理器,我们将注入我们的实体管理器。在add users方法中,我们将获得对每个用户的引用,验证该线程还没有到该用户的链接,调用$ thread-&gt; addUser()然后刷新。
我们将这种功能包装到服务类中的事实将使事情更容易测试,因为我们也可以创建一个命令对象并从命令行运行它。它还为我们提供了添加其他线程相关功能的好地方。我们甚至可以考虑将此管理器注入用户名验证器并将一些isValid代码移动到管理器。