WordPress は基本機能として通常の投稿と固定ページの2種類のページを作ることができますが、function.php を編集したりプラグインを使えば「カスタム投稿タイプ」というものを作ることができます。

カスタム投稿タイプの詳細は少し探せばすぐに出てくるので説明は別の記事にお任せするとして、今回お仕事でちょっと困ったことが起きたので記録しておきます。

既に数ヶ月運用が行われているサイトで、固定ページは親ページ、子ページの階層構造を持っています。

例えば親ページが companyproduct、それぞれに profileapplicatoin という子ページで、階層的に表すと

/company/profile/
/product/application/

といった感じになります。

ここまでは至って普通なのですが、そこにカスタム投稿タイプで固定ページの親ページと同じ companyproduct という名前のスラッグを設定してパーマリンクを更新すると、

/company
/product

はカスタム投稿タイプのページとして優先的に扱われるらしく、既存の固定ページの

/company/profile/
/product/application/

は無いものとして扱われ、空ページが表示されてしまいます。

さて困った・・・。調べてみると共存?ができている上でどの URL ならどちらを見に行く、という割当ができるような情報もあるのですが、少なくとも自分がいま携わっている環境ではそれがうまくいかず途方に暮れてしまいました。

ということで一番簡単な解決方法は固定ページの内容をそのままカスタム投稿タイプに同じスラッグで作り直すことです。結果的に既存の固定ページは不要になりますが、これがいちばん簡単かつ安全な方法です。

ですが色々な事情でそれができない、ということになるとかなり無理矢理な方法で表示することになります。

具体的なコードは次のとおり。

function set_page_view( $query ) {
  // ダッシュボードまたは管理パネルが表示されている、もしくはメインクエリではない場合は処理を中断
  if ( is_admin() || ! $query->is_main_query() ) return;
  // カスタム投稿の空ページは is_single() === true
  if ( $query->is_single() ) {
    class page_view {
      var $name, $ID, $child;
    }

    // 固定ページ内の親ページ「parenta」「parentb」の子ページ一覧を取得
    $pageview_parents = array( 'parenta', 'parentb' );
    $pageview = array();

    for ($i = 0; $i < count($pageview_parents); $i++) {
      $pageview[$i] = new page_view();
      $pageview[$i]->name = $pageview_parents[$i];
      $pageview[$i]->ID = get_page_by_path($pageview_parents[$i])->ID;
      $args = array(
        'orderby' => 'date',
        'order' => 'DESC',
        'post_type' => 'page',
        'child_of' => $pageview[$i]->ID,
        'post_status' => 'publish',
      );
      $pageview[$i]->child = get_pages( $args );
    }

    $request_uri = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);

    // get_pages() には親ページ情報が無いので class page_view の $name を利用
    foreach ($pageview as $pageview_items) {
      foreach ($pageview_items->child as $pageview_child) {
        // 子ページ一覧の URL と リクエスト URL が合致したら強制的に $query->set してテンプレートファイルも指定。
        $tmp_uri = '/' . $pageview_items->name . '/' . $pageview_child->post_name;
        if ($request_uri === $tmp_uri || $request_uri === $tmp_uri . '/') {
          $query->set( 'post_type', 'page' );
          $query->set( 'page_id', $pageview_child->ID );
		  // テンプレートファイルも強制的に設定
          add_filter( 'template_include', 'set_page_template', 99 );
          function set_page_template( $template ) {
            $new_template = locate_template( array( 'page.php' ) );
            if ( !empty( $new_template ) ) return $new_template;
            return $template;
          }
          $queryset = true;
          break;
        }
      }
      if ($queryset) break;
    }
    return;
  }
}
add_action( 'pre_get_posts', 'set_page_view' );

はい。全くスマートではないですね。

今回は対象の固定ページが多く自動的に URL を取得するために get_pages() を使いたかったので pre_get_posts アクションフック内で判定と $query やテンプレートの設定を行いましたが、本来なら parse_request アクションフック内でそれらの処理を行ったほうが良いかもしれません。というのも pre_get_posts 内では既に is_single()is_page() の判定が行われているので、固定ページ(page)の内容を表示しているのに内部的には投稿(single)扱いになってしまうのです。

というか最初にちゃんと先のことも見据えて設計していればこんな無理矢理な方法に頼る必要も無いわけで、長く運用できる Web サイトを目指すならしっかりと URL や階層構造を理解した上で作りましょうね、という教訓でした。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください