当サイトの記事の下部に「関連記事」というセクションがありますが、これは管理画面からチェックボックスで選択したものを表示するようになっています。この記事ではその関連記事の作り方を紹介していきます。

実装内容を確認

管理画面は以下のような画面になります。

管理画面例

関連記事の項目が追加されていて、同じカスタム投稿タイプの中から関連記事をチェックして表示させるといった内容です。

今回は仕様としてチェックされた記事側もチェックした記事を関連記事で表示させるような仕様にしています。


片方の記事の管理画面から、関連記事としてチェックすれば相互作用的に、もう一方の記事にも関連記事として設定されます。


いちいちもう一方の記事の管理画面にいって、関連記事としてチェックする面倒さを省くためにしています。

functions.phpのコード

さっそく functions.php のコードです。

コードをクリップボードにコピー
<?php
  
function add_relative_posts_fields() {
  $array = array('post','custom_post_type'); //関連記事項目を表示させたい投稿タイプ
  add_meta_box( 'relative_posts_field', '関連記事', 'insert_relative_posts', $array , 'side');
}

// 管理画面に同じカスタム投稿タイプの記事を一覧で表示させる。
add_action('admin_menu', 'add_relative_posts_fields');
function insert_relative_posts() {
  global $post;
	$get_posts = get_post_meta( $post->ID,'relative_posts',true );
	$posts     = $get_posts ? $get_posts : array();
	$current_id = get_the_id( );
  $args = array(
    'post_type' => $post->post_type,  // 別の投稿タイプの記事も表示させたい場合はここを変更する。併せて70行目も変更する。
    'posts_per_page' => -1,
    'post_status' => 'publish'
  );
  wp_nonce_field('relative-posts-key', '_relative_posts_nonce');
  $myposts = get_posts($args);  
  foreach( $myposts as $post_rel ):
    setup_postdata( $post_rel );
    if( !($current_id == $post_rel->ID) ):
      if ( in_array($post_rel->ID, $posts) ) { $check = "checked"; } else { $check = ""; } // チェックがついてるものはチェックをつけて表示
      echo '<label><input type="checkbox" name="relative_posts[]" value="'.$post_rel->ID.'" '.$check.'>'.$post_rel->post_title.'</label><br>';
    endif;
  endforeach;
  wp_reset_postdata();
}

// relative_posts[] の値を保存する
add_action('save_post', 'save_relative_posts_custom_fields');
function save_relative_posts_custom_fields($post_id) {
  if ( isset($_POST['_relative_posts_nonce']) && $_POST['_relative_posts_nonce'] ) {
    if ( check_admin_referer('relative-posts-key', '_relative_posts_nonce') ) {
      if ( isset($_POST['relative_posts']) && $_POST['relative_posts'] ) {
        // 関連記事にチェックを入れた場合。
        // まず編集中の記事の relative_posts[] の値を保存する
        update_post_meta( $post_id, 'relative_posts', $_POST['relative_posts'] );  
        $get_check_relative_posts = get_post_meta( $post_id, 'relative_posts', true ); //編集中の記事側でチェックした関連記事のidを取得
        $check_relative_posts     = $get_check_relative_posts ? $get_check_relative_posts : array();        
        // チェックした記事にはチェックした記事側にもチェックを入れる
        foreach($check_relative_posts as $id){
          $get_post_date = get_post_meta( $id ,'relative_posts', true ); // 関連記事側の relative_posts[] の値を取得
          $post_data     = $get_post_date ? $get_post_date : array();
           // 関連記事側の relative_posts[] の値に、編集中の記事のidがなかった場合に、
           // 関連記事側の relative_posts[] の値に、編集中の記事のidを追加する
          if( !(in_array($post_id,$post_data)) ){
            array_push($post_data, $post_id);
            update_post_meta( $id, 'relative_posts', $post_data );           
          }
        }
        uncheck_relative_posts($post_id,$check_relative_posts);    
      } else {
        // すべての関連記事のチェックを外した場合。
        delete_post_meta( $post_id, 'relative_posts', get_post_meta($post_id, 'relative_posts', true) );
        uncheck_relative_posts($post_id);
      }
    }
  }
}

// チェックをはずした記事はチェックをはずした記事側もチェックをはずす関数
function uncheck_relative_posts($post_id,$relative_posts = array()){
  $all_id = array();
  // 同じカスタム投稿タイプのすべての記事を取得
  $all_posts = get_posts(array(
    'posts_per_page'   => -1,
    'post_type'  => get_post_type($post_id) //16行目を変えた場合は、ここも変更する。
  ));
  foreach( $all_posts as $mypost){
    array_push($all_id,$mypost->ID);
  }
  $del_id = array_diff($all_id,$relative_posts);  //編集中の記事側でチェックした関連記事のidを除外
  $del_id = array_diff($del_id,array($post_id));  //編集中の記事のidも除外
  $del_id = array_values($del_id);
  foreach($del_id as $id){
    $get_post_date = get_post_meta( $id ,'relative_posts', true );
    $post_data     = $get_post_date ? $get_post_date : array();
    // $del_idに含まれるidの記事の relative_posts[] の値に
    // 編集中の記事のidがあった場合には、編集中の記事のidを削除する
    if( in_array($post_id,$post_data) ){
      $results = array_diff($post_data,array($post_id));
      $results = array_values($results);
      if($results){
        update_post_meta( $id, 'relative_posts', $results );
      }else{
        delete_post_meta( $id, 'relative_posts', get_post_meta($id, 'relative_posts', true) );
      }
    } 
  } 
}

?>

編集中の記事以外の記事も編集するような仕様なので、「save_post」の時のコードが長くなってしまいます。
チェックをはずす処理は一応関数化(uncheck_relative_posts関数)しました。

同じカスタム投稿タイプしか関連記事として表示しないような設定にしていますが、16行目と70行目を変更すれば、違う投稿タイプも関連記事として表示できます。

大まかな説明はコードに書いてあるとおりです。

フロント側のコード

フロントの表示コードの一例です。

コードをクリップボードにコピー
<?php
  $relative_posts = get_post_meta($post->ID,'relative_posts',true);
  if( $relative_posts ):
?>
<div class="relative-posts">
  <h4>関連記事</h4>
  <ul>
  <?php foreach( $relative_posts as $id ):?>
    <li>
      <h4><?php echo get_the_title($id);?></h4>
      <?php if(has_post_thumbnail( $id )): ?>
        <figure><img src="<?php echo get_the_post_thumbnail_url( $id, 'medium'); ?>" alt="<?php echo get_the_title($id);?>"/></figure>
      <?php endif; ?>
      <a href="<?php echo the_permalink( $id )?>">この関連記事を読む</a>
    </li>
  <?php endforeach;?>
  </ul>
</div>
<?php endif;?>

relative_postsの値があった場合のみ表示するようにしています。
relative_postsの値の中には関連記事のidが入っているので、簡単に関連記事のタイトルなどにアクセスできます。