Langsames Hinzufügen eines weiteren Elements mit unbegrenzt wertvollen Feldern

8

Wenn Sie in Drupal 7 einen Knoten mit einem Feld mit unbegrenzten Werten haben (z. B. ein Bildfeld), wird die Antwortzeit "Ein weiteres Element hinzufügen" sehr langsam, nachdem Sie 10 bis 20 Elemente hinzugefügt haben. Wie bekämpfen Sie dieses Problem? Sind Sie jemals auf dieses Problem gestoßen?

Ich habe ein Projekt erstellt, in dem der Benutzer bis zu 100 Werte eines Bildfelds hinzufügen kann, für das theoretisch unbegrenzte Werte festgelegt sind. Nachdem Sie ein Dutzend Bilder hinzugefügt haben, wird jeder neue Klick auf "Ein weiteres Element hinzufügen" langsamer als zuvor. Ich weiß, dass dies aufgrund der Tatsache geschieht, dass Drupal dieses Feld und alle seine Werte nach jeder Ajax-Anforderung neu erstellt. Je mehr Werte Sie hinzugefügt haben, desto mehr Arbeit muss Drupal für jede "Ajax" -Anforderung leisten, dies ist jedoch nicht der Fall ganz tolle sache.

Gibt es Ansätze, wie ein solches Verhalten geändert / außer Kraft gesetzt werden kann?

Timur Kamanin
quelle

Antworten:

3

Aufbauend auf der Antwort von Charlie stellte ich fest, dass das Neuladen des Blocks ungefähr genauso lange dauert, wenn Sie 1 oder 100 Elemente hinzufügen. Hier ist ein Trick, um eine ausgewählte Liste von Zahlen in dem Formular neben 'Hinzufügen' hinzuzufügen mehr ', damit Sie auswählen können, wie viele Sie hinzufügen. Das spart viel Zeit und ist dennoch flexibel. Könnte in ein kleines Modul eingewickelt werden

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

Ich habe den Vorschlag auch auf Drupal.org unter https://drupal.org/node/1394184#comment-8252701 veröffentlicht, wo die Operation ein ähnliches Problem hatte.

Jowan Sebastian
quelle
Ich habe den obigen Code für ein benutzerdefiniertes Feld mit unbegrenzter Kardinalität angepasst und es hat bei mir gut funktioniert. Die einzige Änderung, die ich an der Kernlogik vorgenommen habe, war, 1 von $ numbtoadd zu subtrahieren, bevor ich sie verwende. Ich denke, das liegt daran, dass items_count unterrepräsentiert ist, da es auf Null basiert.
Dave Bruns
2

Es ist ein Rückschlag von der Art der Formular-API und wie sie das gesamte $formund wieder $form_stateverfügbar auf dem Server macht. Dies ist aus vielen Gründen eine coole Sache, obwohl ich der Meinung bin, dass es aus Sicht der Leistung ziemlich ärgerlich sein kann. Einige Statistiken auf einem Ubuntu 12.04-Server, auf dem Apache2 mit PHP-FPM ausgeführt wird:

  • Ich habe einem Dateifeld 30 Elemente hinzugefügt, jeweils 1 hinzugefügt und hochgeladen, und die Gesamtzeit für das Hochladen + Serverantwort + Javascript-Einfügen des neuen Elements betrug 414 Millisekunden und stieg bei jedem aufeinanderfolgenden Upload um 0 bis 20 Millisekunden, die für Reise Nr. 30 bei 800 Millisekunden enden.

  • Ich habe 100 Mal für ein unbegrenztes Textfeld auf "Ein weiteres Element hinzufügen" geklickt, und die Gesamtzeit stieg von 337 Millisekunden auf 1,3 Sekunden. Wenn meine Form komplexer wäre, würden diese Zahlen nur zunehmen.

In $form_state['fields']['your_field_name']['und']existiert eine Eigenschaft namens items_count. Dies wird verwendet, um die Anzahl der Feld-Widgets zu berechnen, die für ein bestimmtes Feld angezeigt werden sollen. Ich würde empfehlen, dass Sie hook_field_attach_form()das ändern, $form_state bevor das Widget des Felds erstellt wird, und die items_countEigenschaft des Felds auf eine größere Anzahl festlegen , sodass Sie sofort die Anzahl der Felder erhalten, die Sie benötigen. Der Benutzer kann weiterhin weitere Elemente hinzufügen. Es liegt an Ihnen, einen besseren Weg zu finden, um die zusätzlichen Elemente davon abzuhalten, das Formular 10 Seiten lang zu machen. Vielleicht könnte ein Div mit overflow: scroll;funktionieren. Auf jeden Fall kann dies ein Ausgangspunkt für Sie sein, um etwas zu finden, mit dem Ihr Workflow schneller abläuft:

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

Bearbeiten: Dem Beispielcode fehlt eine Logik, um sicherzustellen, dass er nur für das entsprechende Formular ausgeführt wird und Sie kein weiteres Element hinzufügen können. Ich werde dies überarbeiten, wenn ich vor Ort ein besseres Arbeitsbeispiel habe.

Charlie Schliesser
quelle
Hallo Charlie, ich habe über den Trick nachgedacht, den du auch beschrieben hast, aber es wird schlimmer, wenn dein Benutzer Felder neu anordnen möchte (in meinem Fall ist dies eine wichtige Voraussetzung). Wenn Sie versuchen, eines von 100 Feldern per Drag'n'Drop neu zu ordnen, bleibt der Browser für immer hängen ...
Timur Kamanin
Hängt es nur an der Neuordnung von Dateifeldern oder Textfeldern? Das scheint seltsam, da draggable.js nichts an den Server zurücksenden sollte, sondern nur auf Zeilenänderungen warten und dann versteckte Eingabefelder aktualisieren sollte. In welchem ​​Browser und in welcher Version tritt der Hang-In auf? Ich denke, alles, was wir hier entdecken, könnte für viele andere Benutzer nützlich sein.
Charlie Schliesser
Ja, wenn Sie einen reproduzierbaren Anwendungsfall von draggable.js haben, der ein Kernproblem erfordert.