Подтвердить что ты не робот

Порядок создания поля CollectionType формы Symfony

В моей модели у меня есть объект рецепта и объект Ingredient. В объекте Рецепт отношение определяется следующим образом:

/**
 * @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

Добавление и показ рецепта отлично работают до сих пор, однако есть проблема с действием Edit/Update, которое я попытаюсь описать ниже:

В контроллере я загружаю объект и создаю форму следующим образом:

  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"}), начальная загрузка и отображение ингредиентов прекрасно работает. Однако, если пользователь перетаскивает и опускает ингредиенты, значения приоритета изменяются. Если есть ошибки проверки формы и форма должна отображаться повторно, компоненты внутри формы отображаются в прежнем порядке, даже если значения приоритета обновляются.

Например, у меня есть следующие начальные значения Ingredient = > priority в 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 для упорядочения строк, но форма игнорирует новый порядок.

Я также пытался использовать обработчики отправки PRE/POST в классе FormType для упорядочения строк, однако форма все еще игнорирует новый порядок.

Последнее, что работает (вид):

В сущности рецепта определите метод 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

Это работает, но форма создается и обрабатывается дважды, и, честно говоря, это выглядит как хак...

Я ищу лучший способ решить проблему.

4b9b3361

Ответ 1

Вам нужно использовать метод 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;
    });
}