作成日:2021/04/12 更新日:2022/09/01

#11 カスタム投稿機能を使う

投稿ページ機能を拡張した「カスタム投稿タイプ」

Wordpressには「固定ページ」と「投稿ページ」がありますが、投稿ページを拡張したカスタム投稿という機能があります。

上記の例では「chef」と「cource」という名前のカスタム投稿タイプがある状態です。
WordPressに最初からある機能「投稿ページ」には、前回紹介したような「カスタムフィールド」という機能を追加できます。 カスタム投稿は投稿ページとは違う新たな投稿ページの仕様(デザイン・レイアウト)を作るための機能です。
自分の好きな項目を設定することで自由な一覧・詳細ページが作れます。
■サイトを更新管理する方が、HTMLの知識がない。
■標準の投稿とは別の投稿フォーマットを作りたい。
このような場合にカスタム投稿は便利です。
最初に、どんなページ構成にするのかという`設計`がなにより大事です

カスタム投稿タイプの作り方

  1. functions.phpにカスタム投稿タイプを設定
  2. 管理画面で記事を作成
  3. カスタム投稿タイプ用のテンプレートファイルを作る
  4. cssを記述

functions.phpで設定

まずfunctions.phpに、カスタム投稿ページでどんな項目を入れたいかを設定します。

/**
 *
 * カスタム投稿タイプの登録
**/
function create_post_type()
{
  $labels = array(
    'name'          => 'CHEF',
    'singular_name' => 'CHEF',               /*nameと一緒で良い */
    'menu_name'     => 'シェフ',              /*メニュー項目の名前 */
    'all_items'     => 'シェフ一覧',           /*メタボックスの見出し */
    'add_new'       => 'シェフを追加',         /*管理画面ナビメニュー名 */
    'add_new_item'  => '新規シェフ投稿を追加',  /*見出しとボタンの名前 */
  );
  $args = array(
    'labels'        => $labels,
    'public'        => true,    /* 投稿タイプを公開にする */
    'hierarchical'  => true,    /* 階層をもたせる */
    'has_archive'   => true,    /* アーカイブ(一覧)を有効にする(デフォルトはfalse)*/
    'show_in_rest'  => true,    /* falseだとAPIで出力されない */
    'menu_position' => 3,       /* 管理画面の左サイドメニューでの表示順 */
    'menu_icon'     => 'dashicons-coffee',   /* アイコンを選びます */
    'supports'      => ['title', 'editor', 'thumbnail', 'excerpt', 'author', 'custom-fields', 'page-attributes'],
    'taxonomies'    => ['chef_cat']
  );
  register_post_type('chef', $args);
}
add_action('init', 'create_post_type');
解説
関数の作成
まず関数のオプションの設定を作成します。配列で必要なオプションを入れていきます。

function create_post_type() {}
自由に関数名をつけましょう。

'labels' => $labels,
ラベル関係はオプションが多いので、更に連想配列を変数$labelsに入れて分けています。
管理画面での表示名などを設定していきます。
調べると=> __('CHEF')という書き方も見られますが、ロケール(多言語化)に応じて自動翻訳してくれる関数の書き方なので、いったん普通の書き方で大丈夫です。

'menu_icon' => 'dashicons-coffee',
このページでいろんなiconを選択できます。WordPress.org

'supports' => ['title', 'editor', 'thumbnail', 'excerpt',.....],
管理画面の記事作成ページで表示するウィジェットを指定しています。必要なものを適宜入れましょう。
hierarchicalをtrueにしたらpage-attributesを入れましょう。

'show_in_rest' => false,
この設定をtrueにすると、記事作成画面が新エディタ(グーテンベルク)で表示されます。初期値がfalseなので旧エディタのままで良い場合は記述しなくても良いです。

その他の設定はこちら Codex 日本語版

実行の設定

register_post_type('カスタム投稿タイプ名', 'パラメーター')
カスタム投稿タイプ名とパラメーター(設定値を入れている変数)を登録します。

add_action('フックされるアクション名', '実行する関数');
WordPressがWebサイトを表示させるため順次、特定の処理(アクション)をしていきます。そのアクションにフックさせ、登録している関数を実行します。
※フックに関しては後ほど詳しく説明します。

