Как заменить поля и сортировку Views PHP собственным обработчиком Views?

11

Чтобы решить некоторые проблемы с производительностью Views и соблюсти рекомендации, я бы хотел заменить некоторые PHP Views, которые я настроил некоторое время назад, моими собственными пользовательскими обработчиками .

Например, у меня есть поле Views PHP, исключенное из отображения , с этой настройкой:

Код значения:

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

Код выхода :

<?php print $value ; ?>`

Затем я использую это поле в качестве критерия первой сортировки (по возрастанию ) в критериях сортировки Global PHP:

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

Я был бы очень благодарен, если бы вы могли поставить меня на правильный путь: в каких функциях я должен построить тот же самый код, чтобы в конечном итоге PHP оказался в базе данных?

Резюме :

После поиска и прогресса, а также помощи @Renrahf большая часть реализации выглядит нормально, подробно описано ниже. Но я все еще бьюсь над одним : я добавил собственный обработчик полей для вычисления значения, но как я могу упорядочить этот обработчик?

Редактирует:

Что я сделал до сих пор:

.info файл

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

Файл модуля

/**
 * 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 файл

/**
 * 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 файл

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

Оставшийся вопрос:

  • как сделать заказ обработчиком нестандартного поля? Я попытался добавить 'click sortable'=> TRUE,ИЛИ 'sort' => array('handler' => 'views_handler_sort',),ИЛИ $this->query->add_orderby('custom', 'count_depconf_field', 'DESC');в пользовательский обработчик сортировки. Ничто не работает, но возвращает Неизвестный столбец в «предложении заказа»

  • СОВЕРШЕНО : Как я могу попасть $row->product_idи $row->nidвнутрь query()? Мне нужно, чтобы построить подзапрос. : Добавлено поле обработчика представлений и найдено значение строки в рендере ($ values) ...

  • СОВЕРШЕНО : Какую часть обработчика примера я должен редактировать? Только функция запроса? Нужно ли сохранять весь пример кода или только пользовательские части?

Спасибо

Коджо
источник

Ответы:

7

Вам нужно использовать обработчик сортировки представлений: https://api.drupal.org/api/views/handlers!views_handler_sort.inc/group/views_sort_handlers/7.x-3.x

Вы не можете использовать PHP для сортировки результатов по соображениям производительности. PHP можно использовать только для сортировки результатов, если вы извлекаете результаты из всей таблицы, и в большинстве случаев это невозможно.

Итак, вам нужно создать свой собственный обработчик сортировки представлений, настроить его в своем представлении, а затем использовать функции API представлений, чтобы сделать правильные объединения, где, возможно, даже подзапросы для получения данных, необходимых для вашей сортировки. В вашем случае, ряд лиц, имеющих определенные даты и условия типа.

Весь этот код должен находиться в методе query () вашего объекта. Вам нужно выполнить такой запрос:

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

Используя функцию https://api.drupal.org/api/views/plugins%21views_plugin_query_default.inc/function/views_plugin_query_default%3A%3Aadd_orderby/7.x-3.x и подзапрос.

Подзапрос может быть оптимизирован в 3 или более соединениях, и в некоторых случаях условия могут быть, но я не могу сказать без полного запроса.

РЕДАКТИРОВАТЬ

Вы расширяете от объекта "views_handler", но вы должны напрямую расширяться от "views_handler_sort", чтобы иметь возможность использовать максимум основного кода по умолчанию:

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

Как вы можете видеть выше, в вашем случае нужен только метод «запрос», так как вам не нужны никакие конкретные настройки в интерфейсе и т. Д.

Чтобы получить product_id или nid внутри вашего метода «query ()», вы должны использовать существующие поля, которые были добавлены в запрос обработчиками полей представлений (и определены в вашем интерфейсе представлений).

Этот файл является идеальным примером того, чего вы хотите достичь (вы можете найти его в документации по представлениям, он существует, но мне не разрешено устанавливать ссылку, так как моя репутация слишком низкая):

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

Посмотрите, сможете ли вы адаптировать этот код под свои нужды, и я буду рад увидеть конечный результат :)

Renrhaf
источник
Я отредактировал свой вопрос с прогрессом. Если вы хотите завершить свой ответ? Большое спасибо
Kojo
1
Готово, пожалуйста, проверьте это и скажите мне, если вам удалось заставить ваш вид работать :)
Renrhaf
Плохо, я понял, что, поскольку сортировка зависит от запроса к базе данных, похоже, что я никогда не добьюсь сортировки представлений с помощью фиктивного поля, подобного тому, которое создано обработчиком! Так что я окончательно должен работать над подзапросом!
Кожо
1
Извините, что не отреагировал раньше! Я надеюсь, что мой ответ был полезен, но вы сделали всю работу самостоятельно, я не знал о псевдониме подзапроса, спасибо за ваше подробное решение, это поможет многим людям.
Renrhaf
4

Ниже я расскажу о полной реализации того, как я сделал замену сортировки Views PHP на собственный обработчик Views .

.info файл

files[] = includes/views_handler_my_custom_sort.inc

Файл модуля

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

файл 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');

  }
}

Небольшое объяснение: после понимания того, как реализовать обработчики Views, я запутался с подзапросом:

  • сопоставьте его с внешним запросом, чтобы получить динамический результат «строка за»: та же таблица и столбец, но разные псевдонимы: WHERE nod.nid = node.nid
  • установить псевдоним в add_orderby: $this->query->add_orderby(NULL, $sub_query, 'DESC', 'subquery');работает, но $this->query->add_orderby(NULL, $sub_query, 'DESC');не

Этот последний момент был удивительным, потому что хотя он 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 )работает с прямым вводом SQL, он не распространяется на текущую настройку.

Вам нужно указать псевдоним подзапроса, и окончательный запрос будет выглядеть примерно так: 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

Попытки вычислить значения для сортировки результата в поле настраиваемого обработчика не сработали, поскольку сортировка представлений выполняется на основе БД, а обработчик настраиваемого поля является своего рода фиктивным полем ... по крайней мере, это был мой вывод.

Коджо
источник