Пользовательский запрос с исключением категории и "белым списком" после меты


У меня есть два виджета, один показывает Новости , а другой показывает все, кроме новостей (мы назовем второй Блог ). Часть блога в настоящее время работает правильно.

Новостные сообщения либо классифицируются как In the News, ЛИБО имеют пользовательскую мету публикации in-the-news, равную 1. Важно отметить, что не для всех сообщений будет определена пользовательская мета-запись.

Основная проблема, с которой я сталкиваюсь, заключается в том, что мне нужно сравнить мета-пост на основе относится ли сообщение к той или иной категории.

Возможно, мне потребуется выполнить пользовательский запрос базы данных для такого поведения, по крайней мере, для новостей. Возможно ли выполнить пользовательский запрос и преобразовать его в стандартный объект WP_Query?


Что я пробовал

Запрос на Новости.

Проблема: показывает только сообщения в категории 75, которые имеют in-the-news = 1.

Желаемый результат: Показать все сообщения из категории 75 независимо от значение in-the-news и любые дополнительные сообщения из-за пределов категории, которые имеют in-the-news = 1

query_posts(array(
  'ignore_sticky_posts' => true,
  'posts_per_page' => 3,
  'cat' => "75", // Only posts within category 75 (News)

  // Including posts tagged to show "In the News"
  'meta_query' => array(
    'relation' => 'OR',
    array(
      'key' => 'in-the-news',
      'value' => '1',
      'compare' => '=',
    ),
    array( 
      'key' => 'in-the-news',
      'compare' => 'NOT EXISTS',
      'value' => '',
    ),
  )
));

Вот запрос для блога :

Этот запрос, похоже, работает правильно, показывает все записи за пределами категории 75 и скрывает записи с in-the-news = 1.

query_posts(array(
  'ignore_sticky_posts' => true,
  'posts_per_page' => 3,
  'cat' => "-75", // All posts EXCLUDING those in category 75 (News)

  'meta_query' => array(
    'relation' => 'OR',
    array(
      'key' => 'in-the-news',
      'value' => '0',
      'compare' => '=',
    ),
    array( 
      'key' => 'in-the-news',
      'compare' => 'NOT EXISTS',
      'value' => '',
    ),
  )
));

Решение

Спасибо s_ha_dum за удивительные примеры, вот последний запрос, который я использую. Логика здесь заключается в том, что мы выполняем пользовательский запрос MySQL для массива идентификаторов сообщений, а затем выполняем стандартный WP_Query с использованием возвращенных идентификаторов.

Примечание: Логика здесь была немного сложнее, чем в исходном сообщении. В принципе, у нас изначально были "Новости" и "Блог". Но теперь у нас тоже есть "Риск". Кроме того, мета-запись in-the-news была изменена на show-in-blog, поскольку мы изменили функциональность. Я не могу ожидать, что кто-то еще когда-либо поймет причины этого в первую очередь, так как даже я думаю, что это смешно (Но клиент этого хочет, так тому и быть!)

Вот мой запрос, для Виджет "Блог". Небольшие изменения сделаны для области "Новости". Я не уверен, можно ли очистить запрос. Я также не уверен, что это плохая идея - выполнять так много выборок в одном запросе.

global $wpdb;

$query = // Splitting the SQL query to it's own code block for syntax highlighting!

  SELECT `ID`
  FROM {$wpdb->posts} posts
  WHERE (

    /* Exclude posts in News (94) 
       Ignore show-in-blog option for News here, 
       as News is displayed on the same page */
    94 NOT IN ( 
      SELECT `term_taxonomy_id` 
      FROM {$wpdb->term_relationships}
      WHERE `object_id` = posts.`ID`
    )

    AND

    /* Exclude posts in Risk (96), 
       But show Risk posts that have the postmeta "show-in-blog" */
    (

      96 NOT IN ( 
        SELECT `term_taxonomy_id` 
        FROM {$wpdb->term_relationships}
        WHERE `object_id` = posts.`ID`
      )
      OR
      (
        96 IN ( 
          SELECT `term_taxonomy_id` 
          FROM {$wpdb->term_relationships}
          WHERE `object_id` = posts.`ID`
        )
        AND
        1 IN (
          SELECT `meta_value` 
          FROM {$wpdb->postmeta}
          WHERE 
          `meta_key` = 'show-in-blog'
          AND `post_id` = posts.`ID`
        )
      ) /* OR */

    ) /* AND */

  ) /* WHERE */

  ORDER BY `post_date` DESC;

$args = array(
  // Only include IDs returned by the above query
  'post__in' => $wpdb->get_col( $query ),
  'posts_per_page' => 10,
);

$wp_query = new WP_Query( $args );

get_template_part('loop', 'frontpage');
Author: Radley Sustaire, 2013-05-07

1 answers

Можно ли выполнить пользовательский запрос и преобразовать его в стандартный Объект WP_Query?

Да. Вы можете сделать что-то вроде...

$post_ids = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE ....");
$posts_qry = new WP_Query(array('post__in'=> $posts_ids,'orderby' => 'post__in'));

Второй запрос сохранит порядок результатов по сравнению с первым.

Я совершенно уверен, что вы не можете выполнить этот первый запрос с помощью WP_Query или любой другой функции. Логика слишком сложна. Вам понадобится предложение JOIN или WHERE, которое WP_Query не поддерживает. К сожалению, написание этого SQL будет тоже сложно, просто чтобы получить категорию.

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

$post_ids = new WP_Query(array(
  'fields' => 'ids',
  'ignore_sticky_posts' => true,
  'posts_per_page' => 3,
  'cat' => "75", // Only posts within category 75 (News)
));

$post_ids_2 = new WP_Query(array(
  'fields' => 'ids',
  'ignore_sticky_posts' => true,
  'posts_per_page' => 3,
  'post__not_in' => $post_ids->posts,
  'meta_query' => array(
    array(
      'key' => '_edit_lock',
      'value' => '0',
      'compare' => '=',
    ),
  )
));
$post_ids = $post_ids->posts + $post_ids_2->posts;
$posts_qry = new WP_Query(array('post__in'=> $posts_ids,'orderby' => 'post__in'));

Это 3 запроса (ой), и заказ может быть проблемой, если вы беспокоитесь об этом.

Другой вариант - изменить запрос с помощью нескольких фильтров. Аргументы WP_Query будут касаться части in-the-news = 1.

$post_ids_v3 = new WP_Query(array(
  'ignore_sticky_posts' => true,
  'posts_per_page' => 20,
  'my_variable' => true,
  // Including posts tagged to show "In the News"
  'meta_query' => array(
    array(
      'key' => 'in-the-news',
      'value' => '1',
      'compare' => '=',
    )
  )
));

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

function posts_join_wpse_98652($join,$qry) {
  global $wpdb;
  if (true === $qry->get('my_variable')) {
    $join .= " JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)";
  }
  return $join;
}
add_filter('posts_join','posts_join_wpse_98652',1,2);

function posts_where_wpse_98652($where,$qry) {
  global $wpdb;
  if (true === $qry->get('my_variable')) {
    $where .= " OR {$wpdb->term_relationships}.term_taxonomy_id IN (75)";
  }
  return $where;
}
add_filter('posts_where','posts_where_wpse_98652',1,2);

Это даже близко не проверено, но, похоже, работает, когда я пытаюсь. Тем не менее, такие вещи бывает трудно исправить, возможно, я совершил ошибку. Едва проверенный. Возможно, глючит. Предостережение опустошитель. Никаких возвратов.

 3
Author: s_ha_dum, 2013-10-27 15:07:22