WordPress でそのサイトの仕様や特性に合ったカスタマイズを行っていると、どうしたら実現できるんだろう?という問題に遭遇することが度々あります。

今回、ある固定ページのメインクエリの中で、カスタム投稿タイプAの投稿の内容を WP_Query を使ったサブクエリで出力し、そのループの中で、各投稿を関連付けたカスタム投稿タイプBの投稿を抽出して、そのパーマリンクを出力したいという事案が発生しました。つまり、WP_Query によるサブクエリのループの中に、別の WP_Query によるサブクエリを入れ子で使うということです。

カスタム投稿タイプBには、Advanced Custom Fields プラグインの“関連”フィールドタイプを使って、カスタム投稿タイプAの投稿を指定するようにしています。

そもそも逆で、カスタム投稿タイプAからカスタム投稿タイプBの投稿を指定するようにすれば、WP_Query を入れ子にしなくても、普通にカスタム投稿タイプAの投稿の内容を出力する中で、カスタム投稿タイプBの投稿のパーマリンクを出力できます。

しかし今回の案件は、カスタム投稿タイプBからカスタム投稿タイプAの投稿を指定する方が、運用する上で編集が簡単でミスが少ないと考え、テーマ側で何とか実現できないかと探ってみることにました。

カスタム投稿タイプBに設定した関連フィールドは、カスタム投稿タイプAの投稿の中から複数を選択することができ、その返り値のフォーマットは投稿IDを取得するようにしています。

で、いきなり結論 w

<?php
// カスタム投稿タイプAの投稿を出力するクエリ
$args = array(
  'post_type' => 'custom-post-type-a', // カスタム投稿タイプAのスラッグ
  'post_status' => 'publish'
);
$my_query = new WP_Query( $args );
if ( $my_query -> have_posts() ):
  while ( $my_query -> have_posts() ): $my_query -> the_post();

   (カスタム投稿タイプAの投稿のタイトル等の内容を出力する記述)

    $post_id = get_the_ID();  // この投稿の投稿ID(※)

    $temp = $my_query;  // 現在(カスタム投稿タイプA)のクエリを一旦格納
    $my_query = null;

    // カスタム投稿タイプBの投稿を出力するクエリ
    $args = array(
      'post_type' => 'custom-post-type-b', // カスタム投稿タイプBのスラッグ 
      'post_status' => 'publish',
      'meta_query' => array(
        array(
          'key' => 'link_field', // カスタム投稿タイプBのカスタムフィールド
          'value' => $post_id, // カスタム投稿タイプAの投稿ID(※)
          'compare' => 'LIKE' // 複数選択としているため'LIKE'とした
        )
      )
    );
    $my_query = new WP_Query( $args );
    if ( $my_query -> have_posts() ):
      while ( $my_query -> have_posts() ): $my_query -> the_post();

        echo '<p><a href="'. get_the_permalink(). '">'. get_the_title(). '</a></p>'. "\n";
	
      endwhile;
    endif;

    $my_query = null;
    $my_query = $temp;  // 元(カスタム投稿タイプA)のクエリに戻す

  endwhile;
endif;
wp_reset_postdata();
?>

カスタム投稿タイプAのクエリの途中で $temp = $my_query; として現在のクエリを一旦格納しておき、カスタム投稿タイプBのクエリを走らせ、格納しておいたクエリに戻しています。

このとき、入れ子にしたクエリを wp_reset_postdata(); でリセットしてしまうと、元のクエリもリセットされてしまいますので、リセットは最後のメインクリに戻すときだけにしています。