initはユーザーのログイン状態がどうなっているかを参照しているタイミングです。ここでカスタム投稿タイプを登録した関数 create_post_type() を実行します。

ファイル関係はこのようになります。
functions.phpを設定した段階で、「設定」→「パーマリンク設定」を表示し、何も変更せずに「変更を保存」ボタンをクリックしましょう。ページを表示させる際に必要な手順となります。

管理画面のダッシュボードの左のメニューのアイコンから新規記事を作成します。
functions.phpで作った関数の内容が、この図のように表示されます。

カスタムフィールドを追加する

これに前回覚えたカスタムフィールドで名前と画像のフィールドを追加しましよう。

フィールドグループからシェフというグループを作成し、図の通りのフィールドを追加しましょう。
chef_imageの「返り値のフォーマット」は画像 URLにしておきましょう。
位置のタブはCHEF投稿に等しい投稿タイプに設定します。

記事を追加

管理画面から「シェフを追加」をクリックし、新規記事を作ります。
記事の内容は適当に書いてみましょう。
写真はここからダウンロード しましょう。

シェフの記事を作ります。このような表示になります。

コピペ用文章 (青空文庫 ドグラ・マグラより)
タイトル3名分:「Italian chef」「japanese chef」「french chef」
本文:
私がウスウスと眼を覚ました時、こうした蜜蜂の唸るような音は、まだ、その弾力の深い余韻を、私の耳の穴の中にハッキリと引き残していた。
それをジッと聞いているうちに……今は真夜中だな……と直覚した。そうしてどこか近くでボンボン時計が鳴っているんだな……と思い思い、又もウトウトしているうちに、その蜜蜂のうなりのような余韻は、いつとなく次々に消え薄れて行って、そこいら中がヒッソリと静まり返ってしまった。

chef_name: 「下北 太郎」「大橋 太郎」「下北 花子」
これをもとに3人分のシェフ情報を追加してみましょう。

テンプレートを作成する

single-{カスタム投稿タイプ名}.phpで作成します。
ここの例ではregister_post_type('chef', $args)chefと設定したとおりsingle-chef.phpとなります。

<?php
get_header();
?>
<div class="contents_wrap">
<main class="main">
  <?php if( have_posts() ): while( have_posts() ): the_post(); ?>

  <div class="chef">
      <div class="chef__img">
          <?php if( get_field('chef_image') ): ?>
            <div>
              <p><img src="<?php echo get_field('chef_image'); ?>"></p>
            </div>
          <?php endif; ?>
      </div>
      <div class="chef__txt">
          <h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
          <?php the_content(); ?>
          <h2><?php echo get_field('chef_name'); ?></h2>
      </div>
  </div>

  <?php endwhile; endif; ?>
</main>
</div>

<?php
get_footer();

テンプレートファイルは見づらいですが、図解するとこんな感じです。

CSSの設定

.mainの中に次のcssを追記しましょう。

.main{
  .sidebar{
    ・・・
  }
  /* 以下を追加 */
  .chef{
    display:flex;
    margin:10px;
    font:normal 0.9em/2.4 serif;
    &__img{
      flex:1;
      margin-right:2em;
    }
    &__txt{
      flex:1;
      h1{
        font-size:1.8em;
        a{
          color:v.$mainColor;
        }
      }
      h2{
        width:100%;
        display:inline-block;
        text-align:right;
        color:v.$mainColor;
      }
    }
  }
  /* ここまで */
}

表示してみましょう

表示はこんな感じです。

もし表示されない場合は、設定 > パーマリンクの設定 を何も変更せずに[変更を保存]をクリックしましょう。

カスタム投稿タイプ一覧を表示してみましょう

single-chef.phpに追記して、カスタム投稿タイプ記事に、他のシェフの一覧を出してみましょう。まずはテンプレートに一覧を表示する部分を追記します。

<?php
get_header();
/* 以下追加 */
$args = array(
  'post_type'    => 'chef',  /* 表示させたいカスタム投稿タイプ名 */
  'numberposts'  => 3,       /* 3件表示(デフォルトは5件) */
);
$the_query = new WP_Query($args); /* サブクエリでデータを取得し変数(インスタンス)に格納 */
?>

