2016-01-23 71 views
11

在我的模型中,我有一個Recipe實體和Ingredient實體。在配方實體的關係是這樣定義的:Symfony表單CollectionType字段的順序

/** 
* @ORM\OneToMany(targetEntity="Ingredient", mappedBy="recipe", cascade={"remove", "persist"}, orphanRemoval=true) 
* @ORM\OrderBy({"priority" = "ASC"}) 
*/ 
private $ingredients; 

中的成分實體:

/** 
* @ORM\ManyToOne(targetEntity="Recipe", inversedBy="ingredients") 
* @ORM\JoinColumn(name="recipe_id", referencedColumnName="id") 
*/ 
private $recipe; 

我的工作CRUD控制器上的食譜,我希望用戶能夠動態地添加成分。我還希望用戶拖放配料以在配方中設置其優先級(順序)。我爲此使用了CollectionType表單字段。

這個頁面教程:

http://symfony.com/doc/current/cookbook/form/form_collections.html

添加和配方的表現是完美的,到目前爲止的工作,但沒有與編輯/更新動作,我會嘗試下面描述的一個問題:

在控制器中,我加載實體和創建這樣的形式:

public function updateAction($id, Request $request) 
    { 
     $em = $this->getDoctrine()->getManager(); 
     $recipe = $em->getRepository('AppBundle:Recipe')->find($id); 


     $form = $this->createEditForm($recipe); 
     $form->handleRequest($request); 

     ... 

    } 

由於先驗ty保存在數據庫中,我有@ORM\OrderBy({"priority" = "ASC"}),原料的初始加載和顯示正常。但是,如果用戶拖放配料周圍,優先級值會改變。如果存在表單驗證錯誤並且表單需要重複顯示,則即使更新了優先級值,表單中的成分也會按舊的順序顯示。

例如,我有以下的初始成分=>的優先級值在DB:

  • A => 1
  • B => 2
  • C => 3

表格行按順序顯示:A,B,C;

用戶改變訂單後,我有:

  • B => 1
  • A => 2
  • C => 3

但形式行仍顯示作爲A,B,C;

我知道窗體已經初始化爲A,B,C,並且更新priority不會改變ArrayCollection的元素順序。但是我幾乎不知道如何改變它。

我迄今爲止嘗試:

$form->getData(); 
// sort in memory 
$form->setData(); 

這是不行的,因爲顯然它是不允許上已經有輸入表單中使用使用setData()。

我也嘗試設置DataTransformer來排序行,但表單忽略新的順序。

我也嘗試在FormType類中使用PRE/POST提交處理程序來排序行,但是表單仍然會忽略新的順序。

那(種)工作的最後一件事情是這樣的:

在配方實體,定義sortIngredients()方法,它在內存中排序的ArrayCollection,

public function sortIngredients() 
    { 
     $sort = \Doctrine\Common\Collections\Criteria::create(); 
     $sort->orderBy(Array(
      'priority' => \Doctrine\Common\Collections\Criteria::ASC 
    )); 

     $this->ingredients = $this->ingredients->matching($sort); 

     return $this; 
    } 

然後,控制器:

$form = $this->createEditForm($recipe); 
    $form->handleRequest($request); 

    $recipe->sortIngredients(); 

    // repeatedly create and process form with already sorted ingredients 
    $form = $this->createEditForm($recipe); 
    $form->handleRequest($request); 

    // ... do the rest of the controller stuff, flush(), etc 

這個工程,但形式創建和處理兩次,老實說,它看起來像一個黑客...

我在尋找更好的方法來解決問題。

回答

9

您需要使用表單類型的finishView方法。

這裏是代碼的例子:

public function finishView(FormView $view, FormInterface $form, array $options) 
{ 
    usort($view['photos']->children, function (FormView $a, FormView $b) { 
     /** @var Photo $objectA */ 
     $objectA = $a->vars['data']; 
     /** @var Photo $objectB */ 
     $objectB = $b->vars['data']; 

     $posA = $objectA->getSortOrder(); 
     $posB = $objectB->getSortOrder(); 

     if ($posA == $posB) { 
      return 0; 
     } 

     return ($posA < $posB) ? -1 : 1; 
    }); 
} 
+3

作品!花了半天的時間...謝謝你。太糟糕了,Symfony網站上關於這種方法的文檔並不多。 – Karolis