Como substituir o campo Views PHP e classificar pelo manipulador Views personalizado?

11

Para resolver alguns problemas de desempenho do Views e respeitar as melhores práticas, gostaria de substituir alguns do PHP que configurei há algum tempo pelos meus próprios manipuladores personalizados .

Por exemplo, eu tenho um campo PHP do Views, excluído da exibição , com essa configuração:

Código do valor:

if( $row->sticky ==1 ) {
  return 100;
} else {

  if ( isset($row->product_id) && $row->product_id != ""  ){

    $query = "SELECT COUNT(statut.entity_id) FROM field_data_field_statut_depart statut"
    . " INNER JOIN  field_data_field_product product ON statut.entity_id= product.field_product_product_id"
    . " INNER JOIN  field_data_field_date_depart depart ON statut.entity_id = depart.entity_id"
    . " WHERE product.entity_id = ". $row->nid." AND field_statut_depart_value IN (2,3) AND field_date_depart_value > NOW(); ";

    $select = db_query($query);
    $count = $select->fetchField();

    return $count; 
  }
  else {
    return -1;
  }
}

Código de saída :

<?php print $value ; ?>`

Então eu uso esse campo como primeiro critério de classificação ( crescente ), em um critério de classificação Global do PHP:

if ($row1->php> $row2->php) return -1; else return 1;

Ficaria muito grato se você pudesse me colocar no caminho certo: em quais funções devo criar o mesmo código para acabar com o PHP no banco de dados?

Resumo:

Após a pesquisa e o progresso, além da ajuda do @Renrahf, a maior parte da implementação parece correta, detalhados abaixo. Mas ainda estou lutando com um ponto : adicionei um manipulador de campo personalizado para calcular um valor, mas como posso fazer o pedido por esse manipulador?

Editar% s :

O que eu fiz até agora:

arquivo .info

files[] = views_handler_vts_products_sort.inc
files[] = includes/views_handler_vts_count_depconf_field.inc

Arquivo de módulo

/**
 * Implements hook_views_data().
 */
function vts_views_handler_views_data() {
  $data['custom']['table']['group'] = t('Custom');
  $data['custom']['table']['join'] = array(
    // #global is a special flag which let's a table appear all the time.
    '#global' => array(),
  );

  $data['custom']['custom_handler'] = array(
    'title' => t('Vts custom Sort Handler'),
    'help' => 'Sorts products by sticky first then by custom statut field',
    'sort' => array(
      'handler' => 'views_handler_vts_products_sort',
    ),
  );

  $data['custom']['count_depconf_field'] = array(
    'title' => t('Sum of products with status confirmed '),
    'help' => t('Calculate Sum of products with status confirmed, to order lists".'),
    'field' => array(
      'handler' => 'views_handler_vts_count_depconf_field',
      'click sortable'=> TRUE,
    ),
    /*'sort' => array(
      'handler' => 'views_handler_sort',
    ), */
  );  
  return $data;
}

function vts_views_handler_views_api() {
    return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'vts_views_handler'),
  );
}

views_handler_vts_products_sort Arquivo

/**
 * Base sort handler that has no options and performs a simple sort.
 *
 * @ingroup views_sort_handlers
 */
class views_handler_vts_products_sort extends views_handler_sort {

  function query() {
    $this->ensure_my_table();
    // Add the field.
    $this->query->add_orderby('node', 'sticky', 'DESC');
  }
}

views_handler_vts_count_depconf_field Arquivo

/*
 * A simple field to calculate the value I wish to order by.
 */
class views_handler_vts_count_depconf_field extends views_handler_field {

  function query() {
    //do nothing
  }

  function render($values) {
    $count = 0;

    $product_id = isset($values-> commerce_product_field_data_field_product_product_id)? $values-> commerce_product_field_data_field_product_product_id: NULL;
    if(!is_null($product_id)){

      $query = "SELECT COUNT(statut.entity_id) FROM field_data_field_statut_depart statut"
      . " INNER JOIN  field_data_field_product product ON statut.entity_id= product.field_product_product_id"
      . " INNER JOIN  field_data_field_date_depart depart ON statut.entity_id = depart.entity_id"
      . " WHERE product.entity_id = " . $values->nid . " AND field_statut_depart_value IN (2,3) AND field_date_depart_value > NOW(); ";

      $select = db_query($query);
      $count = $select->fetchField();
    }
    return $count;
  }
}

Pergunta restante:

  • como encomendar pelo manipulador de campo personalizado? Tentei adicionar 'click sortable'=> TRUE,OR 'sort' => array('handler' => 'views_handler_sort',),OR $this->query->add_orderby('custom', 'count_depconf_field', 'DESC');no manipulador de classificação personalizado. Nenhuma funciona, mas retorna a coluna Desconhecida na 'cláusula de ordem'

  • FEITO : Como posso entrar $row->product_ide $row->nidentrar query()? Eu preciso dele para construir a subconsulta. : Adicionado um campo de manipulador de visualizações e encontrou os valores da linha em render ($ values) ...

  • CONCLUÍDO : Qual parte do manipulador de exemplo eu tenho que editar? A função de consulta apenas? Preciso manter todo o código de exemplo ou apenas as peças personalizadas?

Obrigado

Kojo
fonte

Respostas:

7

Você precisa usar um manipulador de classificação de visualizações: https://api.drupal.org/api/views/handlers!views_handler_sort.inc/group/views_sort_handlers/7.x-3.x

Você não pode usar o PHP para classificar seus resultados por razões de desempenho. O PHP só pode ser usado para classificar resultados se você buscar todos os resultados da tabela, e isso não é uma opção na maioria das vezes.

Portanto, você precisa criar seu próprio manipulador de classificação de exibição, configurá-lo em sua exibição e, em seguida, usar as funções da API de exibições para fazer as junções apropriadas, onde, talvez até subconsultas, para alcançar os dados necessários para sua classificação. No seu caso, várias entidades com condições específicas de data e tipo.

Todo esse código deve residir no método "query ()" do seu objeto. Você precisa obter uma consulta como esta:

SELECT table_x.field_y, ...
FROM  ...
...
...
ORDER BY row.sticky, (SELECT COUNT(statut.entity_id) 
FROM field_data_field_statut_depart statut
INNER JOIN field_data_field_product product
INNER JOIN field_data_field_date_depart depart
WHERE product.entity_id = table_x.field_y
AND field_statut_depart_value IN (2,3) 
AND field_date_depart_value > NOW())

Usando a função https://api.drupal.org/api/views/plugins%21views_plugin_query_default.inc/function/views_plugin_query_default%3A%3Aadd_orderby/7.x-3.xe uma subconsulta.

A subconsulta pode ser otimizada em 3 ou mais articulações e em algumas condições onde talvez, mas não posso dizer sem toda a consulta.

EDITAR

Você se estende do objeto "views_handler", mas deve se estender diretamente de "views_handler_sort" para poder usar o máximo do código padrão principal:

class views_handler_vts_products_sort extends views_handler_sort {
  /**
   * Called to add the sort to a query.
   */
  function query() {
    $this->ensure_my_table();
    // Add the field.
    $this->query->add_orderby($this->table_alias, $this->real_field, $this->options['order']);
  }
}

Como você pode ver acima, apenas o método "query" é necessário no seu caso, pois você não precisa de configurações específicas na interface do usuário etc.

Para obter o product_id ou nid dentro do método "query ()", você deve usar os campos existentes que foram adicionados à consulta pelos manipuladores de campo das visualizações (e definidos na interface do usuário das visualizações).

Este arquivo é o exemplo perfeito do que você deseja obter (você pode encontrá-lo na documentação das visualizações, é um existente, mas não tenho permissão para definir o link, pois minha reputação é muito baixa):

class views_handler_sort_node_version_count extends views_handler_sort {
  function query() {
    $this->ensure_my_table();

    $this->query->add_orderby(NULL, '(SELECT COUNT(vid) FROM {node_revision} WHERE nid = {' . $this->table_alias . '}.nid)', $this->options['order'], 'sort_node_version_count');
  }
}

Veja se você pode adaptar esse código à sua necessidade e ficarei feliz em ver o resultado final :)

Renrhaf
fonte
Editei minha pergunta com progresso. Se você deseja completar sua resposta? Muito obrigado
Kojo
1
Feito, por favor verificá-la e me diga se você conseguiu obter o seu trabalho tipo :)
Renrhaf
Meu mal, percebi que, como as visualizações dependem da consulta ao banco de dados para classificar, parece que eu nunca alcançarei uma classificação de visualizações com um campo fictício como o criado pelo manipulador! Então eu definitivamente tenho que trabalhar na subconsulta!
Kojo27
1
Desculpe por não reagir antes! Espero que minha resposta tenha sido útil, mas você fez todo o trabalho por conta própria, eu não sabia sobre o alias da subconsulta, obrigado por sua solução detalhada, isso ajudará muitas pessoas.
Renrhaf 31/07
4

Compartilho abaixo a implementação completa de como substituí a classificação do Views PHP por um manipulador de Views personalizado .

arquivo .info

files[] = includes/views_handler_my_custom_sort.inc

Arquivo de módulo

/**
 * Implements hook_views_data().
 */
function MODULE_NAME_views_data() {
  $data['custom']['table']['group'] = t('Custom');
  $data['custom']['table']['join'] = array(
    '#global' => array(),
  );

  $data['custom']['custom_handler'] = array(
    'title' => t('My custom Sort Handler'),
    'help' => 'Sorts products by sticky first then by custom statut field',
    'sort' => array(
      'handler' => 'views_handler_vts_products_sort',
    ),
  );

  return $data;
}

function MODULE_NAME_views_api() {
    return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'MODULE_NAME'),
  );
}

Arquivo views_handler_my_custom_sort.inc

/**
 * Base sort handler that has no options and performs a simple sort.
 *
 * @ingroup views_sort_handlers
 */
class views_handler_my_custom_sort extends views_handler_sort {

  function query() {
    $this->ensure_my_table();

    $sub_query = "(SELECT COUNT(p.field_product_product_id) "
      . "FROM field_data_field_product p "
      . "LEFT JOIN field_data_field_statut_depart statut ON statut.entity_id = p.field_product_product_id "
      . "LEFT JOIN field_data_field_date_depart depart ON depart.entity_id = p.field_product_product_id  "
      . "LEFT JOIN node nod ON nod.nid = p.entity_id "
      . "WHERE nod.nid = node.nid "//This is a the obligatory condition mapping the subquery with the outer query
      . "AND field_statut_depart_value IN (2,3) "
      . "AND field_date_depart_value > NOW())";

    /* I'm timeless to write the query with the object syntax, here was a beginning
    $sub_query = db_select('field_data_field_product', 'p');
    $sub_query->addField('p', 'field_product_product_id');
    $sub_query->leftJoin('node', 'nod', 'nod.nid = p.entity_id');
    $sub_query->where("nod.nid = node.nid");
    $sub_query->countQuery(); */  

    $this->query->add_orderby('node', 'sticky', 'DESC');
    $this->query->add_orderby(NULL, $sub_query, 'DESC', 'subquery');

  }
}

Um pouco de explicação: depois de entender como implementar os manipuladores de Views, fiquei confuso com a subconsulta:

  • mapeie-o com a consulta externa para obter um resultado dinâmico "linha por": mesma tabela e coluna, mas alias diferente: WHERE nod.nid = node.nid
  • defina o alias em add_orderby: $this->query->add_orderby(NULL, $sub_query, 'DESC', 'subquery');funciona, mas $this->query->add_orderby(NULL, $sub_query, 'DESC');não

Esse último ponto foi surpreendente, porque, embora SELECT TITLE FROM node ORDER BY (SELECT COUNT(field_product_product_id) FROM field_data_field_product p LEFT JOIN node nod ON nod.nid = p.entity_id WHERE nod.nid = node.nid )trabalhe na entrada direta do SQL, não ocorre na configuração atual.

Você precisa especificar o alias da subconsulta e a consulta final será algo como SELECT TITLE, (SELECT COUNT(field_product_product_id) FROM field_data_field_product p LEFT JOIN node nod ON nod.nid = p.entity_id WHERE nod.nid = node.nid ) as subquery FROM node ORDER BY subquery

As tentativas de calcular os valores para classificar o resultado em um campo de manipulador personalizado não funcionaram porque a classificação de Views é feita com base no banco de dados e o manipulador de campo personalizado é um tipo de campo fictício ... pelo menos essa foi minha conclusão.

Kojo
fonte