"Agregar otro elemento" lento con campos de valor ilimitado

8

En Drupal 7, cuando tiene un nodo con campo que tiene valores ilimitados (digamos, campo de imagen), el tiempo de respuesta "agregar otro elemento" se vuelve muy lento después de agregar 10-20 elementos. ¿Cómo luchas contra este problema? ¿Alguna vez has encontrado este problema?

Creé un proyecto, donde el usuario puede agregar hasta 100 valores de un campo de imagen que, en teoría, tiene valores ilimitados. Pero, después de agregar una docena de imágenes, cada nuevo clic en "Agregar otro elemento" se vuelve más lento que el anterior. Sé que esto sucede debido al hecho de que Drupal reconstruye este campo y todos sus valores después de cada solicitud de ajax, por lo que cuantos más valores haya agregado, más trabajo tendrá que hacer Drupal en cada solicitud de "ajax", pero, en realidad, esto no es Muy buena cosa.

¿Hay algún enfoque sobre cómo cambiar / anular tal comportamiento?

Timur Kamanin
fuente

Respuestas:

3

Sobre la base de la respuesta de Charlie, descubrí que se tarda aproximadamente la misma cantidad de tiempo en volver a cargar el bloque si está agregando 1 o 100 elementos, por lo que aquí hay un truco para agregar una lista selecta de números en el formulario junto a 'agregar más "para que pueda elegir cuántos está agregando. Esto ahorra mucho tiempo y sigue siendo flexible. Podría ser envuelto en un pequeño módulo

<?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;
  }
}
?>

También publiqué la sugerencia en Drupal.org en https://drupal.org/node/1394184#comment-8252701 donde el operador tuvo un problema similar.

jowan sebastian
fuente
Adapte el código anterior para un campo personalizado con cardinalidad ilimitada y funcionó bien para mí. El único cambio que hice a la lógica central fue restar 1 de $ numbtoadd antes de usarlo. Creo que esto se debe a que items_count está subrepresentada ya que está basada en cero.
Dave Bruns
2

Es la vuelta de soplo de la naturaleza de la forma de la API y cómo se hace todo el $formy $form_statedisponibles de nuevo en el servidor. Esto es algo genial por muchas razones, aunque estoy de acuerdo en que puede ser bastante molesto desde una perspectiva de rendimiento. Algunas estadísticas en un servidor Ubuntu 12.04 que ejecuta Apache2 con PHP-FPM:

  • Agregué 30 elementos a un campo de archivo, agregué y cargué 1 a la vez, y el tiempo total para la carga + respuesta del servidor + inserción de JavaScript del nuevo elemento pasó de 414 milisegundos, aumentando en cada carga sucesiva entre 0-20 milisegundos, terminando en 800 milisegundos para el viaje número 30.

  • Hice clic en "Agregar otro elemento" para un campo de texto ilimitado 100 veces, y el tiempo total pasó de 337 milisegundos a 1.3 segundos. Si mi forma fuera más compleja, estos números solo aumentarían.

En $form_state['fields']['your_field_name']['und']existe una propiedad llamada items_count. Esto se utiliza para calcular la cantidad de widgets de campo que se deben mostrar para un campo determinado. Le recomendaría que lo use hook_field_attach_form()para alterar $form_state antes de que se construya el widget del campo y configure la items_countpropiedad del campo para que sea un número mayor, lo que le dará la cantidad de campos que necesita de inmediato. El usuario aún podrá agregar más elementos. Depende de usted encontrar una mejor manera de ocultar los elementos adicionales para que el formulario no tenga más de 10 páginas; tal vez un div con overflow: scroll;podría funcionar. De todos modos, este puede ser un punto de partida para que encuentres algo que permita que tu flujo de trabajo vaya más rápido:

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

Editar: al código de muestra le falta algo de lógica para garantizar que solo se ejecute para el formulario apropiado y no le permitirá 'agregar otro elemento'. Revisaré esto cuando tenga un mejor ejemplo de trabajo local.

Charlie Schliesser
fuente
Hola Charlie, también pensé en el truco que describiste, pero las cosas empeoran cuando tu usuario quiere reordenar los campos (en mi caso, es un requisito vital). Cuando intenta reordenar uno de los 100 campos a través de arrastrar y soltar, el navegador se bloquea para siempre ...
Timur Kamanin
¿Se cuelga solo en reordenar campos de archivos o campos de texto también? Eso parece extraño, ya que draggable.js no debería enviar nada al servidor, sino solo escuchar los cambios de fila y luego actualizar los campos de entrada ocultos. Además, ¿en qué navegador y versión está experimentando el bloqueo? Creo que cualquier cosa que descubramos aquí podría ser útil para muchos otros usuarios.
Charlie Schliesser
Sí, si tiene un caso de uso reproducible de draggable.js colgando que requiere un problema central.