Порядок запросов Wordpress по настраиваемым полям, отсутствующим сообщениям

Это мой текущий запрос:

query_posts(array_merge(array('tag' => $pagetag,'meta_key'=>priority,'orderby' =>meta_value, 'order' =>'ASC','paged' => get_query_var('paged'))));

Моя проблема в том, что запрос показывает мне только сообщение, которое имеет значения для моего «мета_ключа», что означает, что «приоритет» не равен NULL. Как я могу улучшить этот запрос, чтобы он по-прежнему упорядочивал мой мета_ключ, но также отображал все сообщения, которые не являются NULL?

Заранее спасибо!


person nimi    schedule 03.07.2012    source источник


Ответы (4)


Проблема в том, что WordPress добавляет INNER JOIN в таблицу wp_postmeta, как только вы упоминаете meta_key в своих условиях. Один из способов обойти проблему — добавить фильтр к предложению order by, примерно так:

function so_orderby_priority($original_orderby_statement) {
    global $wpdb;

    return "(SELECT $wpdb->postmeta.meta_value
               FROM $wpdb->postmeta
              WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id
                AND $wpdb->postmeta.meta_key = 'priority') ASC";
}

add_filter('posts_orderby', 'so_orderby_priority');

query_posts(
    array(
        'tag' => $pagetag,
        'paged' => get_query_var('paged')
    )
);

remove_filter('posts_orderby', 'so_orderby_priority');

Примечание. MySQL сначала сортирует значения NULL. Если вы хотите, чтобы они сортировались последними, попробуйте что-то вроде этого (при условии, что все ваши приоритеты стоят перед ZZZZZ в алфавитном порядке):

function so_orderby_priority($original_orderby_statement) {
    global $wpdb;

    return "IFNULL(
               (SELECT $wpdb->postmeta.meta_value
                  FROM $wpdb->postmeta
                 WHERE $wpdb->posts.ID = $wpdb->postmeta.post_id
                   AND $wpdb->postmeta.meta_key = 'priority'),
                'ZZZZZ') ASC";
}

Изменить

Вот еще немного пояснений, предполагающих, что вы хотя бы немного понимаете SQL.

Ваш исходный query_posts привел к следующему запросу к базе данных:

SELECT wp_posts.*
FROM   wp_posts
       INNER JOIN wp_term_relationships ON ( wp_posts.id = wp_term_relationships.object_id )
       INNER JOIN wp_postmeta ON ( wp_posts.id = wp_postmeta.post_id )
WHERE  1 = 1
       AND ( wp_term_relationships.term_taxonomy_id IN ( 3 ) )
       AND wp_posts.post_type = 'post'
       AND ( wp_posts.post_status = 'publish'
              OR wp_posts.post_status = 'private' )
       AND ( wp_postmeta.meta_key = 'priority' )
GROUP  BY wp_posts.id
ORDER  BY wp_postmeta.meta_value ASC
LIMIT  0, 10; 

Именно INNER JOIN wp_postmeta удалил из результатов все сообщения без приоритета.

Удаление условий, связанных с meta_*, из query_posts:

query_posts(
    array(
        'tag' => $pagetag,
        'paged' => get_query_var('paged')
    )
);

решил эту проблему, но порядок сортировки по-прежнему неправильный. Новый SQL

SELECT wp_posts.*
FROM   wp_posts
       INNER JOIN wp_term_relationships ON ( wp_posts.id = wp_term_relationships.object_id )
WHERE  1 = 1
       AND ( wp_term_relationships.term_taxonomy_id IN ( 3 ) )
       AND wp_posts.post_type = 'post'
       AND ( wp_posts.post_status = 'publish'
              OR wp_posts.post_status = 'private' )
GROUP  BY wp_posts.id
ORDER  BY wp_posts.post_date DESC
LIMIT  0, 10; 

Фильтр posts_orderby позволяет нам изменить предложение ORDER BY: wp_posts.post_date DESC заменяется тем, что возвращает фильтр. Окончательный SQL становится:

SELECT wp_posts.*
FROM   wp_posts
       INNER JOIN wp_term_relationships ON ( wp_posts.id = wp_term_relationships.object_id )
WHERE  1 = 1
       AND ( wp_term_relationships.term_taxonomy_id IN ( 3 ) )
       AND wp_posts.post_type = 'post'
       AND ( wp_posts.post_status = 'publish'
              OR wp_posts.post_status = 'private' )
GROUP  BY wp_posts.id
ORDER  BY (SELECT wp_postmeta.meta_value
           FROM   wp_postmeta
           WHERE  wp_posts.id = wp_postmeta.post_id
                  AND wp_postmeta.meta_key = 'priority') ASC
LIMIT  0, 10 

который делает то, что вам нужно.

person Hobo    schedule 03.07.2012
comment
Благодарю вас! Работал потрясающе... хороший код... Но я был бы очень рад, если бы вы могли объяснить немного больше, что вы сделали. Я новичок в php и не совсем понял... Чем отличается запрос функции от исходного запроса? И что конкретно сделал фильтр? - person nimi; 04.07.2012
comment
Рад, что это помогло. Решение больше похоже на MySQL, чем на PHP, поскольку query_posts генерирует SQL для работы с базой данных за кулисами. Фильтр полностью заменяет порядок сортировки в SQL кодом, который будет сортировать результаты так, как вам нужно. Я отредактирую свой ответ, чтобы добавить немного больше деталей. - person Hobo; 04.07.2012

Мне нужно было выполнить аналогичную задачу на странице users.php для пользовательского столбца, который я добавил, и использовал следующий код, который я изменил из Hobo.

