ORDER BY wp_post по названию пользовательского столбца в wp_query

Вопрос или проблема

Я уже много исследовал этот вопрос, но нигде не нашел подходящего ответа. Это очень небольшая задача для меня, если нужно сделать запрос MySQL. Но так как у меня нет хорошего опыта работы в WP, я очень расстроен из-за решения этого вопроса.

Как сортировать wp_query по пользовательскому столбцу, который я добавил в таблицу wp_posts.

    $args = array(
    /*'base'               => '%_%',
    'format'             => '?paged=%#%',
    'total'              => 1,
    'current'            => 0,
    'show_all'           => false,
    'end_size'           => 1,
    'mid_size'           => 2,
    'prev_next'          => true,
    'prev_text'          => __('« Previous'),
    'next_text'          => __('Next »'),
    'type'               => 'plain',
    'add_args'           => false,
    'add_fragment'       => '',
    'before_page_number' => '',
    'after_page_number'  => ''*/
    'posts_per_page' => 10,
    'post_type' => array('food', 'buffets'),
    'meta_key'  => 'post_priority',
    'orderby'   => 'meta_value_num',    
    'order'  => 'ASC'

);

Когда я пробую с этим, на самом деле сортировка идет по таблице wp_postmeta wp_postmeta.meta_value+0, что означает, что вместо таблицы wp_posts она сортируется по таблице postmeta. Post_priority — это мой пользовательский столбец в таблице wp_posts

Вот результат запроса, который я получил:

"SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts 
INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) 
WHERE 1=1 AND ( \n wp_postmeta.meta_key = 'post_priority'\n) 
AND wp_posts.post_type IN ('food', 'buffets') 
AND (wp_posts.post_status="publish" OR wp_posts.post_author = 2 
AND wp_posts.post_status="private")
GROUP BY wp_posts.ID 
ORDER BY wp_postmeta.meta_value+0 
ASC LIMIT 0, 10","posts"

Вы указали сортировку по meta_value, поэтому сортировка идет по этому значению. В таблице wp-posts нет столбца “post_priority”, поэтому сортировка по нему невозможна. Допустимые значения для “orderby” не безграничны, это фиксированный набор.

Вы не можете изменить базовые таблицы и ожидать, что код будет об этом знаеть. Основная причина наличия таблицы postmeta заключается в том, чтобы позволить вам вставлять произвольные метаданные вместо того, чтобы изменять таблицы, добавляя столбцы, о которых код не знает.

Удалите ваш дополнительный столбец и храните данные как postmeta. Установите meta_key в “priority” и meta_value в значение приоритета. Тогда ваш запрос будет работать.

Я немного исследовал вашу проблему, и она заключается в функции WP_Query::parse_orderby(). Там все, что вы хотите использовать в качестве сортировки, проходит санитизацию, как указал пользователь Otto. К сожалению, нет фильтра для вмешательства. И да, лучше использовать некоторые мета поля, если вы можете позволить себе дорогой JOIN в каждом get_posts. Если нет, имейте в виду, что поле menu_order тоже может быть использовано, если вы не имеете дела с типами постов nav_menu_item.

В любом случае, я нашел два решения и хочу поделиться ими здесь. Для получения более подробной информации прочтите https://wp-includes.org/296/custom-wp_posts-column-sortable/

Расширение WP_Query

Самый чистый способ — написать собственный класс запроса:

<?php

class Enhanced_Post_Table_Query extends \WP_Query {

  /**
   * Расширить порядок клаузов собственными столбцами.
   *
   * @param string $order_by
   *
   * @return bool|false|string
   */
  protected function parse_orderby( $order_by ) {
    $parent_orderby = parent::parse_orderby( $order_by );

    if ( $parent_orderby ) {
      // WordPress знает, что делать => оставляем как есть
      return $parent_orderby;
    }

    // список разрешенных полей, которые мы расширили
    $additional_allowed = array(
      'something',
    );

    if ( ! in_array( $order_by, $additional_allowed, true ) ) {
      // не разрешенный столбец => ранний выход
      return false;
    }

    // По умолчанию: сортировать по полю поста.
    global $wpdb;

    return $wpdb->posts . '.post_' . sanitize_key( $order_by );
  }
}

Теперь вы можете выполнять запросы и сортировать с помощью пользовательского поля:

$get_posts = new Enhanced_Post_Table_Query;

$get_posts->query(
  array(
    'orderby' => 'something'
  )
);

Поздний фильтр для перезаписи

Немного грязный способ, но он работает. Так как нет прямого фильтра для выполнения этой задачи, вы можете выбрать более поздний фильтр и манипулировать запросом (чем позже, тем более грязно):

