Использование фильтра 'parse_query' для отображения постов по нескольким критериям
Я пытаюсь отфильтровать страницу записей в админке WordPress, чтобы показывать посты, которые соответствуют критерию "A" -или- критерию "B". Изучил документацию, но не нашел подходящего способа.
Подробности: Я ограничил роли пользователей так, что авторы могут редактировать только свои посты. Однако я добавил произвольное поле, позволяющее авторам выбирать других авторов, чтобы дать им право редактировать запись. Эта функциональность работает идеально, если использовать только один критерий в фильтре 'parse_query', но при попытке использовать оба запрос считает, что я хочу показать посты, соответствующие ВСЕМ критериям (чего нет).
Для справки, вот мой код (в functions.php):
add_filter('parse_query', 'show_appropriate_posts');
function show_appropriate_posts($query) {
if ( strpos($_SERVER[ 'REQUEST_URI' ], '/wp-admin/edit.php') !== false ) {
if ( !current_user_can('manage_options') ) {
global $current_user;
// Показывать только посты текущего пользователя (A).
$query->set('author', $current_user->id);
// Показывать посты, где пользователь "отмечен" (B).
$query->set('meta_key', 'add_editing_permission_for');
$query->set('meta_value', $current_user->id);
$query->set('meta_compare', 'LIKE');
//@TODO: Нужно показывать посты, соответствующие (A) -или- (B).
}
}
}
Повторюсь, оба варианта (A) и (B) работают по отдельности.
Игнорируйте мой комментарий о meta_query
. Он не только не работает с $query->set()
, но и не позволяет контролировать критическое требование запроса "A ИЛИ B".
Вместо этого, я считаю, что вам подойдет комбинация хука действия pre_get_posts
и хука фильтра posts_where
, как показано ниже.
add_action('pre_get_posts', 'my_show_appropriate_posts');
function my_show_appropriate_posts($query){
if(strpos($_SERVER[ 'REQUEST_URI' ], '/wp-admin/edit.php') !== false) :
if(!current_user_can('manage_options')) :
/** Сбросить, чтобы точно контролировать, где эта часть запроса включена */
$query->set('author', null);
/** Убедиться, что включены необходимые таблицы для meta-запроса */
$query->set('meta_query', array(
array(
'key' => 'add_editing_permission_for',
'value' => 'dummy', // Это не может быть пустым из-за бага в WordPress
'compare' => '='
)
));
/** Вызов фильтра для изменения части '$where' запроса */
add_filter('posts_where', 'my_custom_posts_where');
endif;
endif;
}
function my_custom_posts_where( $where = '' ){
global $wpdb;
/** Добавляем необходимые условия в часть '$where' запроса */
$where.= sprintf(
' AND ( %1$s.post_author = %2$s OR ( %3$s.meta_key = "add_editing_permission_for" AND %3$s.meta_value = %2$s ) )',
$wpdb->posts,
get_current_user_id(),
$wpdb->postmeta
);
/** Удаляем фильтр, так как он больше не нужен */
remove_filter('posts_where', 'my_custom_posts_where');
return $where;
}
Рекомендуемые материалы
Хук действия
pre_get_posts
- http://codex.wordpress.org/Plugin_API/Action_Reference/pre_get_postsХук фильтра
posts_where
- http://codex.wordpress.org/Plugin_API/Filter_Reference/posts_where

Спасибо за это - с небольшими изменениями это сработало так, как мне нужно (заменил первый AND в вашем sprintf на OR и отредактировал meta_query). Еще раз спасибо!

Без проблем. Мне интересно, почему вам пришлось изменить AND
на OR
? Идея в том, что вы указываете запросу "(выполнить все предыдущие условия) AND
(убедиться, что автор записи - текущий пользователь OR
убедиться, что текущему пользователю разрешено редактировать запись)". Изменяя это на OR
, вы игнорируете все остальные условия, такие как тип записи - например, пользователи, которые являются авторами страниц, могут увидеть их в списке, а не только записи.

Кроме того, я вижу, что кто-то отклонил ваше редактирование моего ответа. Причина в том, что оно ничего не "исправляет". Например, meta_query
не имеет никакого значения (насколько я знаю), вам просто нужно передать его в $query->set()
, чтобы убедиться, что сделаны соответствующие соединения. А насчет изменения AND
на OR
- если бы вы могли объяснить, почему вы это сделали, в комментариях для других пользователей, это было бы замечательно, и тогда я мог бы отредактировать ответ, если это необходимо. Спасибо.

Честно говоря, я думал, что починил это, но это оказалось лишь иллюзией работы — ха-ха. Оператор OR
заставил всё отображаться, что выглядело как исправление. На самом деле я всё ещё застрял на этом — я уверен, что ваш код сработает, но пока не смог разобраться с деталями. Почему-то остальная часть SQL не работает правильно (и я не понимаю почему, ведь она выглядит именно так, как я хочу). Я даже рад, что моя правка была отклонена, потому что она могла бы завести других людей по ложному пути (туда, где я сейчас).

Это связано со стороной meta_key
в SQL-запросе. Даже если я попытаюсь запросить только это, я не получаю никаких результатов. Похоже, что $query->set
из первой функции как-то влияет на это — мне кажется, что он может неправильно подготавливать эти ключи? О, вам нужно убрать второй вызов add_filter('posts_where', 'my_custom_posts_where');
в вашем ответе — иначе он срабатывает на всех постах (в том числе и на фронтенде) — эта строка должна быть только внутри первой функции.

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

Хорошее замечание, я удалил тот, что был в глобальной области видимости.

Хорошо, теперь всё работает. Это немного неловко, но единственное дополнительное изменение, которое нужно внести в ваш ответ - это заменить '='
после 'dummy'
на '!='
. Это был окольный путь к решению через тестирование SQL-запросов в phpmyadmin, но в итоге всё теперь работает как надо.
