Order of Symfony form CollectionType field

丶灬走出姿态 提交于 2021-01-18 09:48:26

问题


In my model I have a Recipe entity and Ingredient entity. In Recipe entity, the relation is defined like this:

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

In Ingredient entity:

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

I am working on CRUD controller for the recipe and I want the user to be able to add ingredients dynamically. I also want the user to drag-and-drop ingredients to set their priority (order) in the recipe. I am using CollectionType form field for this.

and this page as tutorial:

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

Adding and showing of the recipe are working perfectly so far, however there is a problem with Edit/Update action, which I will try to describe below:

In the controller, I load the entity and create the form like this:

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


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

      ...

    }

Since the priority is saved in the DB, and I have @ORM\OrderBy({"priority" = "ASC"}), the initial loading and display of ingredients works fine. However if the user drags and drops ingredients around, the priority values change. In case there are form validation errors and the form needs to be displayed repeatedly, ingredients inside the form get displayed in the old order, even though priority values are updated.

For example, I have the following initial Ingredient => priority values in DB:

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

The form rows are displayed in order: A,B,C;

After user changes the order, I have:

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

but the form rows are still displayed as A,B,C;

I understand that the form has been initialized with order A,B,C, and updating priority doesn't change the element order of ArrayCollection. But I have (almost) no idea how to change it.

What I have tried so far:

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

This doesn't work, as apparently it isn't allowed to use setData() on form which already has input.

I have also tried to set a DataTransformer to order the rows, but the form ignores new order.

I have also tried to use PRE/POST submit handlers in the FormType class to order the rows, however the form still ignores the new order.

The last thing that (kind of) works is this:

In Recipe entity, define sortIngredients() method which sorts ArrayCollection in memory,

  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;
  }

Then, in the controller:

  $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

This works, but the form is created and processed twice, and honestly it looks like a hack...

I am looking for a better way to solve the problem.


回答1:


You need to use finishView method of your form type.

Here is the example of code:

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;
    });
}


来源:https://stackoverflow.com/questions/34967795/order-of-symfony-form-collectiontype-field

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!