«Ajouter un autre élément» lent avec des champs de valeur illimités

8

Dans Drupal 7, lorsque vous avez un nœud avec un champ qui a des valeurs illimitées (disons, un champ d'image), le temps de réponse "ajouter un autre élément" devient très lent après avoir ajouté 10 à 20 éléments. Comment combattez-vous ce problème? Avez-vous déjà rencontré ce problème?

J'ai créé un projet, où l'utilisateur peut ajouter jusqu'à 100 valeurs d'un champ d'image qui, en théorie, a un réglage de valeurs illimité. Mais, après avoir ajouté une douzaine d'images, chaque nouveau clic sur "Ajouter un autre élément" devient plus lent que le précédent. Je sais que cela se produit parce que Drupal reconstruit ce champ et toutes ses valeurs après chaque demande ajax, donc plus vous ajoutez de valeurs, plus Drupal a de travail à faire sur chaque demande "ajax", mais, en fait, ce n'est pas très bonne chose.

Existe-t-il des approches sur la façon de modifier / remplacer un tel comportement?

Timur Kamanin
la source

Réponses:

3

en s'appuyant sur la réponse de Charlie, j'ai constaté qu'il fallait environ le même temps pour recharger le bloc si vous ajoutez 1 ou 100 éléments, alors voici une astuce pour ajouter une liste de nombres dans le formulaire à côté de 'ajouter plus 'afin que vous puissiez choisir le nombre que vous ajoutez. Cela économise beaucoup de temps et reste flexible. Pourrait être enveloppé dans un petit module

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

J'ai également publié la suggestion sur Drupal.org à https://drupal.org/node/1394184#comment-8252701 où l'op avait un problème similaire.

jowan sebastian
la source
J'ai adapté le code ci-dessus pour un champ personnalisé avec une cardinalité illimitée et cela a bien fonctionné pour moi. La seule modification que j'ai apportée à la logique principale a été de soustraire 1 de $ numbtoadd avant de l'utiliser. Je pense que c'est parce que items_count est sous-représenté car il est basé sur zéro?
Dave Bruns
2

C'est le retour de la nature de l'API de formulaire et de la façon dont il rend l'intégralité $formet la $form_statedisponibilité sur le serveur. C'est une bonne chose pour de nombreuses raisons, bien que je convienne que cela peut être assez ennuyeux du point de vue des performances. Quelques statistiques sur un serveur Ubuntu 12.04 exécutant Apache2 avec PHP-FPM:

  • J'ai ajouté 30 éléments à un champ de fichier, en ajoutant et en téléchargeant 1 à la fois, et le temps total pour le téléchargement + la réponse du serveur + l'insertion javascript du nouvel élément est passé de 414 millisecondes, augmentant à chaque téléchargement successif de 0 à 20 millisecondes, se terminant à 800 millisecondes pour le voyage numéro 30.

  • J'ai cliqué sur "Ajouter un autre élément" pour un champ de texte illimité 100 fois, et le temps total est passé de 337 millisecondes à 1,3 seconde. Si mon formulaire était plus complexe, ces chiffres ne feraient qu'augmenter.

En $form_state['fields']['your_field_name']['und']existe une propriété appelée items_count. Ceci est utilisé pour calculer le nombre de widgets de champ qui doivent être affichés pour un champ donné. Je vous recommande d'utiliser hook_field_attach_form()pour modifier le widget $form_state avant la construction du champ et définir la items_countpropriété du champ pour être un plus grand nombre, vous donnant ainsi le nombre de champs dont vous avez besoin immédiatement. L'utilisateur pourra toujours ajouter d'autres éléments. Ce serait à vous de trouver une meilleure façon de cacher les éléments supplémentaires de faire le formulaire de 10 pages; peut-être qu'un div avec overflow: scroll;pourrait fonctionner. Quoi qu'il en soit, cela peut être un point de départ pour vous de trouver quelque chose qui permet à votre flux de travail d'aller plus vite:

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

Modifier: il manque une certaine logique à l'exemple de code pour garantir qu'il ne s'exécute que pour le formulaire approprié et ne vous permettra pas d'ajouter un autre élément. Je réviserai ceci quand j'aurai un meilleur exemple de travail localement.

Charlie Schliesser
la source
Bonjour Charlie, j'ai pensé à l'astuce que vous avez décrite aussi, mais les choses empirent lorsque votre utilisateur veut réorganiser les champs (dans mon cas, c'est une exigence vitale). Lorsque vous essayez de réorganiser l'un des 100 champs via drag'n'drop, le navigateur se bloque pour toujours ...
Timur Kamanin
Est-ce que cela se bloque uniquement lors de la réorganisation des champs de fichier ou des champs de texte? Cela semble étrange, car draggable.js ne devrait pas renvoyer quoi que ce soit au serveur, mais simplement écouter les changements de ligne, puis mettre à jour les champs de saisie masqués. De plus, dans quel navigateur et quelle version rencontrez-vous le blocage? Je pense que tout ce que nous découvrons ici pourrait être utile à de nombreux autres utilisateurs.
Charlie Schliesser
Oui, si vous avez un cas d'utilisation reproductible de la suspension de draggable.js qui appelle un problème de base.