web-dev-qa-db-ja.com

カテゴリとタグの配列に基づくカスタムアーカイブページ

例として、カテゴリとタグを含む渡された配列に基づいてカスタムアーカイブページを作成しようとしています。

http://www.mydomain.com/archives-custom.php?c=cat1,cat2,cat3&t=tag1,tag2,tag3

関連するアーカイブページを生成するには、カスタム分類法またはカスタムクエリを作成する必要がありますか。

例として、 "Test"投稿は "News"と "Business"に分類され、 "Asia"と "China"としてタグ付けされました。その投稿から、「ニュース」と「ビジネス」に分類され、「アジア」と「中国」とタグ付けされた他の投稿を表示するアーカイブへのリンクが欲しいのです。

その場合、リンクは以下のようになります。

http://www.mydomain.com/archives-custom.php?c=news+business&t=asia+china

例は私が達成したいことをより明確にすると思います。

1
JackTheKnife

これは私にとってかなり大規模な小さなプロジェクトであることがわかりました。

基本的なアイデア

私のやり方はカスタムアーカイブページを使うことでした。これがここでは最良のやり方だと思われるからです。カスタム分類法の問題は、構造が整っている場合、それを変更することは非常に面倒なことになるだろうということです。また、メインクエリで何かできることがあれば、私は常にカスタムクエリから離れようとします。

カスタムアーカイブページ(私が行った)の大きなハードルはWordpressがデフォルトでこの設定を提供していないということです。最終目標を達成するために、私はたくさんのカスタムコードを書かなければなりませんでした(そして、@ @ Miloのおかげで、書き換え部分の助けを得なければなりませんでした)。

ワークフロー

私はコードをよくコメントしているので、ここでは詳しく説明しません。重要な点について説明します)。