<div class="contents_wrap">
<main class="main">
  <div class="chef">
    ・・・カスタム投稿記事を表示している箇所
  </div>
</main>


<section class="list">
  <div class="list__item">
    
    <?php if( $the_query->have_posts() ): ?>
        <?php while( $the_query->have_posts() ): $the_query->the_post();?>
            <div>
              <a href="<?php the_permalink(); ?>">
                <div class="list__item-image" style="background-image: url(<?php the_post_thumbnail_url('medium');?>)"></div>
                <?php the_title(); ?>
                <h3><?php echo get_field('chef_name'); ?></h3>
              </a>
              <p><?php the_excerpt(); ?></p>
            </div>
        <?php endwhile; ?>
        
        <?php wp_reset_postdata(); ?>
        <?php else : ?>
        <p>関連アイテムはまだありません。</p>
    <?php endif; ?>
  </div>
</section>


</div>

<?php
get_footer();

サブクエリを表示させるWP_Query()とは


$the_query = new WP_Query($args);
WordPressはリクエストされたURLに対してデータベースに接続し、画面を表示するデータを自動で取得します(メインクエリ)。 そのデータをループさせることをメインループと呼び、標準の投稿ページやカテゴリーページなどを表示する際に使います。

WP_Query()クラスはメインの条件とは異なる、例えばあるページにカスタム投稿の一覧を表示したいなどのサブクエリをループ (サブループ)させるための方法です。サブクエリは表示させたいデータ取得の条件の指示を引数$argsに指定し、クエリを発行します。
サブクエリ説明
また必要な知識として、WordPressはページを表示するときに、対象の記事データのすべての情報を$wp_queryというグローバル変数にデータを格納しています。 各ループ毎のひとつひとつの記事データは、$postというグローバル変数に格納され、 $post->IDのようにIDプロパティを指定するなどして様々なデータを出力できます。
※グローバル変数: どのファイル(コード)からでもアクセスできるデータのこと。

サブループを終了すること


<?php wp_reset_postdata(); ?>
endwhileループ終了後に必ずwp_reset_postdata()でサブループ内の$postをリセットして、メインループのためにグローバル変数$postを復元しておきます。

get_posts()関数 と WP_Query()クラスの違い

取得できる情報量の違いで、get_posts()よりWP_Query()のほうがは投稿データの情報を少なくしてあります。単純な一覧表示などはget_posts()で十分です。WP_Query()はより多くのパラメーターが利用でき複雑な処理ができます。 軽量な処理を求められるシビアな環境でなければWP_Query()を使って良いかと思います。
▼解説
get_posts()関数

$args = array(
  'post_type'    => 'chef',
  'numberposts'  => 3,
);
$my_post = get_posts($args);
var_dump($my_post);  /* 取得データを見てみよう */

foreach ( $my_post as $post ): setup_postdata( $post );
・・・略
endforeach; wp_reset_postdata();
・単に投稿記事のデータのみ取得したい場合
・配列をforeach()でループする
・テンプレートタグを使えるようにするため、ループ内にsetup_postdata($post)関数でデータをセットする必要がある

WP_Query()クラス

$args = array(
  'post_type'    => 'chef',
  'numberposts'  => 3
);
$the_query = new WP_Query($args);
var_dump($the_query);  /* 取得データを見てみよう */

if( $the_query->have_posts() ): while( $the_query->have_posts() ): $the_query->the_post();
・・・略
endwhile; endif; wp_reset_postdata();
・表示されているのデータ以外の情報(SQL文、is_singleなど)も取得したい場合
・連想配列(オブジェクト)をwhile()でループする
・have_posts()でデータの有無をチェックし、the_post()でカウントと値($post)の変更を行い、ループを実行し続けます
共通
・引数$argsに投稿を取得するための条件(パラメーター)を設定します。

※WP_Queryインスタンスは$the_queryという名前の変数に代入しています。ループ内部で$postsが動いているので、上書きしないように別の名前をつけましょう。

get_posts()
WP_Query()

CSSの調整


