2016-07-22 99 views
1

我正在使用Symfony 2.7創建一個ToDo列表。該Form包含列表名稱和簡單的文本字段的items集合:Symfony表單 - 重新排序/排序CollectionType元素

class TodoType extends AbstractType { 
    public function getName() { 
     return 'todo_list'; 
    } 

    public function setDefaultOptions(OptionsResolverInterface $resolver) { 
     $resolver->setDefaults(array(
      'data_class' => 'AppBundle\Entity\TodoList', 
     )); 
    } 

    public function buildForm(FormBuilderInterface $builder, array $options) { 
     $builder 
      ->add('name', 'text', array(
       'label' => 'Name', 
       ... 
      )) 
      ->add('create', 'submit', array(
       'label' => 'Save', 
       ... 
      )); 

      $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { 
       $todoList = $event->getData(); 
       $form = $event->getForm(); 

       $form 
        ->add('items', 'collection', array(
         'type' => 'todo_list_item_type', 
         'allow_add' => true, 
         'allow_delete' => true, 
         //'by_reference' => false, 
         'data' => ($todoList != null ? $todoList->getItems() : null), 
        )); 

      }); 
     } 
    } 
} 

這工作得很好。該項目陣列包括在使用「陣列式」的名字形式:

<form ...> 
    ... 
    <ul class="sortable"> 
     <li> 
      <input id="todo_list_items_0_name" class="item-name form-control" type="text" value="Shopping" placeholder="Name" required="required" name="todo_list[items][0][name]"> 
     </li> 
     <li> 
      <input id="todo_list_items_1_name" class="item-name form-control" type="text" value="Wash car" placeholder="Name" required="required" name="todo_list[items][1][name]"> 
     </li> 
     ... 
    <ul> 

現在我想用jQuery Sortable Plugin進行重新排序。這工作正常。用戶可以按他喜歡的方式重新排序項目。但是,這不會影響項目的存儲順序。

我認爲這個問題是顯而易見的:的排序插件只改變DOM,而項目名稱中的項目,其中包括他們的數組索引的位置,保持不變。因此,Item0在提交表單時仍然有index = 0,不管它在列表中的新位置。

有沒有解決方案來解決這個問題?我發現的唯一解決方案是聽取排序插件的stop事件(一旦重新排序完成後觸發),並使用一些JavaScript手動查找/替換字段名稱中的索引編號。這當然會起作用,但對字段名稱的更改會很脆弱。例如,如果未來版本的Symfony將使用some_field_name_0而不是some_field_0_name,則替換代碼將不再工作。

另一種解決方案是,將Item類擴展爲附加字段sortOrder,並將該信息作爲隱藏字段包含在表單中。我仍然需要使用JS手動更新這個索引,但至少我會對格式有更多的控制。

但是我想知道沒有更簡單的方法。排序集合類型沒有什麼特別的。所以也許有一個開箱即用的解決方案?

回答

1

你的方法看起來對我來說合理(儘管我會進一步討論另一種選擇)。沒有開箱即用的解決方案,集合表單類型依賴於數據「自然」的順序。

最後一個問題的解決方案是使用「原型」選項。在您的Twig視圖中,您可以顯示原型,這意味着集合中任何(新)表單的HTML。
這是如果你使用form_widgetform_row自動完成,否則,你可以做到這一點(從文檔here):

<ul class="tags" data-prototype="{{ form_widget(form.tags.vars.prototype)|e('html_attr') }}"> 
    ... 
</ul> 

然後你就可以很容易地,可靠地獲取表單名稱:

// Get the data-prototype explained earlier 
var prototype = $('.tags').data('prototype'); 

// I assume we use jQuery 
var $prototype = $(prototype); 

// Get the item name 
// by convention, instad of a numeric index it will contain __name__ 
var itemName = $prototype.attr('name'); 

// We can replace '__name__' it to whatever you like 
var nameFor1 = itemName.replace(/__name__/g, 1); 

另一種方法

另一種方法是將position字段添加到todo_list_item元素。假設它是一個Doctrine實體,您可以使用它上面的Sortable Doctrine擴展:您可以在文檔中找到所有詳細信息here,儘管您不需要自己手動啓用Doctrine擴展,但可以使用StofDoctrineExtensionBundle

如果它是一個簡單的數據數組(或者您不希望添加擴展名或將位置保存到數據庫),則可以從setItems方法中重新排序項目。

+0

謝謝!用重新設置索引號時很容易識別的一些自定義內容替換'__name__'是個不錯的選擇。我已經使用原型動態地添加新的項目到表單中,我可以在這一步中輕鬆地更改'__name__'的值。不過,我不明白這是如何工作的項目已經存在,因此與表單加載。我對這些價值/物品沒有任何影響。 –

+0

以您找到的解決方案,重新命名字段,以便將正確的順序「發送」給Symfony,它爲您提供了一個生成新名稱的模板。因此,如您所述,使您的解決方案免受Symfony升級/更改。 – romaricdrigon