文学雑誌のための非常に複雑なWebサイトを作成しています。カスタム投稿タイプのIssueとEventには、投稿とは異なるパーマリンク構造が必要でした(これはbasic http://example.com/%postname% です)。
私の仕事は簡単なものではなく、たとえそれがかなりねじれたものであったとしても、私はどうにか解決策を思いつくことができました。それが理想かどうかわからないので、私はWPユーザーの意見を聞きたいと思います。彼らは私の内部よりも$wp_query
のダークハートをよく理解しています。 WP Stackexchangeがコードレビューの適切な場所ではない場合は、他の場所を参照してください。
私は次のことを達成する必要がありました。
まず、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_query
にcibulka_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スキームの設定と同様に非常に大きな結果になるため)については、いくつかの反対意見があります。
これまでのところ、私はこれらの警告を発見しました:
archive
と認識しますが、single
として認識します。そのため、is_single()
、is_single('issue')
はfalse
を返し、body_class()
の結果は混乱します。もっと表示されたら、ここに追加します。
どうもありがとうございました。
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);
...修正できません。 :)
それはパフォーマンスの問題なので、そのデータを別の方法で格納/取得し、一致するように別のメタフィールドを設定する必要がないことで、メタキーの使用をまったく避けることができるかもしれません。
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_date
とmenu_order
の両方がposts
テーブルの行にあるため(したがって自動的に$post
オブジェクトにもあるため)、データを一致させるためにSQLがpostmeta
テーブルにアクセスする必要がないことです。知っている何千もの倍数であれば小さな利益...あなたはまだその年の何百もの投稿を得て、このようにそれらをループしている可能性があります。
その代わりに、menu_order
とpost_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);
}