.main{
  いろいろ
}
.list{
  margin:20px auto;
  &__item{
    display:flex;
    @include v.mq('max','lg'){
      display:block;
    }
    div{
      flex:3;
      @include v.mq('max','lg'){
        margin-top:40px;
      }
      p{
        display:block;
        margin-top:20px;
        width:90%;
      }
    }
    &-image{
      width:300px;
      height:300px;
      margin:10px 0;
      background-repeat:no-repeat;
    }
  }
}
カスタム投稿一覧の表示が確認できます

http://localhost:8003/chef/georges/ でアクセスするとこのような表示になります。

#12 カスタムタクソノミー機能を使う

カスタムタクソノミーについて

カスタム投稿タイプを設定してみましたがカテゴリーの機能を追加すればもっと便利になります。

標準の「投稿」にはカテゴリータグが標準機能として付いてきますが、 カスタム投稿タイプを作った場合は、標準機能以上の分類をしたい場合がほとんどのため、自分でカテゴリーやタグを作ることが多いです。

このときのカテゴリータグのことをカスタム タクソノミーと呼びます。 タクソノミーとは分類のことです。標準についているカテゴリー・タグもタクソノミーと呼べますが、 標準タクソノミーとは呼ばれません。あくまでカテゴリー・タグという名称になります。カスタム投稿の場合はカスタムタクソノミーと呼びます。
サブクエリ説明

カスタムタクソノミーの作り方

  1. functions.phpのカスタム投稿タイプ内にカスタムタクソノミーを設定
  2. 管理画面でカスタムタクソノミーを登録
  3. カスタム投稿タイプ用のテンプレートにカスタムタクソノミーを記述
  4. cssを記述

functions.phpの設定

今回はシェフの個人ページに料理ジャンル在籍店舗の2つのカテゴリを追加したいと思います。
入れる箇所はカスタム投稿タイプの登録をするcreate_post_type()関数の中に追記します。

/**
 *
 * カスタム投稿タイプの登録
**/
function create_post_type()
{
  $labels = array(
    ・・・省略
  );
  $args = array(
    ・・・省略
  );
  register_post_type('chef', $args);

  /* 追加ここから */
  /**
  *
  * カスタムタクソノミー設定
  **/
  /* 1つ目 */
  register_taxonomy(
    'food',  /* カスタムタクソノミー名を決める  */
    'chef',  /* 設定する対象のカスタム投稿タイプ */
    array(
      'label'             => '料理ジャンル',   /* カスタムタクソノミーラベル名 */
      'public'            => true,           /* 使用可能にします */
      'show_ui'           => true,           /* 管理画面上に編集画面を表示 */
      'hierarchical'      => true,           /* 階層を持たせるかどうか */
      'show_admin_column' => true,           /* 管理画面のカスタム投稿の一覧表示画面にタクソノミーを表示 */
      'show_in_rest'      => true            /* 記事制作のブロックエディタに表示 */
    )
  );
  /* 2つ目 */
  register_taxonomy(
    'shop',  /* カスタムタクソノミー名を決める  */
    'chef',  /* 設定する対象のカスタム投稿タイプ */
    array(
      'label'             => '在籍店舗',
      'public'            => true,
      'show_ui'           => true,
      'hierarchical'      => true,
      'show_admin_column' => true,
      'show_in_rest'      => true
    )
  );
  /* 追加ここまで */
}
add_action('init', 'create_post_type');
▼解説

