2012-02-22 38 views
2

我問了this question,發現我們無法得到DataTransformer拋出的錯誤信息(根據唯一回答的用戶,也許這是可能的,我不知道)。Symfony2數據轉換器,驗證器和錯誤信息

無論如何,現在我知道了,我被困在一個驗證問題。假設我的模型是這樣的:我有包含幾個參與者(用戶)的線程。

<?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中是否存在。

這裏是\ n分隔的用戶列表轉換爲ArrayCollection(這樣的數據綁定是確定)的DataTransformer:

<?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));,如果我可以在錯誤消息中放置不存在的用戶名列表,它會很酷。

謝謝!

回答

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. 我們已經使用變換器從文本區域複製數據,並在form-> bind()過程中生成用戶名數組。

  2. 然後我們使用驗證器來確認每個用戶名實際存在於數據庫中。如果有任何不那麼我們產生一個錯誤信息和form-> isValid()將失敗。所以現在我們回到了控制器中,我們知道我們有一個有效的用戶名(可能是逗號分隔或可能只是一個數組)的列表。現在我們想將這些添加到我們的線程對象中。

一種方法是創建一個線程管理器服務並將此功能添加到它。所以在控制器中我們可能有:

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

對於線程管理器我們會注入我們的實體管理器。在add用戶方法中,我們將獲得對每個用戶的引用,驗證該線程是否還沒有指向此用戶的鏈接,請調用$ thread-> addUser(),然後刷新。

事實上,我們已經將這種功能包裝到一個服務類中會使事情變得更容易測試,因爲我們也可以創建一個命令對象並從命令行運行它。它也給我們一個很好的地方添加額外的線程相關的功能。我們甚至可以考慮將此管理器注入用戶名驗證程序,並將一些isValid代碼移至經理。

+1

而你如何處理數據綁定?如果Doctrine沒有爲「參與者」字段(它是OneToMany)獲取ArrayCollection,它會給出錯誤。我應該以與「參與者」無關的形式製作一個專業領域嗎?也許你有一個鏈接呢? – Nanocom 2012-02-22 20:11:21

+0

無論如何,感謝您的回答 – Nanocom 2012-02-22 20:11:32

+0

添加了一個新字段「用戶名」到我的線程實體,包含數組。如果表單有效,我在ArrayCollection中添加用戶名。但仍然存在問題。如何從驗證程序獲取用戶實例? – Nanocom 2012-02-22 20:46:54