web-dev-qa-db-fra.com

Comment remplacer Views PHP et tri par gestionnaire de vues personnalisé?

Afin de résoudre certains problèmes de performances de Views et de respecter les meilleures pratiques, j'aimerais remplacer certains Views PHP que j'ai configurés il y a quelque temps par mes propres gestionnaires personnalisés.

Par exemple, j'ai un Views PHP, exclu de l'affichage, avec cette configuration:

Code de valeur:

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

Code de sortie :

<?php print $value ; ?>`

Ensuite, j'utilise ce champ comme premier critère de tri ( ascendant), dans un Global PHP critère de tri:

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

Je serais vraiment reconnaissant si vous pouviez me mettre sur la bonne voie: dans quelle (s) fonction (s) devrais-je construire ce même code pour finir avec PHP dans la base de données?

Sommaire :

Après la recherche et la progression, plus l'aide de @Renrahf, la plupart de la mise en œuvre semble correcte, détaillée ci-dessous. Mais je me bat toujours avec un point : j'ai ajouté un gestionnaire de champs personnalisé pour calculer une valeur, mais comment puis-je commander par ce gestionnaire?

Modifications:

Ce que j'ai fait jusqu'à présent:

. fichier info

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

Fichier de module

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

Fichier views_handler_vts_products_sort

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

Fichier views_handler_vts_count_depconf_field

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

Question restante:

  • comment commander par le gestionnaire de champs personnalisé? J'ai essayé d'ajouter 'click sortable'=> TRUE, OR 'sort' => array('handler' => 'views_handler_sort',), OR $this->query->add_orderby('custom', 'count_depconf_field', 'DESC'); dans le gestionnaire de tri personnalisé. Rien ne fonctionne mais retourne une colonne inconnue dans la 'clause de commande'

  • [~ # ~] terminé [~ # ~]: Comment puis-je obtenir $row->product_id Et $row->nid Dans query()? J'en ai besoin pour construire la sous-requête.: Ajout d'un champ de gestionnaire de vues et trouvé les valeurs de ligne dans render ($ values) ...

  • [~ # ~] done [~ # ~]: Quelle partie de l'exemple de gestionnaire faire Je dois éditer? La fonction de requête uniquement? Dois-je conserver l'exemple de code entier ou seulement les parties personnalisées?

Je vous remercie

11
Kojo

Vous devez utiliser un gestionnaire de tri des vues: https://api.drupal.org/api/views/handlers!views_handler_sort.inc/group/views_sort_handlers/7.x-3.x

Vous ne pouvez pas utiliser PHP pour trier vos résultats pour des raisons de performances. PHP ne peut être utilisé pour trier les résultats que si vous récupérez l'intégralité des résultats du tableau, et c'est pas une option la plupart du temps.

Ainsi, vous devez créer votre propre gestionnaire de tri de vues, le configurer dans votre vue, puis utiliser les fonctions de l'API de vues pour créer les jointures appropriées, où, peut-être même des sous-requêtes pour atteindre les données dont vous avez besoin pour votre tri. Dans votre cas, un certain nombre d'entités ayant des conditions de date et de type particulières.

Tout ce code doit résider dans la méthode "query ()" de votre objet. Vous devez réaliser une requête comme celle-ci:

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

En utilisant la fonction https://api.drupal.org/api/views/plugins%21views_plugin_query_default.inc/function/views_plugin_query_default%3A%3Aadd_orderby/7.x-3.x et une sous-requête.

La sous-requête peut être optimisée en 3 articulations ou plus et certaines où les conditions peuvent être, mais je ne peux pas le dire sans toute la requête.

[~ # ~] modifier [~ # ~]

Vous vous étendez à partir de l'objet "views_handler" mais vous devez directement étendre à partir de "views_handler_sort" pour pouvoir utiliser le maximum de code par défaut de 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']);
  }
}

Comme vous pouvez le voir ci-dessus, seule la méthode "query" est nécessaire dans votre cas car vous n'avez pas besoin de configurations spécifiques dans l'interface utilisateur, etc.

Pour obtenir le product_id ou nid dans votre méthode "query ()" , vous devez utiliser les champs existants qui ont été ajoutés à la requête par les gestionnaires de champs de vues (et définis dans l'interface utilisateur de vos vues).

Ce fichier est l'exemple parfait de ce que vous voulez réaliser (vous pouvez le trouver dans la documentation des vues, c'est un fichier existant mais je ne suis pas autorisé à définir le lien car ma réputation est trop faible):

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

Voyez si vous pouvez adapter ce code à vos besoins et je serai heureux de voir le résultat final :)

7
Renrhaf

Je partage ci-dessous l'implémentation complète sur la façon dont j'ai fait pour remplacer Views PHP tri par un gestionnaire de vues personnalisé.

fichier .info

files[] = includes/views_handler_my_custom_sort.inc

fichier module

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

fichier 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 peu d'explication: après avoir compris comment implémenter les gestionnaires de vues, je me suis confondu avec la sous-requête:

  • mappez-le avec la requête externe pour obtenir un résultat dynamique "ligne par": même table et colonne mais alias différent: WHERE nod.nid = node.nid
  • définissez l'alias dans add_orderby: $this->query->add_orderby(NULL, $sub_query, 'DESC', 'subquery'); fonctionne, mais $this->query->add_orderby(NULL, $sub_query, 'DESC'); ne fonctionne pas

Ce dernier point était surprenant car si 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 ) fonctionne en entrée directe SQL, il ne l'est pas dans la configuration actuelle.

Vous devez spécifier l'alias de sous-requête et la requête finale sera quelque chose comme 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

Le tente de calculer les valeurs pour trier le résultat dans un champ de gestionnaire personnalisé, n'a pas fonctionné car le tri des vues est effectué sur une base de données et le gestionnaire de champ personnalisé est une sorte de champ fictif ... du moins, c'était ma conclusion.

4
Kojo