Использование фильтра 'parse_query' для отображения постов по нескольким критериям

26 янв. 2015 г., 03:40:47
Просмотры: 14.9K
Голосов: 1

Я пытаюсь отфильтровать страницу записей в админке 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) работают по отдельности.

3
Комментарии

После дополнительного исследования я думаю, что мне может понадобиться пользовательский SQL-запрос. Если это так, я был бы признателен за любую помощь в его интеграции с указанным выше фильтром.

GreatBlakes GreatBlakes
26 янв. 2015 г. 07:38:44

Посмотрите на meta_query (чтобы заменить meta_key, meta_value и meta_compare), возможно, вы сможете что-то сделать с этим.

David Gard David Gard
26 янв. 2015 г. 16:53:34

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

David Gard David Gard
26 янв. 2015 г. 17:34:26
Все ответы на вопрос 1
8

Игнорируйте мой комментарий о 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;

}

Рекомендуемые материалы

26 янв. 2015 г. 17:33:48
Комментарии

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

GreatBlakes GreatBlakes
26 янв. 2015 г. 21:05:54

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

David Gard David Gard
27 янв. 2015 г. 12:43:26

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

David Gard David Gard
27 янв. 2015 г. 12:53:52

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

GreatBlakes GreatBlakes
27 янв. 2015 г. 20:58:39

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

GreatBlakes GreatBlakes
27 янв. 2015 г. 21:03:23

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

kaiser kaiser
28 янв. 2015 г. 02:22:30

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

David Gard David Gard
28 янв. 2015 г. 11:28:24

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

GreatBlakes GreatBlakes
29 янв. 2015 г. 21:55:49
Показать остальные 3 комментариев