Я думаю, что проблема в основном связана с sql-структурой запроса, и я не эксперт ….
Мне нужно искать сообщения (пользовательский тип сообщения) по двум параметрам:
pd_city
pd_country
Обратите внимание, что отношение meta_query равно «OR», поэтому, если любой из двух вышеперечисленных символов LIKE, мы должны иметь некоторые результаты.
Третий ключ (is_sponsored) используется для сортировки сообщений! Это может быть 1 или 0, а записи, значения которых «is_sponsored» равны 1, должны быть указаны сверху.
Итак, вот что говорит WordPress:
$sfp_query_args = array( 'sfp_complex_search' => 'yeap', 'tax_query' => array( array( 'taxonomy' => 'sfp_post_category', 'terms' => $term_id ) ), //'meta_key' => 'is_sponsored', 'post_type' => 'sfpposts', 'post_status' => 'publish', 'showposts' => (int)$per_page, 'paged' => $paged, 'meta_query' => array( 'relation' => 'OR', array( 'key' => 'pd_city', 'value' => $sfp_search_meta, 'compare' => 'LIKE' ), array( 'key' => 'pd_country', 'value' => $sfp_search_meta, 'compare' => 'LIKE' ), array( 'key' => 'is_sponsored' ) ) ); $sfp_search = new WP_Query( $sfp_query_args );
Мне также нужно фильтровать результаты с помощью «posts_orderby», чтобы привлечь спонсоров:
add_filter( 'posts_orderby', 'sfp_modify_search' ); function sfp_modify_search( $orderby ) { if( !is_admin() && is_page( $this->options[ 'sfp_page_entries_search' ] ) ) { global $wpdb; $orderby = " CASE WHEN mt2.meta_value = 0 THEN 1 END, $wpdb->posts.post_date DESC "; } return $orderby; }
Реальная проблема заключается в том, что с этим запросом возвращаются ВСЕ POSTS из «sfp_post_category», а не только те, которые соответствуют «pd_city» или «pd_country», потому что ВСЕ POSTS ИМЕЛИ «metal key is_sponsored» (и значение установлено в 1 или 0). Еще раз: «is_sponsored» необходим для сортировки!
Когда var_dump
var_dump( $sfp_search->request );
… WordPress 'sql выглядит так:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID 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) INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) WHERE 1=1 AND ( wp_term_relationships.term_taxonomy_id IN (77) ) AND wp_posts.post_type = 'sfpposts' AND (wp_posts.post_status = 'publish') AND ( (wp_postmeta.meta_key = 'pd_city' AND CAST(wp_postmeta.meta_value AS CHAR) LIKE '%something%') OR (mt1.meta_key = 'pd_country' AND CAST(mt1.meta_value AS CHAR) LIKE '%something%') OR mt2.meta_key = 'is_sponsored' ) GROUP BY wp_posts.ID ORDER BY CASE WHEN mt2.meta_value = 0 THEN 1 END, wp_posts.post_date DESC LIMIT 0, 10
Как удалить все сообщения, которые не соответствуют «pd_city» или «pd_country» из результатов?
Суть дела – мета-запросы, не поддерживающие разные и / или вложенные отношения, – недостаток, кстати, это и привело меня в бешенство. В недавнем примере также есть сценарий поиска.
То, что вы хотите сделать просто, не может быть выполнено с помощью WP_Query
а также одного цикла.
Как вы, кажется, заметили, размещаете ли вы ключ сортировки в массиве meta_query
или вне его, поскольку общий аргумент запроса не имеет значения. Если вы установите отношение мета-запроса к OR
и укажите meta_key
любом месте запросов args без установки сопровождающего параметра meta_value
, запрос всегда будет возвращать хотя бы все сообщения, где установлена эта мета-клавиша.
Кстати и ради полноты: когда вы используете один meta_query с !=
Как значение для meta_compare
, запрос возвращает все результаты с meta_key
набора meta_key
а не равным заданному meta_value
– он не будет возвращать какие-либо сообщения, которые вообще не используется meta_key
. Другой момент, когда мета-запросы терпят неудачу.
Я вижу два варианта. Во- is_sponsored
, вы можете опустить мета-ключ is_sponsored
из запроса, также опустить разбивку на страницы, получить правильные сообщения и выполнить сортировку со вторым экземпляром WP_Query
, передав ему отфильтрованные идентификаторы сообщений через параметр post__in
:
$sfp_search_args = array( 'sfp_complex_search' => 'yeap', 'tax_query' => array( array( 'taxonomy' => 'sfp_post_category', 'terms' => $term_id ) ), 'post_type' => 'sfpposts', 'post_status' => 'publish', 'meta_query' => array( 'relation' => 'OR', array( 'key' => 'pd_city', 'value' => $sfp_search_meta, 'compare' => 'LIKE' ), array( 'key' => 'pd_country', 'value' => $sfp_search_meta, 'compare' => 'LIKE' ) ) ); $sfp_search = new WP_Query( $sfp_search_args ); $post_ids = array(); while ( $sfp_search->have_posts() ) : $sfp_search->next_post(); $post_ids[] = $sfp_search->post->ID; endwhile; $sfp_ordered_args( 'post__in' => $post_ids, // note that 'showposts' is deprected 'posts_per_page' => (int)$per_page, 'paged' => $paged, 'meta_key' => 'is_sponsored', 'order' => 'DESC', 'orderby' => 'meta_value_num date' ); $sfp_ordered = new WP_Query( $sfp_ordered_args ); while ( $sfp_ordered->have_posts() ) : $sfp_ordered->next_post(); // display posts endwhile;
Обратите внимание, что параметр $orderby
WP_Query
будет принимать несколько значений, разделенных пробелом. Ваша модификация поиска может быть более сложной, чем необходимо.
Так как мне нравится ваша идея var_dumping свойства запроса объекта request
, позвольте мне запустить быстрое и, конечно же, непроверенное вторичное предложение:
Если вы слегка изменили данный SQL, заменив логический оператор OR mt2.meta_key = 'is_sponsored'
на AND
и соответствующим образом переместив его, вы могли бы потянуть сообщения с помощью $wpdb
:
$sfp_post_ids = $wpdb->get_col( " SELECT wp_posts.ID 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) INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) WHERE 1=1 AND ( wp_term_relationships.term_taxonomy_id = $term_id ) AND wp_posts.post_type = 'sfpposts' AND (wp_posts.post_status = 'publish') AND ( (wp_postmeta.meta_key = 'pd_city' AND CAST(wp_postmeta.meta_value AS CHAR) LIKE '%$sfp_search_meta%') OR (mt1.meta_key = 'pd_country' AND CAST(mt1.meta_value AS CHAR) LIKE '%$sfp_search_meta%') ) AND mt2.meta_key = 'is_sponsored' GROUP BY wp_posts.ID ORDER BY CASE WHEN mt2.meta_value = 0 THEN 1 END, wp_posts.post_date DESC " );
На этом этапе у вас есть два варианта:
Или итерации по массиву $sfp_post_ids
с простым foreach
и вытащить данные post с помощью get_post()
индивидуально в этом цикле или, если вы хотите получить тонкости WP_Query
– подкачки, теги шаблонов и т. Д. – $sfp_post_ids
post__in
параметру post__in
как в решении 1.
Это сложно 🙂
Я собирался предположить, что вам может не понадобиться array( 'key' => 'is_sponsored' )
в массиве «meta_query», и вы можете сделать это, добавив «meta_key» в основной массив, но он выглядит как вы это пробовали. Получали ли вы те же результаты?
JOIN
s может усложнить ситуацию. Вы подключились к posts_orderby
. Вы считали, что подключаетесь к posts_fields
и добавляете подзапрос, который доставит вам ваше meta_value?
(SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = 'is_sponsored' AND post_id = {$wpdb->posts}.ID) as is_sponsored
Все это происходит из-за отношения OR
к meta_query
и того, как WordPress генерирует фактическую строку запроса. Я в конечном итоге подключился к фильтру posts_clauses
чтобы изменить where
и orderby
фрагменты запроса:
public function wpse_68002_orderby_fix($pieces){ global $wpdb; $pieces['where'] .= " AND $wpdb->postmeta.meta_key = 'your_meta_key'"; // <--- update here with your meta_key name $pieces['orderby'] = "$wpdb->postmeta.meta_value ASC"; return $pieces; }
Просто добавьте фильтр перед настройкой объекта WP_Query, а затем обязательно удалите его после запуска запроса, чтобы не влиять на другие запросы:
add_filter( 'posts_clauses', 'wpse_68002_orderby_fix', 20, 1 ); $query = new WP_Query($args); $result = $query->get_posts(); remove_filter( 'posts_clauses', 'wpse_68002_orderby_fix', 20 );
Не забудьте оставить meta_key
и meta_key
из аргументов запроса.