<?php

/**
 * Добавить пользовательский столбец wp_posts для сортировки.
 *
 * @param string   $order_clause SQL-клаузула для сортировки.
 * @param WP_Query $query        Объект запроса.
 *
 * @return string Клаузула порядка, например, "wp_posts.post_foo DESC" или подобное.
 */
function custom_column_sort_filter( $order_clause, $query ) {

  // список разрешенных полей, которые мы расширили
  $additional_allowed = array(
    'something',
  );

  if (
    ! in_array(
      $query->get('orderby'),
      $additional_allowed,
      true
    )
  ) {
    // неизвестный столбец => оставить как было
    return $order_clause;
  }

  global $wpdb;

  return $wpdb->posts
         . '.post_'
         . sanitize_key( $query->get('orderby') )
       . ' ' . $query->get( 'order' );

}

add_filter( 'posts_orderby', 'custom_column_sort_filter', 10, 2 );

Теперь почти каждый вызов get_posts получает информацию о вашем пользовательском столбце:

get_posts(
  [
    'orderby'          => 'something',
    // ВАЖНО:
    'suppress_filters' => false,
  ]
);

Но только если установлено “suppress_filters”. Это должно использоваться всеми плагинами. Есть еще больше решений через preg_replace, но они очень поздние и замена с помощью REGEXP всегда грязная и опасная.

Надеюсь, вы сможете с этим работать!

.

Ответ или решение

Чтобы ответить на ваш вопрос о сортировке wp_query по пользовательской колонке, добавленной в таблицу wp_posts, нам нужно более подробно рассмотреть структуру базы данных WordPress и то, как она обрабатывает пользовательские данные.

Теория:

WordPress обрабатывает настроенные поля и метаданные с использованием специализированной таблицы wp_postmeta, а не изменяет структуру основной таблицы wp_posts. Это позволяет более безопасно и гибко работать с метаданными без модификации базовой модели данных WordPress. Ключевой функцией этой архитектуры является отделение данных от логики, что позволяет избежать ошибок и конфликтов при обновлении WordPress.

Стандартный метод сортировки записей по пользовательскому полю заключается в использовании параметра meta_key в WP_Query, который затем указывает на сортировку по meta_value.

Пример:

Ваш текущий запрос предполагает, что сортировка должна происходить по колонке post_priority в wp_posts, которая не поддерживается WordPress по умолчанию. Вместо этого, рекомендуется использовать таблицу wp_postmeta.

Пример использования настройки для сортировки:

$args = array(
    'posts_per_page' => 10,
    'post_type'      => array('food', 'buffets'),
    'meta_key'       => 'post_priority',
    'orderby'        => 'meta_value_num',
    'order'          => 'ASC'
);

Теперь вы указали WordPress, что нужно использовать поле metakey для сортировки, что он обрабатывает через добавление внутреннего соединения на wp_postmeta.

Применение:

Если действительно нужна сортировка по колонке из wp_posts, например post_priority, у которой нет стандартного интерфейса, вам следует прибегнуть к расширению функционала через создание специальных классов или использование фильтров.

Первый подход: Создание собственного класса запроса, расширяя WP_Query, чтобы вручную обрабатывать параметр orderby.

class Enhanced_Post_Table_Query extends WP_Query {

  protected function parse_orderby( $order_by ) {
    $parent_orderby = parent::parse_orderby( $order_by );

    if ( $parent_orderby ) {
      return $parent_orderby;
    }

    // Допустимые дополнительные поля
    $additional_allowed = array(
      'post_priority',
    );

    if ( ! in_array( $order_by, $additional_allowed, true ) ) {
      return false;
    }

    global $wpdb;
    return $wpdb->posts . '.post_' . sanitize_key( $order_by );
  }
}

Второй подход: Использование фильтра posts_orderby на более позднем этапе обработки запроса, чтобы вручную указать WordPress использовать настроенную колонку.

function custom_column_sort_filter( $order_clause, $query ) {
  $additional_allowed = array(
    'post_priority',
  );

  if ( ! in_array( $query->get('orderby'), $additional_allowed, true ) ) {
    return $order_clause;
  }

  global $wpdb;
  return $wpdb->posts . '.post_' . sanitize_key( $query->get('orderby') ) . ' ' . $query->get( 'order' );
}

add_filter( 'posts_orderby', 'custom_column_sort_filter', 10, 2 );

Эти решения дадут вам нужную гибкость и безопасность при работе с нестандартными потребностями сортировки в WordPress. Следует всегда помнить, что модификация базовых таблиц может привести к проблемам совместимости в будущем.

Оцените материал
Добавить комментарий

Капча загружается...