Медленно «Добавить еще один элемент» с неограниченными полями

8

В Drupal 7, когда у вас есть узел с полем, которое имеет неограниченные значения (скажем, поле изображения), время отклика «добавить другой элемент» становится очень медленным после добавления 10–20 элементов. Как вы боретесь с этой проблемой? Вы когда-нибудь сталкивались с этой проблемой?

Я создал проект, в котором пользователь может добавить до 100 значений поля изображения, которое теоретически имеет неограниченные значения. Но после добавления дюжины изображений каждый новый щелчок «Добавить еще один элемент» становится медленнее, чем предыдущий. Я знаю, что это происходит из-за того факта, что Drupal перестраивает это поле и все его значения после каждого ajax-запроса, поэтому чем больше значений вы добавили, тем больше работы Drupal должен выполнять для каждого «ajax-запроса», но на самом деле это не так. довольно хорошая вещь.

Есть ли какие-либо подходы к тому, как изменить / переопределить такое поведение?

Тимур Каманин
источник

Ответы:

3

Основываясь на ответе Чарли, я обнаружил, что перезагрузка блока занимает примерно столько же времени, если вы добавляете 1 или 100 элементов, так что вот хитрость, чтобы добавить список выбора номеров в форме рядом с «добавить» больше », так что вы можете выбрать, сколько вы добавляете. Это экономит много времени и остается гибким. Можно обернуть в маленький модуль

<?php
/**
* Implements hook_field_attach_form()
*/
function village_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode){
  $options = array('language' => field_valid_language($langcode));
  // Merge default options.
  $default_options = array(
    'default' => FALSE,
    'deleted' => FALSE,
    'language' => NULL,
  );
  $options += $default_options;
  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
  $instances = _field_invoke_get_instances($entity_type, $bundle, $options);
  // Iterate through the instances.
  $return = array();
  foreach ($instances as $instance) {
    // field_info_field() is not available for deleted fields, so use
    // field_info_field_by_id().
    $field = field_info_field_by_id($instance['field_id']);
    $field_name = $field['field_name'];
    //If we are looking at our field type and specific widget type, and we are multiple entries
    if($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED){
      //Check just in case the button is here, and add another #submit function
      if(isset($form[$field['field_name']]['und']['add_more'])){
        // add a simple select list, this defaults to numb 3
        $form[$field['field_name']]['add_more_number'] = array(
          '#type' => 'select',
          '#title' => t('Add more no.'),
          '#options' => drupal_map_assoc(range(0, 50)),
          '#default_value' => 2,
        );
        $form[$field['field_name']]['und']['add_more']['#submit'][] = 'village_field_add_more_submit';
        $form[$field['field_name']]['und']['add_more']['#value'] = 'Add more rows';
      }
    }
  }
}
function village_field_add_more_submit($form, &$form_state){
  $button = $form_state['triggering_element'];
  // Go one level up in the form, to the widgets container.
  $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1));
  $field_name = $element['#field_name'];
  $langcode = $element['#language'];
  $parents = $element['#field_parents'];
  // Alter the number of widgets to show. items_count = 0 means 1.
  $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
  //get the number from the select
  $numbtoadd = $form[$field_name]['add_more_number']['#value'];
  if($numbtoadd){
    $field_state['items_count'] += $numbtoadd;
    field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
    $form_state['rebuild'] = TRUE;
  }
}
?>

Я также разместил предложение на Drupal.org по адресу https://drupal.org/node/1394184#comment-8252701, где у ОП была похожая проблема.

Джован Себастьян
источник
Я адаптировал приведенный выше код для настраиваемого поля с неограниченным количеством элементов, и он работал хорошо для меня. Единственное изменение, которое я внес в основную логику, - это вычесть 1 из $ numbtoadd перед его использованием. Я думаю, это потому, что items_count недопредставлен, поскольку он основан на нуле?
Дэйв Брунс
2

Это обратная связь с природой API форм и с тем, как все это делается $formи $form_stateдоступно обратно на сервер. Это крутая вещь по многим причинам, хотя я согласен, что это может быть довольно раздражающим с точки зрения производительности. Немного статистики на сервере Ubuntu 12.04 под управлением Apache2 с PHP-FPM:

  • Я добавил 30 элементов в поле файла, добавляя и выгружая по 1 за раз, и общее время загрузки + ответа сервера + вставки javascript нового элемента составляло 414 миллисекунд, увеличиваясь при каждой последующей загрузке примерно на 0-20 миллисекунды, заканчивающиеся в 800 миллисекундах для поездки номер 30.

  • Я щелкнул «Добавить еще один элемент» для неограниченного текстового поля 100 раз, и общее время выросло с 337 миллисекунд до 1,3 секунд. Если бы моя форма была более сложной, эти цифры только увеличивались бы.

В $form_state['fields']['your_field_name']['und']существует свойство называется items_count. Это используется для расчета количества виджетов полей, которые должны отображаться для данного поля. Я бы порекомендовал вам использовать hook_field_attach_form()для изменяете $form_state до того виджет месторождения построен и установить поля в items_countсвойстве быть больше числа, тем самым давая вам количество полей , которые нужно сразу же. Пользователь по-прежнему сможет добавлять больше элементов. Вам следует найти лучший способ скрыть лишние элементы от создания формы длиной в 10 страниц; возможно, div с overflow: scroll;может работать. В любом случае, это может быть отправной точкой для вас, чтобы найти что-то, что позволит вашему рабочему процессу идти быстрее:

function mymodule_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
  $form_state['field']['field_my_field'][$langcode]['items_count'] = 100;
}

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

Чарли Шлиссер
источник
Привет, Чарли, я подумал о трюке, который ты описал, но все становится еще хуже, когда твой пользователь хочет изменить порядок полей (в моем случае это жизненно важное требование). Когда вы пытаетесь изменить порядок одного из 100 полей с помощью drag'n'drop, браузер зависает навсегда ...
Тимур Каманин
Висит ли он только при переупорядочении файловых полей или текстовых полей? Это кажется странным, поскольку draggable.js не должен отправлять что-либо обратно на сервер, а просто прослушивает изменения строк и затем обновляет скрытые поля ввода. Кроме того, в каком браузере и версии вы зависаете? Я думаю, что все, что мы здесь обнаружим, может быть полезным для многих других пользователей.
Чарли Шлиссер
Да, если у вас есть воспроизводимый сценарий использования draggable.js, который вызывает основную проблему.