Использование фильтра '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). Еще раз спасибо!
GreatBlakes
Без проблем. Мне интересно, почему вам пришлось изменить AND на OR? Идея в том, что вы указываете запросу "(выполнить все предыдущие условия) AND (убедиться, что автор записи - текущий пользователь OR убедиться, что текущему пользователю разрешено редактировать запись)". Изменяя это на OR, вы игнорируете все остальные условия, такие как тип записи - например, пользователи, которые являются авторами страниц, могут увидеть их в списке, а не только записи.
David Gard
Кроме того, я вижу, что кто-то отклонил ваше редактирование моего ответа. Причина в том, что оно ничего не "исправляет". Например, meta_query не имеет никакого значения (насколько я знаю), вам просто нужно передать его в $query->set(), чтобы убедиться, что сделаны соответствующие соединения. А насчет изменения AND на OR - если бы вы могли объяснить, почему вы это сделали, в комментариях для других пользователей, это было бы замечательно, и тогда я мог бы отредактировать ответ, если это необходимо. Спасибо.
David Gard
Честно говоря, я думал, что починил это, но это оказалось лишь иллюзией работы — ха-ха. Оператор OR заставил всё отображаться, что выглядело как исправление. На самом деле я всё ещё застрял на этом — я уверен, что ваш код сработает, но пока не смог разобраться с деталями. Почему-то остальная часть SQL не работает правильно (и я не понимаю почему, ведь она выглядит именно так, как я хочу). Я даже рад, что моя правка была отклонена, потому что она могла бы завести других людей по ложному пути (туда, где я сейчас).
GreatBlakes
Это связано со стороной meta_key в SQL-запросе. Даже если я попытаюсь запросить только это, я не получаю никаких результатов. Похоже, что $query->set из первой функции как-то влияет на это — мне кажется, что он может неправильно подготавливать эти ключи? О, вам нужно убрать второй вызов add_filter('posts_where', 'my_custom_posts_where'); в вашем ответе — иначе он срабатывает на всех постах (в том числе и на фронтенде) — эта строка должна быть только внутри первой функции.
GreatBlakes
Почему вы добавляете фильтр дважды (один раз внутри колбэка и один раз в глобальной области видимости)? На первый взгляд, он тогда будет выполняться для каждого запроса. +1 за остальную часть ответа :)
kaiser
Хорошее замечание, я удалил тот, что был в глобальной области видимости.
David Gard
Хорошо, теперь всё работает. Это немного неловко, но единственное дополнительное изменение, которое нужно внести в ваш ответ - это заменить '=' после 'dummy' на '!='. Это был окольный путь к решению через тестирование SQL-запросов в phpmyadmin, но в итоге всё теперь работает как надо.
GreatBlakes