web-dev-qa-db-ja.com

CPTパーマリンクを設定する方法としてのMeta_Query - それは良いことですか?

文学雑誌のための非常に複雑なWebサイトを作成しています。カスタム投稿タイプのIssueとEventには、投稿とは異なるパーマリンク構造が必要でした(これはbasic http://example.com/%postname% です)。

私の仕事は簡単なものではなく、たとえそれがかなりねじれたものであったとしても、私はどうにか解決策を思いつくことができました。それが理想かどうかわからないので、私はWPユーザーの意見を聞きたいと思います。彼らは私の内部よりも$wp_queryのダークハートをよく理解しています。 WP St​​ackexchangeがコードレビューの適切な場所ではない場合は、他の場所を参照してください。

私は次のことを達成する必要がありました。

  1. URLはCPTに関連する日付を反映しています - http://example.com/event/%eventyear%/%eventmonth%/%postname%http:/のように/example.com/event/%year%/%issue-num%
  2. URLの最後の部分は、 http://example.com/issue/2016/5 および http://example.com/issue/のように複製できます。 2015/5
  3. CPTにはアーカイブがあります - 2012年問題のアーカイブは http://example.com/issue/2012 にあります。
  4. URLの最後の部分はタイトル以外のもの(おそらくメタ値)から生成されるべきです。

まず、CPTを登録しました。

$cpt_args = array(
  'label' => 'Issue',
  'hierarchical' => false,
  'has_archive' => true,
  'rewrite' => array('slug' => 'issue/%issueyear%'),
);

それから、書き換えタグを登録しました。

add_action('init', function() use ($tags) {
    add_rewrite_tag("%issueyear%");
});

それから、私は `%issueyear%の私の" rewrite_tag翻訳 "をカスタムメタ値に登録しました:

$func = function($permalink, $post) {

  if (strpos($permalink, "%issueyear%")) {
    $tags_used[] = $t;
  }

  $issue_start = get_post_meta($post->ID, 't_issue_start', true);

  $old = basename($permalink);
  $new = $this->date->get_time('Y', $issue_start);
  $permalink = str_replace($old, $new, $permalink);

  return $permalink;

};

add_action('post_link', $func, 10, 2);
add_action('post_type_link', $func, 10, 2);

それから、書き換えルールを追加し、メタ値をURLに渡しました。

add_action('init', function() {
    add_rewrite_rule(
        "^issue/([0-9]{4})/([0-9]{1,})/?",
        'index.php?post_type=issue&year=$matches[1]&cibulka_key=cibulka_slug&cibulka_val=$matches[2]',
        'top'
    );
};

add_action('query_vars', function($vars) {
    $vars[] = 'cibulka_key';
    $vars[] = 'cibulka_val';
    return $vars;
});

URL http://example.com/2016/5は私にarchive.phpテンプレートを与えるでしょう。 $wp_querycibulka_keyが設定されている場合は、そこで単一のテンプレートを含めました。 include_template_with_varは私の関数です。テンプレートにパラメータを渡すことができます。

/** Archive.php */

global $wp_query;
if (isset($wp_query->query['cibulka_key'])) {
    $meta_value = $wp_query->query['cibulka_val'] . '_' . $wp_query->query['year'];
    $args = array(
        'post_type' => $wp_query->query['post_type'],
        'meta_key' => $wp_query->query['cibulka_key'],
        'meta_value' => $meta_value,
        'posts_per_page' => 1
    );
    $posts = get_posts($args);
    if (!empty($posts)) {
        $data = array(
            'id' => $posts[0]->ID,
            'post' => $posts[0]
        );
    } else {
        $data = array();
    }
    include_template_with_var('single.php', $data);
} else {
    // Do normal archive stuff
}

Archive.phpの代わりに$data['id']を含む単一の投稿テンプレートが提供されます。

:これは私の実際のコードではありません。私の質問のために、非常に単純化しました。


これは私が達成しなければならない4つのポイントすべてを達成します、しかしそれがこれに近づく正しい方法であるかどうかだけではわかりません - パフォーマンスに関して(イベントがたくさんあるでしょう)、標準など。この解決策(URLスキームの設定と同様に非常に大きな結果になるため)については、いくつかの反対意見があります。

これまでのところ、私はこれらの警告を発見しました:

  1. WPは http://example.com/issue/5 のコンテンツをarchiveと認識しますが、singleとして認識します。そのため、is_single()is_single('issue')falseを返し、body_class()の結果は混乱します。

もっと表示されたら、ここに追加します。


どうもありがとうございました。


編集1

Archive.phpから条件文を削除し、それをフィルタに置き換えました。これで警告1が解決され、テンプレートからロジックが削除されました。わーい!

// Change global `wp_query` to retrieving the post by meta query AND mark it as single (not archive)
add_action('pre_get_posts', function() {
    global $wp_query;
    if (!isset($wp_query->query['cibulka_key'])) { return; }

    switch ($wp_query->query['post_type']) {
        case 'issue':
            $meta_value =  $wp_query->query['cibulka_val'] . '_' . $wp_query->query['year'];
        break;
    }

    $wp_query->set('meta_key', $wp_query->query['cibulka_key']);
    $wp_query->set('meta_value', $meta_value);

    $wp_query->is_singular = true;
    $wp_query->is_single = true;
    $wp_query->is_archive = false;

    remove_all_actions ( '__after_loop');

});

// Use single.php rather than archive.php, if global `$wp_query` contains my custom properties
add_filter('template_include', function($template) {

    global $wp_query;
    if (!isset($wp_query->query['cibulka_key'])) { return $template; }

    $single_tmplt = locate_template('single.php');
    return $single_tmplt;

});

何らかの理由でbody_class()single-issue CSSクラスをこのように追加しませんが、それは何もありません...

add_filter('body_class', function($body_class) {
    if (is_single() && get_post_type() === 'issue') {
        $body_class[] = 'single-issue';
    }
    return $body_class;
}, 11, 1);

...修正できません。 :)

6
Petr Cibulka

それはパフォーマンスの問題なので、そのデータを別の方法で格納/取得し、一致するように別のメタフィールドを設定する必要がないことで、メタキーの使用をまったく避けることができるかもしれません。

a。出版された$post->post_date...から年を取得することができます。そのため、クエリを実行するときはdate引数を使用するだけです。

$args = array(
    'post_type' => $wp_query->query['post_type'],
    'date_query' => array( array('year' => $wp_query->query['year']) ),
    'posts_per_page' => -1
);
$posts = get_posts($args);

b。ページ属性の$post->menu_orderフィールドを使用して発行番号を設定できます。これは追加のコードなしでそれ自身のポストライティングスクリーンメタボックス(そしてさらにポストリストスクリーン上のクイック編集)を与えるという追加の利点を持つでしょう、そしてフィールドの目的によく合っています(あなたがページをするように問題を並べます)。登録時に投稿タイプにサポートを追加するか、次のようにすることもできます。

add_post_type_support('issue','page-attributes');

...それからあなたは上記のコードに従うことになるでしょう:

if (!empty($posts)) {
    foreach ($posts as $post) {
        if ($post->menu_order == $wp_query->query['cibulka_val']) {
            $data['id'] = $post->ID;
        }
    }
}

利点は、post_datemenu_orderの両方がpostsテーブルの行にあるため(したがって自動的に$postオブジェクトにもあるため)、データを一致させるためにSQLがpostmetaテーブルにアクセスする必要がないことです。知っている何千もの倍数であれば小さな利益...あなたはまだその年の何百もの投稿を得て、このようにそれらをループしている可能性があります。

その代わりに、menu_orderpost_dateを使用することもできますが、Post IDを取得するためだけのカスタムクエリを所有しています。これは本当にisの非常に効率的な方法です。それ - 本当に速いものはここにはありません。例えば:

if (isset($wp_query->query['cibulka_key'])) {
    global $wpdb;
    $query = "SELECT ID FROM ".$wpdb->prefix."posts 
              WHERE YEAR(post_date) = '".$wp_query->query['year']."' 
              AND menu_order = '".$wp_query->query['cibulka_val']."' 
              AND post_status = 'publish'";
    $postid = $wpdb->get_var($query);
    if ($postid) {
        $data['id'] = $postid;
        // if this is really needed here?
        $data['post'] = get_post($postid);
    } else {$data = array();}
    include_template_with_var('single.php', $data);
}
3
majick