Come sostituire il campo PHP Views e l'ordinamento tramite il gestore Views personalizzato?


11

Per risolvere alcuni problemi di prestazioni di Views e rispettare le migliori pratiche, vorrei sostituire alcuni PHP di Views configurati qualche tempo fa dai miei gestori personalizzati .

Ad esempio ho un campo PHP Views, escluso dalla visualizzazione , con quella configurazione:

Codice valore:

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

Codice di uscita :

<?php print $value ; ?>`

Quindi uso quel campo come primo criterio di ordinamento ( crescente ), in un criterio di ordinamento PHP globale:

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

Sarei davvero grato se potessi mettermi sulla buona strada: in quale funzione dovrei costruire lo stesso codice per finire con PHP nel database?

Sommario :

Dopo la ricerca e i progressi, oltre all'aiuto di @Renrahf, la maggior parte dell'implementazione sembra ok, dettagliata di seguito. Ma sto ancora combattendo con un punto : ho aggiunto un gestore di campo personalizzato per calcolare un valore, ma come posso ordinare da quel gestore?

Modifiche:

Quello che ho fatto finora:

file .info

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

File del modulo

/**
 * 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 file

/**
 * 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 file

/*
 * 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;
  }
}

Domanda rimanente:

  • come ordinare dal gestore campo personalizzato? Ho provato ad aggiungere 'click sortable'=> TRUE,OR 'sort' => array('handler' => 'views_handler_sort',),OR $this->query->add_orderby('custom', 'count_depconf_field', 'DESC');nel gestore di ordinamento personalizzato. Nessuno funziona ma restituisce la colonna sconosciuta nella "clausola d'ordine"

  • FATTO : Come posso entrare $row->product_ide $row->niddentro query()? Ne ho bisogno per costruire la subquery. : Aggiunto un campo gestore viste e trovato i valori di riga nel rendering ($ valori) ...

  • FATTO : Quale parte del gestore di esempio devo modificare? Solo la funzione query? Devo conservare l'intero codice di esempio o solo le parti personalizzate?

Grazie

Risposte:


7

È necessario utilizzare un gestore di ordinamento delle visualizzazioni: https://api.drupal.org/api/views/handlers!views_handler_sort.inc/group/views_sort_handlers/7.x-3.x

Non è possibile utilizzare PHP per ordinare i risultati per motivi di prestazioni. PHP può essere utilizzato per ordinare i risultati solo se recuperi i risultati dell'intera tabella e questa non è un'opzione per la maggior parte del tempo.

Quindi, è necessario creare il proprio gestore dell'ordinamento della vista, configurarlo nella vista e quindi utilizzare le funzioni dell'API delle viste per creare i giusti join, dove, forse, anche le subquery per raggiungere i dati necessari per il proprio ordinamento. Nel tuo caso, un numero di entità con particolari condizioni di data e tipo.

Tutto questo codice deve risiedere nel metodo "query ()" dell'oggetto. Devi ottenere una query come questa:

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())

Utilizzando la funzione https://api.drupal.org/api/views/plugins%21views_plugin_query_default.inc/function/views_plugin_query_default%3A%3Aadd_orderby/7.x-3.xe una subquery.

La sottoquery può essere ottimizzata in 3 o più giunti e in alcuni casi in cui le condizioni possono ma non posso dirlo senza l'intera query.

MODIFICARE

Si estende dall'oggetto "views_handler" ma si dovrebbe estendere direttamente da "views_handler_sort" per poter utilizzare il massimo del codice predefinito di base:

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']);
  }
}

Come puoi vedere sopra, nel tuo caso è necessario solo il metodo "query" poiché non hai bisogno di configurazioni specifiche nell'interfaccia utente, ecc.

Per ottenere product_id o nid all'interno del metodo "query ()", è necessario utilizzare i campi esistenti che sono stati aggiunti alla query dai gestori dei campi delle viste (e definiti nell'interfaccia utente delle viste).

Questo file è l'esempio perfetto di ciò che vuoi ottenere (puoi trovarlo nella documentazione delle viste, è già presente ma non mi è permesso impostare il collegamento perché la mia reputazione è troppo bassa):

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');
  }
}

Vedi se riesci ad adattare questo codice alle tue necessità e sarò felice di vedere il risultato finale :)


Ho modificato la mia domanda con progressi. Se desideri completare la tua risposta? Grazie mille
Kojo,

1
Fatto, dai un'occhiata e dimmi se sei riuscito a far funzionare il tuo tipo :)
Renrhaf,

Mio male, mi sono reso conto che poiché le viste si basano su query DB per l'ordinamento, sembra che non riuscirò mai a ottenere un ordinamento di viste con un campo fittizio come quello creato dal gestore! Quindi devo assolutamente lavorare sulla subquery!
Kojo,

1
Ci scusiamo per non aver reagito prima! Spero che la mia risposta sia stata utile, ma tu hai fatto tutto il lavoro da solo, non sapevo della cosa di alias di subquery, grazie per la tua soluzione dettagliata, aiuterà molte persone.
Renrhaf,

4

Condivido di seguito l'implementazione completa su come ho fatto per sostituire l'ordinamento Views PHP con un gestore Views personalizzato .

file .info

files[] = includes/views_handler_my_custom_sort.inc

File del modulo

/**
 * 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'),
  );
}

file 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');

  }
}

Un po 'di spiegazione: dopo aver capito come implementare i gestori di Views, mi sono confuso con la subquery:

  • mappalo con la query esterna per ottenere un risultato "riga per" dinamico: stessa tabella e colonna ma alias diverso: WHERE nod.nid = node.nid
  • imposta l'alias in add_orderby: $this->query->add_orderby(NULL, $sub_query, 'DESC', 'subquery');funziona, ma $this->query->add_orderby(NULL, $sub_query, 'DESC');non lo fa

Quest'ultimo punto è stato sorprendente perché mentre 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 )funziona con l'input diretto SQL, non è presente nell'impostazione corrente.

Devi specificare l'alias di subquery e la query finale sarà simile 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

I tentativi di calcolare i valori per ordinare il risultato in un campo del gestore personalizzato, non hanno funzionato perché l'ordinamento delle Viste viene eseguito su base DB e il gestore del campo personalizzato è una specie di campo fittizio ... almeno questa è stata la mia conclusione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.