在我的模型中,我有一个菜谱实体和一个食材实体。在菜谱实体中,关系的定义如下:
/**
* @ORM\OneToMany(targetEntity="Ingredient", mappedBy="recipe", cascade={"remove", "persist"}, orphanRemoval=true)
* @ORM\OrderBy({"priority" = "ASC"})
*/
private $ingredients;
在Ingredient实体中:
/**
* @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);
...
}
由于优先级在数据库中已保存,并且我有@ORM\OrderBy({"priority" = "ASC"})
,因此成分的初始加载和显示工作正常。但是,如果用户拖放成分,则优先级值会更改。如果存在表单验证错误并且需要反复显示表单,则表单中的成分将按旧顺序显示,即使更新了优先级值。
例如,数据库中的初始成分 => 优先级值如下:
- 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提交处理程序来对行进行排序,然而表单仍然忽略了新顺序。
最后一件(有点)有效的事情是:
在Recipe实体中,定义一个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
这个方法可以运行,但表单会被创建和处理两次,而且老实说它看起来像是一个hack...
我正在寻找更好的解决问题的方法。