register_taxonomy('food', 'chef', $args);
register_taxonomy()の引数は(タクソノミーの名前, 対象のpostや投稿タイプ名, パラメータ); となります。
第一引数:タクソノミーの名前
32文字以内で好きな名前をつけましょう。予約語は使えませんので確認(関数リファレンス しましょう。URLや管理画面で表示するときなどに使います。一度ここの名称を設定して管理画面で登録したあとここの名称を変更してしまうと、データが消えてしまうので気をつけましょう。
第二引数:対象のpostや投稿タイプ名
カスタムタクソノミーを使う投稿ポストタイプを指定します。複数ある場合は'chef'array('chef', 'typeA', 'typeB', 'typeC')とします。
第三引数:パラメータ
各種項目の設定をします。色々ありますので確認(関数リファレンス してみましょう。
設定と実際の画面の関係はこのようになります。

管理画面から登録

料理ジャンルにはFrench, Italian, 日本を、 在籍店舗は東京, 大阪, 福岡を入れてみましょう。


名前とスラッグを入れましょう。


メニューに設定したカスタムタクソノミーが表示されました。
それぞれ3人のシェフに設定してみましょう。

カスタム投稿タイプ、カスタムタクソノミーのテンプレートについて


カスタム投稿やカスタムタクソノミーのテンプレート関係はこの様になっています。
カスタムタクソノミーで作られた要素の一つ一つのことはタームという名称で呼ばれます。 この例では、タクソノミー名「shop」、管理画面で登録したスラッグ「tokyo, osaka, fukuoka」がタームとなります。
シェフの一覧を表示したい場合はarchive-chef.phpで良いでしょう。
仮にカスタムタクソノミー「在籍店舗」の一覧を表示したい場合はtaxonomy-shop.phpというテンプレートを作ります。
しかし、複数のカスタムタクソノミーがあった場合でも、テンプレートが同一で良い場合は、taxonomy.phpでも良いでしょう。
表示させるURL
カスタム投稿タイプ  :https://exsample.com/カスタム投稿タイプ名/投稿スラッグ/
カスタムタクソノミー :https://exsample.com/タクソノミー名/ターム/

カスタム投稿タイプ一覧「archive-***.php」

一覧を出す場合はarchive-{カスタム投稿タイプ名}.phpでテンプレートファイルを作成します。
カスタム投稿タイプ「chef」の投稿一覧ページを作成し、カスタムフィールドや、アイキャッチ画像、カスタムタクソノミーを表示してみました。

<?php
get_header();
$args = array(
  'post_type' => 'chef'
);
$the_query = new WP_Query($args);
?>

<div class="contents_wrap">
<main class="main">

  <?php if( $the_query->have_posts() ): ?>
  <?php while( $the_query->have_posts() ): $the_query->the_post(); ?>
  <section class="archive-chef">
    <h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
    <time datetime="<?php the_time('Y-m-d'); ?>"><?php the_time('Y.m.d'); ?></time>

    <p>■カスタムフィールド:      <?php the_field('chef_name'); ?></p>
    <p>■カスタムフィールド:      <?php echo wp_get_attachment_image( get_post_meta(get_the_ID(), 'chef_image', true), 'large' ); ?></p>
    <p>■アイキャッチ画像:       <?php echo get_the_post_thumbnail(get_the_ID(), 'thumbnail'); ?></p>
    <p>■カスタムタクソノミーfood:<?php echo get_the_term_list(get_the_ID(), 'food'); ?></p>
    <p>■カスタムタクソノミーshop:<?php echo get_the_term_list(get_the_ID(), 'shop'); ?></p>

    <div>■本文:<?php the_excerpt(); ?></div>
  </section>
  <?php endwhile; ?>
  <?php wp_reset_postdata(); ?>
  <?php endif; ?>

</main>
</div>

<?php
get_footer();

.contents_wrap{
  ・・・省略
  .list{
    ・・・省略
  }
  /* ここから追加 */
  .archive-chef{
    h1{
      margin:40px 0 0;
    }
    p{
      a{display:inline;}
    }
  }
  /* ここまで */
}
カスタムフィールドの画像、カスタムタクソノミーを取得する方法

get_post_meta( 投稿ID, カスタムフィールド名, true )
get_post_meta()でカスタムフィールドなどのメタデータが取得できます。
カスタムフィールドで設定した「画像」を表示したい場合は、まず画像IDを取得します。
カスタムフィールド名「chef_image」は、管理画面のカスタムフィールドで設定した「フィールド名」です。


wp_get_attachment_image(画像ID, サイズ)
続いてテンプレートタグwp_get_attachment_image() はget_post_meta()で取得した画像IDのデータでimg要素(HTML)を生成します。
サイズはthumbnail(150px), medium(300px), large(1024px), fullがあります。
※リンク画像にしたい場合はwp_get_attachment_link() を使いましょう。


get_the_term_list()
は、カスタムタクソノミーの値がハイパーリンク付きで出力されます。

http://localhost:8003/chef/でアクセスするとこのようになります。
cssで調整すればオリジナルの一覧ページができますね。

カスタムタクソノミー一覧「taxonomy.php」

カスタムタクソノミーごとの一覧ページを表示させることが出来ます。

<?php
get_header();

$page_object = get_queried_object();
$args = array(
  'post_type'      => 'chef',
  'taxonomy'       => $page_object->taxonomy,
  'term'           => $page_object->slug,
  'posts_per_page' => 5,
);
$custom_query = new WP_Query($args);
?>

<div class="contents_wrap">
<main class="main">
<h1><?php echo $page_object->slug ?></h1>
<section class="taxonomy">
  <div>
  <?php if( $custom_query->have_posts() ): ?>
      <?php while( $custom_query->have_posts() ): $custom_query->the_post(); ?>
          <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
          <div class="data"><?php the_time('Y/m/d'); ?></div>
          <?php echo get_the_post_thumbnail(get_the_ID(), 'thumbnail'); ?>
          <div class="entry"><?php the_excerpt(); ?></div>
      <?php endwhile; ?>
  <?php wp_reset_postdata(); ?>
  <?php endif; ?>
  </div>
  <div class="taxonomy__list">
    <h3>カスタムタクソノミーのターム一覧</h3>
    <ul>
      <?php
        $terms = get_terms($page_object->taxonomy);
        foreach ( $terms as $term ) {
          echo '<li><a href="'.get_term_link($term).'">'.$term->name.'</a></li>';
        }
      ?>
    </ul>
  </div>
</section>
</main>
</div>

<?php
get_footer();

.contents_wrap{
  ・・・省略
  .archive-chef{
    ・・・省略
  }
  /* ここから追加 */
  .taxonomy{
    display:flex;
    &__list{
      width:35%;
      margin-left:auto;
    }
  }
  /* ここまで */
}
前回の例によって、標準以外の情報を出力したかったのでサブループnew WP_Query( $args )を使いました。
▼解説
get_queried_objectゲット クエリード オブジェクト()で取得したデータを見てみましょう。
var_dump($page_object); をすると表示されているページのクエリ情報のオブジェクトが取得できます。
一覧ページで表示されているページのタクソノミーやスラッグ情報を取得できます。 ページのタイプによって表示される内容が異なりますので、毎回確認したほうが安全です。

object(WP_Term)#5298 (10) {
  ["term_id"]    => int(13)
  ["name"]       => string(6) "東京"
  ["slug"]       => string(5) "tokyo"
  ["term_group"] => int(0)
  ["term_taxonomy_id"]=> int(13)
  ["taxonomy"]   => string(4) "shop"
  ["description"]=> string(0) ""
  ["parent"]     => int(0)
  ["count"]      => int(2)
  ["filter"]     => string(3) "raw"
}
タクソノミー`shop`ではなく`food`と別のタームのページを表示しても、動的に文字列が変更できるので、 レイアウトの変更がないのであれば複数のtaxonomy-{カスタムタクソノミー名}.phpを作らなくても済みます。

別の方法として、wp_get_object_termswp ゲット オブジェクト タームズ()も利用できます。

wp_get_object_terms(タームを取得するオブジェクトのID, カスタムタクソノミー名);
↓
$term = wp_get_object_terms($post_ID, 'shop');
wp_get_object_terms() で、指定したカスタムタクソノミー名ごとのタームのオブジェクトデータを取得します。取得された$term[0]に各種データが入っています。 var_dunp($term[0])で確認するとget_queried_object()と同じ内容が確認できます。
ほかの情報を入れたい場合はarchive-{カスタム投稿名}.phpで使ったテンプレートタグを入れてみましょう。


$terms = get_terms('shop');
foreach ( $terms as $term ) {
  echo '<li><a href="'.get_term_link($term).'">'.$term->name.'</a></li>';
}
get_terms()で登録されているタームがある場合は、一覧を取得してliタグで表示させます。

http://localhost:8003/shop/tokyo/ で表示されます。
http://localhost:8003/shop/fukuoka/ や http://localhost:8003/food/french/ でも
同じtaxonomy.phpテンプレートで表示できます。