web-dev-qa-db-ja.com

非公開投稿の場合はget_adjacent_post()をフィルタリングし、JOIN/WHEREを変更するにはどうすればいいですか?

編集3 - G.M.の提案に従って改訂。コードはエラーなしで実行されますが、それでも隣接する投稿フィルターはフィルタリングされません。変更はコードのコメントに記載され、現在の質問は投稿の下部に示されます。

編集2 - 下のG.M.のコード提案を反映しています。変更されたjoin/whereフィルター関数がどこに行くべきかについて混乱しているので、私はmu-pluginコード構造の詳細を追加しました。

編集1 - 下記のKaiserのコメントを反映、例を示しています。また、どのコードが投稿をフィルター処理しているのか間違っていました。質問はMembersプラグインに関連していません。

私はmu-pluginが私のWPクエリをフィルタリングしているので、インデックスループでは訪問者は読むことができる投稿しか見ることができません。これは、投稿の作成者に、各投稿に複数レベルのメンバーシップ分類を割り当てる機能を提供することで実現します。

最も低い(公の)レベルでは、投稿に添付される会員分類はありません。

これがmu-pluginのクエリフィルタです。

if ( ! class_exists ( 'Site_Members_Taxonomy' ) ) {

class Site_Members_Taxonomy
{
    //define( 'LANG', 'some_textdomain' ); deleted LANG, was throwing errors

    public $post_types = array( 'post', 'page', 'gallery' );
    public $tax = 'membership';
    public $tax_label; // CHANGE: made these three vars public

    public function __construct()
    {
    $this->tax_label = __( 'Membership' );

    // plugin defines taxonomy and membership meta and then:

    add_action( 'pre_get_posts', array( &$this, 'filter_query' ) );

    /**
     * Modify the query based on membership taxonomy
     */
    function filter_query( $query ) {

        //quit right now if in admin
        if( is_admin() ) return;

        $tax_query = '';
        $terms = $this->get_terms_to_exclude(); // see function below

        if( $terms ) 
            $tax_query = array(
                   array(
                        'taxonomy' => $this->tax,
                        'field' => 'slug',
                        'terms' => $terms,
                        'operator'=>'NOT IN'
                    )
            );

        // if after all that we have a tax query to make, lets do it!
        if ( $tax_query )
            set_query_var('tax_query', $tax_query);

    }

    } //end class

    global $Site_Members_Taxonomy;
    $Site_Members_Taxonomy = new Site_Members_Taxonomy();

}

// finally, outside the class definition, there is a function `members_can_view` to be used as a conditional to hide/show various template tags etc.

ここまでは順調ですね。このフィルターはとてもうまくいきます。

問題は、シングル投稿ビューではnext_post_link()previous_post_link()がフィルタを取得できないため、訪問者はにアクセスできないreadにアクセスできません。これらの記事へのリンクをフィルタリングして、実際に読むことができる次の記事へのリンクのみを表示します。

私は基本的にget_adjacent_post()で上記のクエリフィルタを使いたいのです。

Link-template.phpの WPコアTrac (および以下にリンクされているKaiserの投稿も同様)私はnext_post_link()get_next_post()およびget_adjacent_post()が次のフィルタを通して隣接するpostクエリを実行することを発見しました。

  • get_{$adjacent}_post_join
  • get_{$adjacent}_post_where
  • get_{$adjacent}_post_sort

REWRITE JOIN/WHEREフィルタ

私はG.M.の答えで提案されているJOIN/WHEREを次のように書き換えてみました。彼が言うように、それはwp-core関数に非常に密接に基づいており、trとttは異なる名前を与えられ、除外された用語は関数に直接渡されます。

この機能は現在サイト上でアクティブになっており、エラーをスローしませんが、隣接する投稿のクエリも正しくフィルタリングしていません。

現在の質問:以下の関数filter_adjacentで$ excluded_termsをスラッグからIDに変換するとき、正しく配列を構築できますか。除外した用語を記録して何が起こっているのかを調べるにはどうすればよいですか。

    // Filters for get_adjacent_posts() so they don't show up in single-post navigation  
    // These filters are added within function __construct{} noted above
    // CHANGE: &$this to $this

    add_filter( 'get_previous_post_where', array( $this, 'filter_adjacent' ) );
    add_filter( 'get_next_post_where', array( $this, 'filter_adjacent' ) );
    add_filter( 'get_previous_post_join', array( $this, 'filter_adjacent' ) );
    add_filter( 'get_next_post_join', array( $this, 'filter_adjacent' ) );


    /**
     * These functions added within mu-plugin class:
     *
     * First, modify excluded terms based on membership taxonomy
     * 
     * NOTE: Excluded terms may be an array
     */

    function get_terms_to_exclude() {

        // if a real admin or "level1" we won't change tax query at all
        if( current_user_can ( 'manage_options' ) || current_user_can ( 'view_level1_posts' ) )
            return false;

        // default/public will exclude all upper level posts
        // in other words, all posts with membership taxonomy
        $terms = array( 'level1','level2','level3' );

        // if a level3 reader we'll exclude level1 and level2 posts
        if( current_user_can ( 'view_level3_posts' ) )
            $terms = array( 'level1', 'level2' );

        // if at level2 level we'll exclude level1 level posts
        if( current_user_can ( 'view_level2_posts' ) )
            $terms = array( 'level1' );

        return $terms;

    }


    /**
     * Next, use these terms to filter the get_adjacent_post JOIN/WHERE
     */

    function filter_adjacent( $clause ) {

        if ( substr_count( current_filter(), '_post_join' ) ) { // filtering join

            if ( empty($clause) ) $clause = '';
            global $wpdb;
            $clause .=
                " INNER JOIN {$wpdb->term_relationships} AS trmship ON p.ID = trmship.object_id
                INNER JOIN {$wpdb->term_taxonomy} ttmship
                ON trmship.term_taxonomy_id = ttmship.term_taxonomy_id";
            return $clause;

        } elseif ( substr_count( current_filter(), '_post_where' ) ) { // filtering where

            $excluded_term_slugs = get_terms_to_exclude();
            if ( ! $excluded_term_slugs ) return $clause; // nothing to filter if no terms
            $excluded_terms = array(); // we needs term ids, let's convert slug in terms ids
            foreach ( $excluded_term_slugs as $slug ) {
                $t = get_term_by( 'slug',  $slug, 'membership' );
                if ( ! $t || is_wp_error($t) ) continue;
                $excluded_terms[] = $t;
            }
            // something wrong in get_level_term_to_exclude()?
            if ( empty($excluded_terms) ) return $where; 

            $posts_in_ex_terms_sql = 
                " AND ttmship.taxonomy = 'membership'
                AND ttmship.term_id NOT IN (" . implode( $excluded_terms, ',' ) . ')';
            // return filtered where clause
            return $clause. $posts_in_ex_terms_sql;
        }
    }
5
nimmolo

まず、除外する用語を返す関数を使用することをお勧めします。 「pre_get_posts」フィルターおよび隣接する投稿フィルター。

そう:

function get_level_term_to_exclude() {
  if ( current_user_can ('manage_options') || current_user_can ('view_level1_posts') ) {
    return false;
  }
  $terms = array( 'level1', 'level2', 'level3' );
  if( current_user_can ( 'view_level2_posts' ) ) {
    $terms = array( 'level1' );
  }
  if( current_user_can ( 'view_level3_posts' ) ) {
    $terms = array( 'level1', 'level2' );
  }
  return $terms;
}

わかりやすくするためにコメントを削除しましたが、コードはよくコメントされており、そこからコードが取得されます。機能トップに管理者のチェックを入れました。

その後、通常はスピーキング、JOINができないWHEREなしでフィルタリングし、WHEREは失敗JOINで定義されていないテーブル名を使用しているため、joinメソッドおよびwhereメソッドが存在しない場合、ただし、joinとwhere methodが存在します。

コアでは、除外用語のフィルターは'WHERE'JOINの両方を介して行われます。実際には、#1183行目で次のように表示されます。

"WHERE p.post_date $op %s AND p.post_type = %s
 AND p.post_status = 'publish' $posts_in_ex_terms_sql"

#1154行目のif ( ! empty( $excluded_terms ) ) {ステートメント内

および#1141行目:

if ( $in_same_term || ! empty( $excluded_terms ) ) {
   $join = " INNER JOIN $wpdb->term_relationships AS tr ON p.ID = tr.object_id
        INNER JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id";

$posts_in_ex_terms_sqlは次のように行#1173に設定されます。

$posts_in_ex_terms_sql = $wpdb->prepare(
  " AND tt.taxonomy = %s
   AND tt.term_id NOT IN (" . implode( $excluded_terms, ',' ) . ')', $taxonomy
);

$excluded_termsが空でない場合にコアで使用されるコードをコピーできると思います...コアで機能する場合は、あなたにも機能するはずです。

したがって、"get_{$adjacent}_post_where"フィルターand"get_{$adjacent}_post_join"を使用して、コアワークフローを模倣できます。

したがって、関数filter_adjacentはjoin句とwhere句で動作するはずです。 current_filter をチェックするだけでうまくいきます。

これは私のヒントを使用してクラスが表示される方法です。

class Site_Members_Taxonomy {

  const LANG = 'some_textdomain';

  public $post_types = array( 'post', 'page', 'gallery' );

  public $tax = 'membership';

  public $tax_label;

  public function __construct() {
    $this->tax_label = __( 'Membership', self::LANG );
    add_action( 'pre_get_posts', array( $this, 'filter_query' ) );
    add_filter( 'get_previous_post_where', array( $this, 'filter_adjacent' ) );
    add_filter( 'get_next_post_where', array( $this, 'filter_adjacent' ) );
    add_filter( 'get_previous_post_join', array( $this, 'filter_adjacent' ) );
    add_filter( 'get_next_post_join', array( $this, 'filter_adjacent' ) );
  }

  function get_term_to_exclude() {
    if (
      is_admin()
      || current_user_can ('manage_options')
      || current_user_can ('view_level1_posts')
    ) {
      return false;
    }
    $terms = array( 'level1', 'level2', 'level3' );
    if( current_user_can ( 'view_level2_posts' ) ) {
      $terms = array( 'level1' );
    }
    if( current_user_can ( 'view_level3_posts' ) ) {
      $terms = array( 'level1', 'level2' );
    }
    return $terms;
  }

  /**
   * Modify the query based on membership taxonomy
   */
  function filter_query( $query ) {
    $terms = $this->get_term_to_exclude();
    if ( ! $terms ) return;
    $tax_query = array(
      array(
        'taxonomy' => $this->tax,
        'field' => 'slug',
        'terms' => $terms,
        'operator' => 'NOT IN'
      )
    );
    set_query_var( 'tax_query', $tax_query );
  }

  /**
   * Filter adjacent posts
   */
  function filter_adjacent( $clause ) {
    if ( substr_count( current_filter(), '_post_join' ) ) {
      if ( empty($clause) ) $clause = '';
      global $wpdb;
       $clause .=
        " INNER JOIN {$wpdb->term_relationships} trmship ON p.ID = trmship.object_id
        INNER JOIN {$wpdb->term_taxonomy} ttmship
        ON trmship.term_taxonomy_id = ttmship.term_taxonomy_id";
      return $clause;
    } elseif ( substr_count( current_filter(), '_post_where' ) ) {
      $excluded_term_slugs = $this->get_term_to_exclude();
      if ( ! $excluded_term_slugs ) return $clause;
      $excluded_terms = array();
      foreach ( $excluded_term_slugs as $slug ) {
        $t = get_term_by( 'slug',  $slug, $this->tax );
        if ( ! $t || is_wp_error($t) ) continue;
        $excluded_terms[] = $t->term_id;
      }
      $excluded_terms = array_filter( array_map( 'intval', $excluded_terms ) );
      if ( empty( $excluded_terms ) ) return $clause; 
      global $wpdb;
      $posts_in_ex_terms_sql = $wpdb->prepare(
        " AND ttmship.taxonomy = '%s'
        AND trmship.term_taxonomy_id NOT IN (" . implode( ',', $excluded_terms ) . ')',
        $this->tax
      );
      return $clause. $posts_in_ex_terms_sql;
    }
  }
}

get_adjacent_postが呼び出されたときに$excluded_terms引数に用語が渡されたり、/または$in_same_termがtrueに設定されている場合でも、フィルターが機能するように、分類テーブルに別のエイリアスを使用しました。

コードは完全にテストされていません。動作するかどうかを教えてください...

3
gmazzap