Drupal: перетаскиваемая родительско-дочерняя таблица

#drupal #drupal-7

#drupal #drupal-7

Вопрос:

Итак, я уже некоторое время занимаюсь этим вопросом. Я пытаюсь создать перетаскиваемую таблицу, которая имеет отношения «родитель-потомок», но в которой дочерние элементы не могут быть перемещены из родительской группы, и все родители могут быть отсортированы друг от друга. Я смоделировал свою форму и тему из кода меню администратора, и он дублирует эту функциональность. Проблема в том, что я могу переместить дочерние элементы к другому родителю или позволить ему стать родителем. В качестве иллюстрации:

 Category 1
|
|--Item 1
|--Item 2
Category 2
|
|--Item 3
|--Item 4
|--Item 5
 

Я хотел бы иметь возможность сортировать элементы 1 и 2 друг с другом, а также элементы 3, 4 и 5 друг с другом, но не перемещать их между категорией 1 и категорией 2. Мне также нужно иметь возможность сортировать категории 1 и категории 2 друг с другом, забирая с собой дочерние элементы. Я перебрал так много комбинаций $action , $group , $subgroup настроек, смешанных с $class настройками для категорий и элементов, что я потерял счет. Ничто из того, что я пробовал до сих пор, не дало желаемого результата. Вот соответствующие фрагменты моего кода в том виде, в каком он есть на данный момент:

В моей форме:

 $form['#tree'] = true;
foreach($categories as $cat) {
    if(!isset($form['categories'][$cat->cid])){
        $form['categories'][$cat->cid] = array(
            'weight' => array(
                '#type'         => 'weight',
                '#delta'        => 25,
                '#attributes'   => array('class' => array('item-weight', 'item-weight-' . $cat->cid)),
            ),
            'cid' => array(
                '#type'         => 'hidden',
                '#value'        => $cat->cid,
                '#attributes'   => array('class' => array('cid')),
            ),
        );
        foreach($cats[$cat->cid] as $item) {
            $form['categories'][$cat->cid]['items'][$item->id] = array(
                'weight' => array(
                    '#type'         => 'weight',
                    '#delta'        => 25,
                    '#default_value'=> $item->weight,
                    '#attributes'   => array('class' => array('item-weight', 'item-weight-' . $cat->cid)),
                ),
                'cid' => array(
                    '#type'         => 'hidden',
                    '#value'        => $cat->cid,
                    '#attributes'   => array('class' => array('cid')),
                ),
            );
        }
    }
}
 

В моей теме:

 $children = element_children($form['categories']);
$rows = array();
if(count($children) > 0) {
    foreach($children as $cid) {
        $row = array(
            drupal_render($form['categories'][$cid]['weight']) .
                drupal_render($form['categories'][$cid]['cid']),
        );

        $rows[] = array(
            'data' => $row,
            'class' => array('draggable', 'tabledrag-root'),
        );
        foreach(element_children($form['categories'][$cid]['items']) as $id) {
            $row = array(
                theme('indentation', array('size' => 1)) . drupal_render($form['categories'][$cid]['items'][$id]['name']),
                drupal_render($form['categories'][$cid]['items'][$id]['weight']) .
                    drupal_render($form['categories'][$cid]['items'][$id]['cid']),
            );

            $rows[] = array(
                'data' => $row,
                'class' => array('draggable', 'tabledrag-leaf'),
            );
        }
        drupal_add_tabledrag('cat-table', 'order', 'sibling', 'item-weight', 'item-weight-' . $cid);
    }
}


drupal_add_tabledrag('cat-table', 'match', 'parent', 'cid', 'cid', 'cid', true, 1);
$output = theme('table', array('header' => $headers, 'rows' => $rows, 'attributes' => array('id' => 'cat-table')));
$output .= drupal_render_children($form);
return $output;
 

Я прочитал документацию drupal_add_tabledrag() , посмотрел код, посмотрел пример кода и поискал drupal.org и погуглил, но ничего не придумал.

Пока мое единственное решение — скопировать и изменить tabledrag.js файл, чтобы просто устранить эти возможности, но, устраняя проблему отступа с элементами (что означает, что они не должны совпадать с категориями), сохранять их в одной категории было неинтересно.

Я полагаю, что самый важный вопрос заключается в том, возможно ли это при использовании стандартного Drupal?

Комментарии:

1. как использовать fieldset для ее форматирования? api.drupal.org/api/drupal /…

Ответ №1:

Я знаю, что вы уже много кодировали, поэтому, возможно, вы не захотите отказываться от этого на данный момент, но DraggableViews отлично подходит для этого. Вы можете настроить обычный вид и добавить этот фильтр draggableviews, он добавляет вес и, при необходимости, родительскую ссылку. Само представление использует ту же систему перетаскивания, что и остальные внутренние таблицы Drupal.

В качестве альтернативы вы можете использовать ссылку на термин и привязать термины таксономии к узлам и просто использовать это перетаскивание.

Если я что-то упускаю из виду в ваших потребностях, мои извинения, просто подумал, что предложу это более простое решение, поскольку оно определенно хорошо служило мне в прошлом. Удачи в любом случае.

Комментарии:

1. Как вы это делаете без модуля? Это должно быть возможно с помощью drupal_add_tabledrag, но я тоже не могу найти способ предотвратить перетаскивание между родителями. Таблица, которую я сортирую, — это форма, а не связанные с ней представления.

Ответ №2:

Только что закончил добавлять эту функциональность в мой модуль

https://github.com/player259/ajax_table

Помощи нет, демо устарело, но я время от времени работаю над этим

Поддержка разделов была достигнута путем переопределения tabledrag.js функции