これはプロセス全体の非常に基本的なワークフローです。

  • 現在の単一の投稿からタグとカテゴリを取得します。より合理化されたより速い操作のために、タグとカテゴリからIDを取得するだけです

  • タグとカテゴリIDを2つの別々の文字列に追加します。関連する投稿を表示するためにこれら2つの文字列が後でtax_queryで使用されるため、これは重要です。

  • 仮想アーカイブページへのリンクを作成します。私はこのようなURLになりました:http://mysite.com/custom-archive/

  • カテゴリIDとタグIDの文字列をそれぞれ保持する2つの新しいquery_varscq、およびtqを設定します。このquery_varsにそれぞれの値を加えたものがリンクURLに追加されます。この値は後でクエリを構築するために使用されます

  • カスタムアーカイブページへのリンクのURLにカスタムquery_varsを追加します。

  • Wordpressが解釈できるようにURLを書き換えます。 (この節私は@Miloから助けを受けました。 彼の答えに見てください質問はこちら

  • parse_query条件式をfalseに設定します(それらすべてを実行するために必ずしも必要というわけではありませんが、私はしましたが、申し訳ありませんが、安全を期すためのケースです)。ここで最も重要なものとあなたが設定しなければならないものはis_home()です。カスタムのリライトルールはURLをhomeに書き換えるからです。これにより、このカスタムページでis_home()がtrueを返すようになります。これは望ましくありません。

  • 必須ではありませんが、実際には便利です。parse_queryを介して新しい条件付きタグを設定し、不要なコードを節約するために条件付き関数is_custom_archive()も作成します。

  • 必須ではありませんが、2つのquery_varsのいずれかがURLに存在する場合に使用するカスタムテンプレートを設定します。私はcustom-archive.phpを使いました。単にindex.phpのコピーを作成し、それをcustom-archive.phpに名前変更することができます。これを省略したい場合は、Wordpressは単に投稿を表示するためにindex.phpを使用します

  • 2つのカスタムpre_get_postsの値に従って投稿を取得するには、tax_queryをメインクエリに設定するためにquery_varsを使用します。これにより、メインクエリが変更されます。

  • 404に直接アクセスする場合は、http://mysite.com/custom-archive/を設定してください。これは、http://mysite.com/category/にアクセスするのと同じ効果があります。この部分を省略してhttp://mysite.com/custom-archive/に直接アクセスした場合、このページはホームページと同じように機能するので、ホームページ上と同じようにすべての投稿が表示されます。

重要な注意事項

  • あなたは、プロセスが完了した後にあなたのパーマリンクをフラッシュする必要があります、そうでなければそれは動作しません

  • 必要に応じてテンプレート名、リンク名、およびquery_varsの名前を変更してください。

  • pre_get_postsアクションに、他に必要なパラメータを追加します。利用可能なすべてのパラメータについては WP_Query をご覧ください。

  • カスタム書き換えルールはデフォルトの投稿タイプとしてpostを設定します。必要に応じて変更できます。また、コードではデフォルトのタグとカテゴリが使用されていますが、他の分類法を利用するように非常に簡単に変更できます。カスタマイズをできるだけ簡単にするために、コードはできるだけ一般的なものにしています

  • 最も重要なことに、このコードは少なくともPHP 5.4を必要とします。それは古いバージョンの修正なしでは動作しません。私はPHP 5.3でのみ導入された多くのクロージャを使いました。その他の重要な構文は、PHP 5.4で導入された短い配列タグ([])の使用です。私はarray()の代わりにこれを使いました

コード

/**
 * Function to get the categories and tags from the current single post
 *
 * @uses wp_get_post_terms()
 * @see http://codex.wordpress.org/Function_Reference/wp_get_post_terms
 *
 * The category and tag ID's are returned which will be used as values for the new query_vars
 *
 * @return (array) $query_string Array of category and tag ID's, each in string format
*/

function get_category_and_tag_ids() {

    /**
     * Get the current single post ID.
     *
     * @uses get_queried_object_id();
     * 
     * $post global is omitted as it is not reliable. Rather use get_queried_object_id()
     * See below link for a complete explanation
     *
     * @link https://wordpress.stackexchange.com/q/167706/31545
    */ 
    $post_id = get_queried_object_id();

    // Set the taxonomies to use, in this case category and post_tag
    $taxonomies = ['category', 'post_tag'];

    foreach( $taxonomies as $taxonomy ) {

            // Use wp_get_post_terms() to get the terms (categories and tags)
        $terms = wp_get_post_terms( $post_id, $taxonomy, ['orderby' => 'id', 'fields' => 'ids'] );

        if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {

            foreach( $terms as $term ) {

                if( $taxonomy == 'category' ) {

                    $categories[] = $term;

                }else{

                    $tags[] = $term;

                }

            }

        }

    }

    // Check if $categories is set, then convert the array into a comma separated string
    if( isset( $categories ) ) {

        $category_string_ids = implode( ',', $categories );

    }

    // Check if $tags is set, then convert the array into a comma separated string
    if( isset( $tags ) ) {

        $tag_string_ids = implode( ',', $tags );

    }

    // Returns $category_string_ids if it is set or empty string on failure 
    $category_query_string = ( isset( $category_string_ids ) ) ? $category_string_ids : '';

    // Returns $tag_string_ids if it is set or empty string on failure 
    $tag_query_string = ( isset( $tag_string_ids ) ) ? $tag_string_ids : '';

    //Create an array of category ids and tag ids to be returned
    $query_string = [
        'category_ids' => $category_query_string,
        'tags_ids' => $tag_query_string 
    ];

    return $query_string;

}

/**
 * Create a link to the virtual page which will be used to display the posts
 *
 * @uses get_home_url()
 * @return $permalink
 *
*/
function get_virtual_page_url() {

    $home_url = get_home_url();
    $slash = '/';
    $virtual_page = 'custom-archive';
    $permalink = get_home_url() . $slash . $virtual_page . $slash;

    return $permalink;

}

/**
 * Register two new query variables cq, tq 
 *
 * @see http://codex.wordpress.org/WordPress_Query_Vars
 *
*/ 
add_filter( 'query_vars', function ( $qvars ) {

    $qvars[] = 'cq';
    $qvars[] = 'tq';

    return $qvars;

});

/**
 * Gets and sets the category and/or tag id as values to the custom query_vars
 *
 * Values are only set if they exists, if no value exists, the query_vars in not set
 *
 * @return $qv
*/
function get_query_vars_values() {

    $custom_query_values = get_category_and_tag_ids();
    $cat_values = $custom_query_values['category_ids'];
    $tag_values = $custom_query_values['tags_ids'];

    switch ( true ) {

        case ( $cat_values && $tag_values ):

            $qv = [
            'cq' => $cat_values,
            'tq' => $tag_values
            ]; 

            break;

        case ( $cat_values && !$tag_values ):

            $qv = [
            'cq' => $cat_values,
            ]; 

            break;

        default: 

            $qv = [];

            break;

    }

    return $qv;

}

/**
 * Build our link to the virtual page, custom-archive
 *
 * Sets the query_vars plus the respective values to the virtual page link
 *
 * @uses add_query_arg()
 * @see http://codex.wordpress.org/Function_Reference/add_query_arg
 *
 * Returns the link if we have values returned by get_query_vars_values()
 * Returns an empty string if get_query_vars_values() have no values
 * @return (string) $link 
*/
function get_create_link_virtual_page() {

    $get_virtual_page_link = get_virtual_page_url();
    $get_query_values = get_query_vars_values();

    $link = ( $get_query_values ) ? add_query_arg( $get_query_values, $get_virtual_page_link ) : '';

return $link;

}

/**
 * Wrapper function to echo the returned value set by get_create_link_virtual_page() 
 * Use display_virtual_page_link() in single.php if you need a plain link
*/
function display_virtual_page_link() {

    echo get_create_link_virtual_page();

}

/**
 * Special thanks to @Milo for the rewrite rule
 * @see https://wordpress.stackexchange.com/a/174243/31545
 *
 * Create a new rewrite rule so that Wordpress can read interpret the page and adjust the main
 * query accordingly. This page will be rewritten to the homepage. Just a note, is_home() will
 * return true on this page. To counter act this, see function below
 *
 * @uses add_rewrite_rule()
 * @link http://codex.wordpress.org/Rewrite_API/add_rewrite_rule
 *
 * You should flush your permalinks for this to take effect or add flush_rewrite_rules()
 * inside a function that is hooked to 'after_switch_theme' or `register_activation_hook`
 * Never use flush_rewrite_rules() outside these two hooks, as it is a really expensive operation
*/ 
add_action( 'init', function () {

    add_rewrite_rule( 'custom-archive/?', 'index.php?post_type=post', 'top' );

});

/**
 * is_home() will return true on this page, and so might any other condition. This is annoying
 * as all functions meant for the home page will effect this custom page if you don't also check
 * for the custom query_vars
 *
 * To avoid this, set these conditions in the main query to false. This will avoid any extract
 * code and eliminate any unexpected output
 *
 * Set a new conditional tag, $wp_query->is_custom_archive if either of the two query_vars is present
 * @link http://codex.wordpress.org/Plugin_API/Action_Reference/parse_query
*/ 
add_filter( 'parse_query', function () {

    global $wp_query;

    //Set new conditional tag
    $wp_query->is_custom_archive = false;

    if( isset( $_GET['cq'] ) || isset( $_GET['tq'] ) ) {

        $wp_query->is_single = false;
        $wp_query->is_page = false;
        $wp_query->is_archive = false;
        $wp_query->is_search = false;
        $wp_query->is_home = false;
        $wp_query->is_custom_archive = true;

    }

}); 

/**
 * Wrapper function for $wp_query->is_custom_archive
 *
 * @return (bool) true on success, false on failure
*/ 
function is_custom_archive() {

    global $wp_query;
    return $wp_query->is_custom_archive;

}

/** 
 * Use a custom template to display our posts if needed. This can be omitted if you wish
 * In this case, index.php will be used if no template is set
 *
 * @uses template_include filter
 * @link http://codex.wordpress.org/Plugin_API/Filter_Reference/template_include
 *
 * @return $template
*/ 
add_filter( 'template_include', function ( $template ) {

    // Checks if we have one of the query_vars in the URL, if so, include custom_archive.php
    if( is_custom_archive() ) {

        $template = get_stylesheet_directory() . '/custom-archive.php';

    }
    return $template;

}, PHP_INT_MAX, 2 );

/**
 * Alter the main query on the custom archive page. We need to add a tax_query and get our custom
 * query_vars and use that in our tax query to get the correct posts
 *
 * @uses pre_get_posts action
 * @link http://codex.wordpress.org/Plugin_API/Action_Reference/pre_get_posts
 *
*/ 
add_action( 'pre_get_posts', function ( $q ) {

        if ( !is_admin() && is_custom_archive() && $q->is_main_query() ) {

            if( isset( $_GET['cq'] ) && isset( $_GET['tq'] ) ) {

                $tax_query =  [
                    [
                        'taxonomy'          => 'category',
                        'terms'             => $_GET['cq'],
                        'include_children'  => false,

                    ],
                    [
                        'taxonomy'  => 'post_tag',
                        'terms'     => $_GET['tq'],
                    ]
                ];

            }elseif( isset( $_GET['cq'] ) && !isset( $_GET['tq'] )) {

                $tax_query = [
                    [
                        'taxonomy'          => 'category',
                        'terms'             => $_GET['cq'],
                        'include_children'  => false,
                    ]
                ];

            }

            $q->set( 'tax_query', $tax_query );

        }

    return $q;

});

/**
 * Gets the current page URL. Will be used to set a 404 if http://mysite.com/custom-archive
 * is visited
 *
 * @uses get_home_url
 * @return (string) $current_url
*/
function get_current_page_url() {

    global $wp;
    $trail_slash = '/';
    $current_url = home_url( add_query_arg( [], $wp->request ) ) . $trail_slash;

    return $current_url;

}

/**
 * Sets a 404 and redirects to a 404 page if http://mysite.com/custom-archive
 * is directly visited
*/ 
add_action( 'template_redirect', function () {

    global $wp_query; 

    if( !is_custom_archive() && get_current_page_url() == get_virtual_page_url() ) {

        $wp_query->set_404();
        status_header( 404 );
        nocache_headers();

    }

});

使用法

リンクをsingle.phpで表示するには、次のようにします。

<?php echo '<a href="' .get_create_link_virtual_page() . '">Custom link</a>'; ?>
4
Pieter Goosen