2016-01-23 62 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