web-dev-qa-db-ja.com

ページ親としてのカスタム投稿タイプ

したがって、カスタム投稿タイプの親としてページを取得する方法については、何百万ものスニペットがあります。

しかし、その逆は疑わしいようです。 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内で正しく登録されているようです。

どうすればワードプレスにこの階層を理解させ、両親として投稿があるページをロードさせることができますか。

5
Qix

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' フックは以下のスコープに最適です。

  • uRLが解析された後に実行されます
  • それはあなたが問い合わせvarsを設定するのに使用できる$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のフィルタと組み合わせることで、必要なものはすべて揃います。

コードはネストされた階層ポータルでも機能することに注意してください。

7
gmazzap

そのため、@ 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 ];
  }
});
0
Qix