Lento "Adicionar outro item" com campos com valor ilimitado

8

No Drupal 7, quando você possui um nó com um campo que possui valores ilimitados (digamos, campo de imagem), o tempo de resposta "adicionar outro item" fica muito lento após a adição de 10 a 20 itens. Como você luta contra esse problema? Você já encontrou esse problema?

Eu criei um projeto, onde o usuário pode adicionar até 100 valores de um campo de imagem que, em teoria, possui valores ilimitados. Porém, depois de adicionar uma dúzia de imagens, cada novo clique em "Adicionar outro item" fica mais lento que o anterior. Sei que isso acontece porque o Drupal reconstrói esse campo e todos os seus valores após cada solicitação de ajax, portanto, quanto mais valores você adicionar, mais trabalho o Drupal terá que fazer em cada solicitação de "ajax", mas, na verdade, isso não é coisa muito boa.

Existem abordagens sobre como alterar / substituir esse comportamento?

Timur Kamanin
fonte

Respostas:

3

com base na resposta de Charlie, descobri que leva aproximadamente a mesma quantidade de tempo para recarregar o bloco se você estiver adicionando 1 ou 100 itens. Portanto, aqui está um truque para adicionar uma lista selecionada de números no formulário ao lado de 'add mais "para que você possa escolher quantos está adicionando. Isso economiza muito tempo e ainda é flexível. Pode ser envolvido em um pequeno 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;
  }
}
?>

Também postei a sugestão no Drupal.org em https://drupal.org/node/1394184#comment-8252701, onde o op teve um problema semelhante.

jowan sebastian
fonte
Eu adaptei o código acima para um campo personalizado com cardinalidade ilimitada e funcionou bem para mim. A única alteração que fiz na lógica principal foi subtrair 1 de $ numbtoadd antes de usá-la. Eu acho que isso ocorre porque items_count está sub-representado, pois é baseado em zero?
Dave Bruns
2

É uma reação à natureza da API do formulário e à forma como ela torna a totalidade $forme a $form_statedisponibilidade novamente no servidor. Isso é legal por várias razões, embora eu concorde que possa ser bastante irritante do ponto de vista de desempenho. Algumas estatísticas em um servidor Ubuntu 12.04 executando o Apache2 com PHP-FPM:

  • Adicionei 30 itens a um campo de arquivo, adicionando e fazendo upload de 1 por vez, e o tempo total para o upload + resposta do servidor + inserção de javascript do novo elemento passou de 414 milissegundos, aumentando em cada upload sucessivo de 0 a 20 milissegundos, terminando em 800 milissegundos para o número da viagem 30.

  • Cliquei em "Adicionar outro item" para um campo de texto ilimitado 100 vezes e o tempo total passou de 337 milissegundos a 1,3 segundos. Se meu formulário fosse mais complexo, esses números apenas aumentariam.

Em $form_state['fields']['your_field_name']['und']existe uma propriedade chamada items_count. Isso é usado para calcular o número de widgets de campo que devem ser exibidos para um determinado campo. Eu recomendo que você use hook_field_attach_form()para alterar o $form_state antes que o widget do campo seja construído e defina a items_countpropriedade do campo como um número maior, fornecendo o número de campos necessários imediatamente. O usuário ainda poderá adicionar mais itens. Depende de você encontrar uma maneira melhor de ocultar os itens extras, deixando o formulário com 10 páginas; talvez um div com overflow: scroll;poderia funcionar. De qualquer forma, este pode ser um ponto de partida para você encontrar algo que permita que seu fluxo de trabalho seja mais 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: o código de exemplo está faltando alguma lógica para garantir que ele seja executado apenas no formulário apropriado e não permita que você 'adicione outro item'. Vou revisar isso quando tiver um exemplo de trabalho melhor localmente.

Charlie Schliesser
fonte
Olá Charlie, eu pensei no truque que você descreveu também, mas as coisas pioram quando o usuário deseja reordenar os campos (no meu caso, é um requisito vital). Quando você tenta reordenar um dos 100 campos através drag'n'drop, trava navegador para sempre ...
Timur Kamanin
Ele depende apenas da reordenação de campos de arquivos ou de texto também? Isso parece estranho, pois o draggable.js não deve enviar nada de volta ao servidor, apenas ouvindo as alterações de linha e atualizando os campos de entrada ocultos. Além disso, em qual navegador e versão você está enfrentando o problema? Acho que tudo o que descobrimos aqui pode ser útil para muitos outros usuários.
Charlie Schliesser
Sim, se você tiver um caso de uso reproduzível de draggable.js suspenso, isso significa um problema principal.