¿Cómo reemplazar el campo PHP de Vistas y ordenarlo por el controlador de Vistas personalizado?

11

Para abordar algunos problemas de rendimiento de Vistas y respetar las mejores prácticas, me gustaría reemplazar algunas Vistas PHP que configuré hace algún tiempo por mis propios controladores personalizados .

Por ejemplo, tengo un campo PHP Vistas, excluido de la pantalla , con esa configuración:

Código de 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 salida :

<?php print $value ; ?>`

Luego uso ese campo como primer criterio de clasificación ( ascendente ), en un criterio de clasificación de PHP Global:

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

Estaría realmente agradecido si pudiera ponerme en el camino correcto: ¿en qué función (es) construiré ese mismo código para terminar con PHP en la base de datos?

Resumen :

Después de la búsqueda y el progreso, más la ayuda de @Renrahf, la mayor parte de la implementación parece correcta, detallada a continuación. Pero todavía estoy luchando con un punto : agregué un controlador de campo personalizado para calcular un valor, pero ¿cómo puedo ordenar por ese controlador?

Ediciones:

Lo que hice hasta ahora:

archivo .info

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

Archivo 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 archivo

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

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

Pregunta restante:

  • ¿Cómo ordenar por el controlador de campo personalizado? Intenté agregar 'click sortable'=> TRUE,OR 'sort' => array('handler' => 'views_handler_sort',),OR $this->query->add_orderby('custom', 'count_depconf_field', 'DESC');en el controlador de clasificación personalizado. Ninguno funciona, pero devuelve la columna Desconocido en 'cláusula de pedido'

  • HECHO : ¿Cómo puedo entrar $row->product_idy $row->nidentrar query()? Lo necesito para construir la subconsulta. : Se agregó un campo de controlador de vistas y se encontraron los valores de fila en render ($ valores) ...

  • HECHO : ¿Qué parte del controlador de ejemplo debo editar? La función de consulta solamente? ¿Necesito mantener todo el código de ejemplo o solo las partes personalizadas?

Gracias

Kojo
fuente

Respuestas:

7

Debe utilizar un controlador de clasificación de vistas: https://api.drupal.org/api/views/handlers!views_handler_sort.inc/group/views_sort_handlers/7.x-3.x

No puede usar PHP para ordenar sus resultados por razones de rendimiento. PHP solo se puede usar para ordenar resultados si obtiene los resultados completos de la tabla, y esa no es una opción la mayor parte del tiempo.

Por lo tanto, debe crear su propio controlador de clasificación de vistas, configurarlo en su vista y luego usar las funciones de API de vistas para hacer las uniones adecuadas, donde, tal vez incluso subconsultas para llegar a los datos que necesita para su clasificación. En su caso, una cantidad de entidades que tienen condiciones particulares de fecha y tipo.

Todo este código tiene que residir en el método "query ()" de su objeto. Necesita realizar una 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())

Mediante el uso de la función https://api.drupal.org/api/views/plugins%21views_plugin_query_default.inc/function/views_plugin_query_default%3A%3Aadd_orderby/7.x-3.x y una subconsulta.

La subconsulta puede optimizarse en 3 o más uniones y en algunas condiciones, pero no puedo decirlo sin toda la consulta.

EDITAR

Se extiende desde el objeto "views_handler", pero debe extenderse directamente desde "views_handler_sort" para poder utilizar el máximo de código predeterminado central:

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 puede ver arriba, solo se necesita el método de "consulta" en su caso, ya que no necesita ninguna configuración específica en la interfaz de usuario, etc.

Para obtener el product_id o nid dentro de su método "query ()", debe usar los campos existentes que fueron agregados a la consulta por los manejadores de campos de vistas (y definidos en su interfaz de usuario de vistas).

Este archivo es el ejemplo perfecto de lo que desea lograr (puede encontrarlo en la documentación de vistas, es uno existente pero no se me permite establecer el enlace ya que mi reputación es demasiado baja):

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

Vea si puede adaptar este código a su necesidad y me alegrará ver el resultado final :)

Renrhaf
fuente
Edité mi pregunta con progreso. Si desea completar su respuesta? Muchas gracias
Kojo
1
Listo, échale un vistazo y dime si
lograste
Lo malo es que me di cuenta de que, dado que las vistas se basan en la consulta de base de datos para ordenar, ¡parece que nunca lograré una vista ordenada con un campo ficticio como el creado por el controlador! ¡Definitivamente tengo que trabajar en la subconsulta!
Kojo
1
Perdón por no reaccionar antes! Espero que mi respuesta haya sido útil, pero usted hizo todo el trabajo usted mismo, no sabía sobre el alias de subconsulta, gracias por su solución detallada, ayudará a mucha gente.
Renrhaf
4

Comparto a continuación la implementación completa sobre cómo reemplacé la ordenación de Vistas PHP por un controlador de Vistas personalizado .

archivo .info

files[] = includes/views_handler_my_custom_sort.inc

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

archivo 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 poco de explicación: después de comprender cómo implementar los manejadores de Vistas, me confundí con la subconsulta:

  • asignarlo con la consulta externa para obtener un resultado dinámico "fila por": misma tabla y columna pero diferente alias: WHERE nod.nid = node.nid
  • establecer el alias en add_orderby: $this->query->add_orderby(NULL, $sub_query, 'DESC', 'subquery');funciona, pero $this->query->add_orderby(NULL, $sub_query, 'DESC');no

Este último punto fue sorprendente porque, aunque 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 )funciona en la entrada directa de SQL, no lo hace en la configuración actual.

Debe especificar el alias de subconsulta y la consulta final será algo así 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

Los intentos de calcular los valores para ordenar el resultado en un campo de controlador personalizado, no funcionaron porque la clasificación de las vistas se realiza en una base de datos y el controlador de campo personalizado es una especie de campo ficticio ... al menos esta fue mi conclusión.

Kojo
fuente