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

11

Afin de résoudre certains problèmes de performances de Views et de respecter les meilleures pratiques, je voudrais 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 champ Vues 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 ( croissant ), dans un critère de tri Global PHP:

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?

Résumé :

Après la recherche et la progression, ainsi que l'aide de @Renrahf, la plupart de la mise en œuvre semble correcte, détaillée ci-dessous. Mais je me bats 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'),
  );
}

views_handler_vts_products_sort fichier

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

/*
 * 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,OU 'sort' => array('handler' => 'views_handler_sort',),OU $this->query->add_orderby('custom', 'count_depconf_field', 'DESC');dans un gestionnaire de tri personnalisé. Aucun ne fonctionne mais retourne une colonne inconnue dans la 'clause de commande'

  • FAIT : Comment puis - je obtenir $row->product_idet à l' $row->nidintérieur query()? J'en ai besoin pour construire la sous-requête. : Ajout d'un champ de gestionnaire de vues et recherche des valeurs de ligne dans le rendu (valeurs $) ...

  • Terminé : quelle partie de l'exemple de gestionnaire dois-je modifier? La fonction de requête uniquement? Dois-je conserver l'intégralité du code d'exemple ou uniquement les parties personnalisées?

Je vous remercie

Kojo
la source

Réponses:

7

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 ce n'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.

ÉDITER

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 votre interface utilisateur de 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 :)

Renrhaf
la source
J'ai édité ma question avec le progrès. Si vous souhaitez compléter votre réponse? Merci beaucoup
Kojo
1
Terminé, veuillez vérifier et dites-moi si vous avez réussi à faire fonctionner votre tri :)
Renrhaf
Mon mauvais, j'ai réalisé que puisque les vues dépendent de la requête DB pour trier, il semble que je ne parviendrai jamais à trier les vues avec un champ factice comme celui créé par le gestionnaire! Je dois donc définitivement travailler sur la sous-requête!
Kojo
1
Désolé de ne pas avoir réagi plus tôt! J'espère que ma réponse a été utile, mais vous avez fait tout le travail vous-même, je ne connaissais pas l'alias de sous-requête, merci pour votre solution détaillée, cela aidera beaucoup de gens.
Renrhaf
4

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

Fichier .info

files[] = includes/views_handler_my_custom_sort.inc

Fichier de 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 bien qu'il 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 factice ... du moins, c'était ma conclusion.

Kojo
la source