Используйте этот фрагмент для вставки таблицы

 $form['map'] = array(
  '#type' => 'ajax_table',
  '#header' => array(t('Element'), t('Settings'), t('Weight')),
  'rows' => array(),
  '#draggable' => array(
    // drupal_add_tabledrag will be called in theme layer
    // NULL first arg to apply to this table
    array(NULL, 'match', 'parent', 'perfect-form-parent', 'perfect-form-parent', 'perfect-form-index'),
    array(NULL, 'depth', 'group', 'perfect-form-depth', NULL, NULL, FALSE),
    array(NULL, 'order', 'sibling', 'perfect-form-weight'),
  ),
  '#draggable_groups' => array(),
);

foreach ($map as $i => $element) {

  // ... some logic

  $form['map']['rows'][$i] = array(
    'data' => array(
      'element' => array(),
      'settings' => array(),
      'tabledrag' => array(
        'index' => array(
          '#type' => 'hidden',
          '#value' => $element['data']['tabledrag']['index'],  
          '#attributes' => array('class' => array('perfect-form-index')),
        ),
        'parent' => array(
          '#type' => 'hidden',
          '#default_value' => $element['data']['tabledrag']['parent'],
          '#attributes' => array('class' => array('perfect-form-parent')),
        ),
        'depth' => array(
          '#type' => 'hidden',
          '#default_value' => $element['data']['tabledrag']['depth'],
          '#attributes' => array('class' => array('perfect-form-depth')),
        ),          
        'weight' => array(
          '#type' => 'weight',
          '#delta' => $max_weight,
          '#default_value' => $weight,
          '#attributes' => array('class' => array('perfect-form-weight')),
        ),
      ),
    ),
    '#attributes' => array('class' => array($row_class_current, $row_class_child)),
  );

  // This means that row with $row_class_child class could have as parent
  // only row with $row_class_parent class
  // NULL means root - there are no parents

  $form['map']['#draggable_groups'][$row_class_child] =
    $depth ? $row_class_parent : NULL;
}
 

Ответ №3:

У меня была похожая проблема на работе, поэтому я разместил здесь свое решение, поскольку ни одно из найденных мной решений не работало корректно во всех ситуациях. Это сделано на 100% на javascript, на стороне php вам просто нужно установить tabledrag в соответствие с parent по pid и отсортировать с братьями и сестрами по весу.

Текущий код работает с модулем example (tabledrag parent / child), чтобы адаптировать его к вашим потребностям, измените .example-item-pid вашим классом для поля ввода PID. Вам просто нужно добавить ее в пример кода, чтобы она работала, и посмотреть, соответствует ли она вашим потребностям.

Первая функция аннулирует любую попытку удаления элементов, которые не имеют того же родительского элемента (PID), что и целевой элемент.

Вторая функция обходит функцию перетаскивания, чтобы поместить элемент в нужное место (= последние дочерние элементы целевой строки) и на нужную глубину (= на ту же глубину, что и целевая строка).

 /**
 * Invalidate swap check if the row target is not of the same parent
 * So we can only sort elements under the same parent and not move them to another parent
 *
 * @override Drupal.tableDrag.row.isValidSwap
 */
// Keep the original implementation - we still need it.
Drupal.tableDrag.prototype.row.prototype._isValidSwap = Drupal.tableDrag.prototype.row.prototype.isValidSwap;
Drupal.tableDrag.prototype.row.prototype.isValidSwap = function(row) {
  if (this.indentEnabled) {
    if (row amp;amp; $('.example-item-pid', this.element).val() !== $('.example-item-pid', row).val()) {
      return false;
    }
  }

  // Return the original result.
  return this._isValidSwap(row);
}

/**
 * Position the dragged element under the last children of the element target for swapping when moving down our dragged element.
 * Removed the indentation, since we can not change parent.
 * @override Drupal.tableDrag.row.dragRow
 */
Drupal.tableDrag.prototype.dragRow = function (event, self) {
  if (self.dragObject) {
    self.currentMouseCoords = self.mouseCoords(event);

    var y = self.currentMouseCoords.y - self.dragObject.initMouseOffset.y;
    var x = self.currentMouseCoords.x - self.dragObject.initMouseOffset.x;

    // Check for row swapping and vertical scrolling.
    if (y != self.oldY) {
      self.rowObject.direction = y > self.oldY ? 'down' : 'up';
      self.oldY = y; // Update the old value.

      // Check if the window should be scrolled (and how fast).
      var scrollAmount = self.checkScroll(self.currentMouseCoords.y);
      // Stop any current scrolling.
      clearInterval(self.scrollInterval);
      // Continue scrolling if the mouse has moved in the scroll direction.
      if (scrollAmount > 0 amp;amp; self.rowObject.direction == 'down' || scrollAmount < 0 amp;amp; self.rowObject.direction == 'up') {
        self.setScroll(scrollAmount);
      }

      // If we have a valid target, perform the swap and restripe the table.
      var currentRow = self.findDropTargetRow(x, y);
      if (currentRow) {
        if (self.rowObject.direction == 'down') {

          /**
           * When going down we want to position the element after the last children and not right under the currentRow
           */
          // create a new row prototype with currentRow
          var rowObject = new self.row(currentRow, 'mouse', self.indentEnabled, self.maxDepth, false);
          // extract all children
          var childrenRows = rowObject.findChildren();
          // if we have children
          if (childrenRows.length > 0) {
            // we change the row to swap with the last children
            currentRow = childrenRows[childrenRows.length - 1];
          } 

          self.rowObject.swap('after', currentRow, self);
        }
        else {
          self.rowObject.swap('before', currentRow, self);
        }
        self.restripeTable();
      }
    }
    /**
     * We have disabled the indentation changes since it is not possible to change parent.
     */

    return false;
  }
};