add_action('pre_user_query', 'qd_users_column_orderby');

function qd_users_column_orderby($userquery){
  if('my_last_login'==$userquery->query_vars['orderby']) { //check if my cusomt meta_key is the column being sorted

    global $wpdb;

    $userquery->query_orderby = " ORDER BY(SELECT $wpdb->usermeta.meta_value
        FROM $wpdb->usermeta
        WHERE $wpdb->users.ID = $wpdb->usermeta.user_id
        AND $wpdb->usermeta.meta_key = 'my_last_login') ".($userquery->query_vars["order"] == "ASC" ? "asc " : "desc ")." , wp_users.user_login ".($userquery->query_vars["order"] == "ASC" ? "asc " : "desc ");
  }
}

Надеюсь, это поможет кому-то, кто нуждается в этой информации.

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

add_filter('manage_users_columns', 'qd_add_user_login_column');

function qd_add_user_login_column($columns) {
  $columns['my_last_login'] = 'Last Logged In';
  return $columns;
}

add_action('manage_users_custom_column',  'qd_show_user_login_column_content', 10, 3);

function qd_show_user_login_column_content($value, $column_name, $user_id) {
  $user = get_userdata( $user_id );
  if ( 'my_last_login' == $column_name ){
    $lastLogin = get_the_author_meta('my_last_login', $user_id);
    if(!$lastLogin){
      return "Never";
    }else{
      date_default_timezone_set(get_option('timezone_string'));
      return date('m/d/y g:ia', $lastLogin); 
    }
  }
  return $value;
}

add_filter( 'manage_users_sortable_columns', 'qd_users_table_sorting' );

function qd_users_table_sorting( $columns ) {
  $columns['my_last_login'] = 'my_last_login';
  return $columns;
}
person Eric K    schedule 14.02.2014
comment
Я знаю, что этот ответ не совсем соответствует отдельному вопросу, но я хотел просто задокументировать свой ответ здесь, так как смог решить свою проблему, используя отмеченный ответ. - person Eric K; 14.02.2014
comment
Это на самом деле помогло мне, наткнувшись на это здесь. У меня есть дата last_login, хранящаяся в виде длинного текста в мета-области пользователя. Я также добавлял 3 новых столбца в таблицу users.php. Это был единственный метод (pre_user_query), который действительно работал для сортировки пользовательского столбца даты, извлеченного из метаобласти. Спасибо за ваш вдумчивый ответ. - person B. Shea; 02.09.2015

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

Ответ Хобо хорошо это объясняет. Здесь я просто собираюсь предложить более простую альтернативу, которая оказалась проще в моем случае (обратите внимание, что это не будет работать правильно, если требуется нумерация страниц).

Я решил сделать сортировку в PHP после того, как запрос сделан. Приятно то, что я лучше контролирую, где заканчиваются сообщения с нулевыми значениями (я хотел, чтобы они отображались последними).

$query = new WP_Query($args); 

//sort by rank
function customSort($a, $b)
{
    $a = get_field("sort_ranking", $a);
    $b = get_field("sort_ranking", $b);

    //handle nulls
    $a = is_numeric($a) ? $a : 9999;
    $b = is_numeric($b) ? $b : 9999;

    if ($a == $b) return 0;
    return ($a < $b) ? -1 : 1;
 }
 usort($query->posts, "customSort");

Здесь у меня есть пользовательское числовое поле с именем sort_ranking, и я использую его для сортировки ASC. Сообщениям с нулевым значением этого поля присваивается 9999, чтобы они оказывались в конце. (Примечание: я использую ACF, поэтому функция get_field)

Надеюсь, это поможет кому-то!

person saudes    schedule 04.10.2017

Самый простой способ сделать это — вставить настраиваемое поле с помощью действия save_post, чтобы у каждого опубликованного сообщения было собственное meta_key со значением по умолчанию.

Используйте запрос MySQL для добавления post_meta ко всем сообщениям, не имеющим мета. Вот и все.

Если вам/кому-то нужна помощь с кодом, просто ответьте :)

ОБНОВЛЕНИЕ

Как и просил Тимусан, добавьте этот код в свой файл functions.php после изменения имени мета-ключа:

add_action('save_post', 'sidati_post_views_metakey');

function sidati_post_views_metakey ($post_id){

    /*
    * $post_id = is the post ID
    * 'sidati_post_views' => is your metakey name (sidati is prefix always nice to add your prefix)
    * 0 => the inital value
    * true => (bool) add true if you want this metakkey become unique
    */

    add_post_meta($post_id, 'sidati_post_views', 0, true);

}


// THIS ACTION MUST RUN ONLY ONE TIME
add_action('init', 'sidati_allposts_must_have_this');

function sidati_allposts_must_have_this(){

    /* Call the WordPress DataBase class */
    global $wpdb; 

    /* This Query will get us all the posts and pages without "sidati_post_views" metakey. */
    $ids = $wpdb->get_row("SELECT ID FROM wpdb->posts WHERE post_type IN ('post', 'page') AND post_status = 'publish' AND ID NOT IN (SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'sidati_post_views')");

    /* After get all posts/pages, now you need to add the meta keys (this may take a few munites if you have many posts/pages)*/
    foreach ($ids as $post_id) {
        add_post_meta($post_id, 'sidati_post_views', 0, true);
    }
}
person Sidati    schedule 16.01.2015
comment
Добавление нескольких примеров кода с самого начала (при публикации вашего ответа) может прояснить, что вы имеете в виду. - person Timusan; 16.01.2015