【问题标题】:Order of Symfony form CollectionType fieldSymfony 表单 CollectionType 字段的顺序
【发布时间】:2016-01-23 19:27:27
【问题描述】:

在我的模型中,我有一个食谱实体和成分实体。在 Recipe 实体中,关系定义如下:

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

      ...

    }

由于优先级保存在数据库中,并且我有@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 提交处理程序对行进行排序,但是表单仍然忽略新的顺序。

(某种)起作用的最后一件事是:

在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

这行得通,但是表单被创建和处理了两次,老实说,它看起来像一个黑客......

我正在寻找解决问题的更好方法。

【问题讨论】:

    标签: forms symfony doctrine-orm arraycollection


    【解决方案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;
        });
    }
    

    【讨论】:

    • 有效!!!花了半天时间...谢谢。太糟糕了,Symfony 网站上没有太多关于这种方法的文档。
    • 伟大的!谢谢
    • @Sergey Fedotov 你是怎么知道的?任何地方都没有可用的文档吗?你刚刚调试了吗?
    【解决方案2】:

    可以将箭头函数 https://www.php.net/manual/en/functions.arrow.php 与太空飞船 https://www.php.net/manual/en/migration70.new-features.php 与 PHP 7 结合使用。

    与前面的例子:

    public function finishView(FormView $view, FormInterface $form, array $options)
    {
        usort($view['photos']->children, fn (FormView $a, FormView $b) => $a->vars['data']->getSortOrder() <=> $b->vars['data']->getSortOrder());
    }
    

    一个更高级的例子,使用任何有序字段从任何地方订购任何集合:

    public function finishView(FormView $view, FormInterface $form, array $options)
    {
        $view['blocks']->children = array_merge($view['blockTexts']->children, $view['blockImages']->children);
    
        usort($view['blocks']->children, fn (FormView $a, FormView $b) => $a->vars['data']->orderNumber <=> $b->vars['data']->orderNumber);
    }
    

    【讨论】:

      猜你喜欢
      • 2017-01-15
      • 2016-10-12
      • 1970-01-01
      • 1970-01-01
      • 2012-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-31
      相关资源
      最近更新 更多