したがって、カスタム投稿タイプの親としてページを取得する方法については、何百万ものスニペットがあります。
しかし、その逆は疑わしいようです。 WordPressのすべてのものは技術的には「投稿」であるため、これは些細なことになるでしょう。しかしそうではありません。
これまでのところ、私は持っています:
add_filter( 'page_attributes_dropdown_pages_args',
function( $dropdown_args, $post = null ) {
$dropdown_args['post_type'] = 'portal';
return $dropdown_args;
} );
そして確かに、ページは私が親としてportal
タイプの投稿を選択することを可能にします。それらはすべて404ですが、URLはwordpress内で正しく登録されているようです。
どうすればワードプレスにこの階層を理解させ、両親として投稿があるページをロードさせることができますか。
WordPressはURLをデータベースクエリに変換できるようにするために書き換えルールのセットを使用します。
ページのURLを扱う正規表現は非常に一般的です、IIRCそれは(.+.?)/?
のようなものです、それは実際には他のルールによってすべてが一致していないように一致します。
example.com/my-portal/sample-page
のようなURLでは 'my-portal'の部分がCPTでsample-page
がページであることをregexで区別することができないため、このような理由であなたのケースでうまくいく書き換えルールを書くことは不可能です。
ネスティングのレベルが多ければ、物事はより複雑になります:my-portal/my-portal-child/sample-page
。
この種のURLを処理するために、WordPressは get_page_by_path()
functionを使用します。ページURLを/
で展開し、ページスラッグを取得してから、それらのスラッグを持つすべての pages についてデータベースに問い合わせます。
たとえば、スラッグが"sample-page"であるページがあり、そのページの親としてCPT "my-portal" WordPressが呼び出すとします。
get_page_by_path('my-portal/sample-page')
しかし、 は を返さず、親が別の page であるスラッグ 'sample-page'を持つスラッグ 'my-portal'を持つページを探します。そのページは存在しないので、404エラーが発生します。
ただし、 get_page_by_path()
は3番目の引数として投稿タイプの配列を受け入れます。これをarray('page', 'portal')
に設定すると、関数はページを正しく見つけることができます。
そのため、(上記のように取得した)ページIDをWP query varsに手動で設定することで問題を解決できます。
'parse_request'
フックは以下のスコープに最適です。
$wp
オブジェクトのインスタンスをフックバックコールバックに渡しますコード:
add_action('parse_request', function ($wp) {
// only if WP found a page
if (isset($wp->query_vars['pagename']) && ! empty($wp->query_vars['pagename'])) {
$page = get_page_by_path( // let's find the page object
$wp->query_vars['pagename'],
OBJECT,
array('page', 'portal') // we need to set both post types
);
if ($page instanceof WP_Post) { // if we find a page
unset($wp->query_vars['pagename']); // remove pagename var
$wp->query_vars['page_id'] = $page->ID; // replace with page_id query var
}
}
});
このコードをOPのフィルタと組み合わせることで、必要なものはすべて揃います。
コードはネストされた階層ポータルでも機能することに注意してください。
そのため、@ gmazzapの answer は正しい方向へのポイントでした。しかし、何らかの理由で、var_dump($wp->query_vars)
は1つのキーattachment
を持つ配列を表示しているだけで、まったく役に立ちませんでした。
しかし、 did にすれば、もう少しトリックを使用してこれを達成することができます。
以下のスクリプトは、階層が正しい場合にのみページを取得します。つまり、各スラッグの親は適切なスラッグ(もちろん、最初のスラッグの場合は0
)と投稿を突き合わせます。
同様に、ワードプレスが扱うべき特別なページを意味するという理由だけで、0または1のスラッグを持つパスをスキップします(また、ページ階層の点では問題になりません)
いいえ、クエリはあまり最適化できません。これは、MySQLが階層データモデルで望んでいるような再帰的な選択/結合をサポートしていないという事実によるものです。
function get_page_by_slug_path( $path ) {
global $wpdb;
global $table_prefix;
$slugs = explode(
'/',
preg_replace(
'~\/+~',
'/',
preg_replace(
'~^\/*(.+?)\/*$~',
'$1',
$path
)
)
);
# Skip if slugs length is 1 or 0, of course.
# A single 'slug' URL may mean something completely different, so
# we'll bank on WordPress knowing what to do with it.
if( count( $slugs ) < 2 ) {
return;
}
$parents = [ 0 ];
foreach( $slugs as $slug ) {
$sql = $wpdb->prepare(
"SELECT ID FROM ${table_prefix}posts WHERE post_name='%s' AND post_parent IN ("
. implode( ',', $parents )
. ")",
$slug
);
$results = $wpdb->get_results( $sql );
if( count( $results ) === 0 ) {
return null;
}
$parents = array_map(
function( $elem ) {
return $elem->ID;
},
$results
);
}
if( count( $parents ) > 1 ) {
trigger_error( E_USER_WARNING, "Multiple IDs for this page slug: "
. implode( ', ', $parents ) );
}
return intval( $parents[0] );
}
add_action('parse_request', function ($wp) {
$path = parse_url(
"http://" . $_SERVER['HTTP_Host'] . $_SERVER['REQUEST_URI'],
PHP_URL_PATH
);
$pageID = get_page_by_slug_path( $path );
if( $pageID ) {
$wp->query_vars = [ 'page_id' => $pageID ];
}
});