カスタム投稿ページのパーマリンクにカスタムフィールドの値を使用する方法

こんにちは、コバヤシです。
今回はWordPressの投稿ページのパーマリンクにカスタムフィールドの値を使用する方法について書きます。

よくある方法

今回は、/hogehoge/【カスタムフィールドで設定した値】で投稿ページを表示したいと思います。
(カスタムフィールドのフィールド名はcodeとします)

ネットで検索すると見つかるのが、以下の方法です。

functions.php

<?php

function custom_rewrite_rule()
{
    add_rewrite_rule( '^hogehoge/([0-9]+)/?$', 'index.php?post_type=xxxxx&code=$matches[1]', 'top' );

}
add_action('init', 'custom_rewrite_rule');

function custom_query_vars($vars)
{
        // コードをGETで読めるように設定
    $vars[] = 'code';

    return $vars;
}
add_filter('query_vars', 'custom_query_vars');

function custom_template_include($template)
{
        // コードがあったら指定した投稿ページのテンプレートファイルを読み込むようにする
    if (get_query_var('code')) {
        $include_template = locate_template(['hoge.php']);
        if (!empty($include_template)) {
            return $include_template;
        }
    }
    return $template;
}
add_filter('template_include', 'custom_template_include');

function custom_pre_get_posts( $query ) {

    if ( ! is_admin() && $query->is_main_query() ) {
            // codeがあったらカスタムフィールドの値を条件に取得するようにする
            if ( get_query_var('code')) {
              $query->set( 'meta_key', 'code' );
              $query->set( 'meta_value', get_query_var('code') );
            }
        }
    }

    return;
}
add_action( 'pre_get_posts', 'custom_pre_get_posts' );

add_rewrite_ruleで、マッチしたコードをGET値として渡して、そのGETの値があったら、クエリの条件の追加と呼び出すテンプレートファイルの変更を行っています。

上記の方法の問題点

上記の方法でも詳細画面は表示されますが、問題があります。
それは、WordPress的にはこのページはアーカイブページとして認識されているということです。
All in One SEOで設定された内容が反映されず、そのままではアーカイブ用のタイトルなどが表示されてしまいます。
functions.phpでフックを書けば変更できますが、All in One SEOの設定画面で入力した値をそのまま使いたい。。。

解決方法

試行錯誤の結果、以下の方法で解決できました。

functions.php

<?php

function custom_rewrite_rule()
{
    add_rewrite_rule( '^hogehoge/([0-9]+)/?$', 'index.php?post_type=xxxxx&code=$matches[1]&p=1', 'top' );

}

function custom_pre_get_posts( $query ) {

    if (! is_admin() && $query->is_main_query()) {
            // codeがあったらカスタムフィールドの値を条件に取得するようにする
            if (get_query_var('code')) {
              $query->set( 'meta_key', 'code' );
              $query->set( 'meta_value', get_query_var('code') );
              $query->set('p', null); // post_idによる検索を削除
            }
    }

    return;
}
add_action( 'pre_get_posts', 'custom_pre_get_posts' );

ミソはadd_rewrite_ruleのURLにP=1を付けることと、pre_get_postsでp(post_id)による条件検索を削除することです。
p=1を付けることでWordPressに投稿ページのアクセスとして認識させることができるので、後はpre_get_postsでpost_idで検索しないように削除してあげるだけでOKです。
WordPress的に投稿ページとして認識されているので、template_includeのフィルターで読み込むテンプレートを変更する必要もありません。

まとめ

書いてしまうと簡単なことですが、この方法にたどり着くまでに少し時間を要しました。
WordPressはちょっとしたカスタマイズのつもりが、手がかかることが多々あります。
しかし、その分だけ理解が深まり、より柔軟なサイト構築が可能になります。
これからも様々なカスタマイズに挑戦し、知識を付けていきたいと思います。