概要説明
プラグインを使わずに、functions.php の register_post_type でカスタム投稿(タイトル・本文・アイキャッチ・専用カテゴリー)を足す方法。前のコードにあった、カテゴリーの一覧が開けない URL のぶつかりを直し、single・archive のテンプレートと、パーマリンクの更新の要る理由も作業環境で確かめました。
~ 目次 ~
プラグインなしでカスタム投稿を足す:functions.php
お知らせ・スタッフ紹介・制作実績・キャンペーンなど、ふつうの「投稿」とは分けて記事を管理したいときに、カスタム投稿を使います。
下のコードは「フォトギャラリー」という投稿の種類と、その専用のカテゴリーを足します。使っているテーマの functions.php(子テーマがあれば子テーマのほう)のいちばん下に足します。
/**
* カスタム投稿「フォトギャラリー」と、その専用のカテゴリーを足す
*/
function fs_register_photo_post_type() {
register_post_type( 'photo', array(
'labels' => array(
'name' => 'フォトギャラリー',
'singular_name' => 'フォト',
'add_new_item' => 'フォトを追加',
'edit_item' => 'フォトを編集',
),
'public' => true,
'show_in_rest' => true, // ブロックエディターを使う・REST API に出す
'menu_position' => 5, // 管理画面のメニューで「投稿」の下
'menu_icon' => 'dashicons-format-gallery',
'has_archive' => true, // 一覧のページ(/photo/)を作る
'rewrite' => array( 'slug' => 'photo', 'with_front' => false ),
'supports' => array( 'title', 'editor', 'thumbnail', 'revisions' ),
) );
register_taxonomy( 'photo_cat', 'photo', array(
'labels' => array(
'name' => 'フォトのカテゴリー',
'singular_name' => 'カテゴリー',
),
'hierarchical' => true, // 「カテゴリー」と同じ親子のある形
'show_in_rest' => true, // ブロックエディターの横に出す
'show_admin_column' => true, // 一覧の表にカテゴリーの列を出す
'rewrite' => array( 'slug' => 'photo-cat', 'with_front' => false ),
) );
}
add_action( 'init', 'fs_register_photo_post_type' );
| 書き方 | 役目 |
|---|---|
register_post_type( 'photo', … ) |
投稿の種類を足す。photo が名前(英小文字・20 文字まで) |
| labels | 管理画面に出る名前。add_new_item などを決めないと「投稿を追加」と出る |
| public => true | サイトに出す・管理画面にメニューを出す |
| show_in_rest => true | ブロックエディターで書けるようにする・REST API(/wp-json/wp/v2/photo)に出す |
| has_archive => true | 一覧のページ(/photo/)を作る |
| rewrite の with_front => false | URL の頭に、パーマリンクの設定の決まった部分(このサイトなら /archives/)を付けない |
| supports | 使う入力の欄。title(タイトル)・editor(本文)・thumbnail(アイキャッチ)・revisions(リビジョン) |
register_taxonomy( 'photo_cat', 'photo', … ) |
photo だけで使うカテゴリーを足す |
| hierarchical => true | 「カテゴリー」と同じ、親子のある形。false にすると「タグ」の形 |
rewrite の slug => 'photo-cat' |
カテゴリーの一覧の URL を /photo-cat/名前/ にする。投稿の種類と同じ photo にしない |
add_action( 'init', … ) |
WordPress の準備(init)のときに登録する。決まりのタイミング |
前の書き方との違い
| 前のコード | 困ること | 直したこと |
|---|---|---|
| カテゴリーの rewrite の slug を photo(投稿の種類と同じ) | カテゴリーの一覧の URL が、1 件ずつの記事の URL として読まれ、カテゴリーの一覧が開けない(下の確かめた結果) | photo-cat に変えた |
| with_front を決めていない | このサイトでは URL が /archives/photo/… になる | with_front => false で /photo/… にした |
| show_in_rest がない | ブロックエディターが使えず、古いエディターになる | true にした |
| label だけ | 「新規追加」の画面などが「投稿を追加」のまま | labels で名前を決めた |
| singular_label | 今の WordPress にはない設定で、効かない | 消して labels の singular_name にした |
| 関数の名前が add_post_type | ほかのテーマやプラグインと名前がぶつかりやすい | 頭にサイトの印(fs_)を付けた |
コードを足すと、管理画面のメニューが増える
管理画面のメニューに「フォトギャラリー」が出て、記事を書けるようになれば、管理画面の側はできあがりです。
パーマリンクの更新を忘れない
コードを足しただけでは、/photo/ などの URL が「ページが見つかりません」(404)になります。WordPress は URL の決まり(リライトルール)を DB に覚えていて、新しい投稿の種類の分が、まだ入っていないからです。
管理画面の「設定」→「パーマリンク」を開くと、決まりが作り直されます(「変更を保存」を押さなくても、開くだけでよい)。コードの中で flush_rewrite_rules() を毎回呼ぶと重くなるので、書かないでください。
確かめた結果
このサイトの作業環境(WordPress 7.1.3・PHP 8.3)で、コードをメモリの中だけで登録して確かめました。下書きの試しの記事とカテゴリーを作り、確かめたあと消しました。サイトのパーマリンクの決まりは作り直していません。
| 見たこと | 前のコード | 新しいコード |
|---|---|---|
| 一覧の URL | /archives/photo/ | /photo/ |
| 1 件ずつの URL | /archives/photo/記事の名前/ | /photo/記事の名前/ |
| カテゴリーの一覧の URL を開くと | 記事として読まれる(カテゴリーの一覧は開けない) | カテゴリーの一覧として読まれる(/photo-cat/名前/) |
| 一覧の 2 ページ目(/photo/page/2/) | - | 一覧の 2 ページ目として読まれる |
| REST API | 出ない | /wp/v2/photo と /wp/v2/photo_cat に出る |
| 管理画面の名前 | 「投稿を追加」 | 「フォトを追加」 |
| パーマリンクの更新の前の DB の決まり | photo の分は入っていない(だから更新が要る) | |
このサイトは Classic Editor のプラグインを使っているので、show_in_rest を true にしても、古いエディターのままでした。ブロックエディターを使っているサイトなら、ブロックエディターで書けます。
1 件ずつのページを作る:single-photo.php
WordPress は、表示する記事の種類に合わせて、テーマの中のファイルを決まった順に探します。single-photo.php がなければ、ふつうの投稿と同じ single.php で表示されます。
| 探す順 | ファイル |
|---|---|
| 1 | single-photo-記事の名前.php(その記事だけ) |
| 2 | single-photo.php(photo の記事ぜんぶ) |
| 3 | single.php |
| 4 | singular.php |
| 5 | index.php |
レイアウトをそのまま使いたいなら、テーマの single.php をコピーして single-photo.php という名前にするのがおすすめです。子テーマがあれば子テーマに置きます
<?php get_header(); ?> <?php while ( have_posts() ) : the_post(); ?> <article <?php post_class( 'photo-single' ); ?>> <?php if ( has_post_thumbnail() ) : ?> <div class="photo-image"><?php the_post_thumbnail( 'full' ); ?></div> <?php endif; ?> <h1 class="photo-title"><?php the_title(); ?></h1> <time class="photo-date" datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>"><?php echo esc_html( get_the_date( 'Y.m.d' ) ); ?></time> <?php // カテゴリー(いくつあっても全部・一覧へのリンクつき) ?> <?php the_terms( get_the_ID(), 'photo_cat', '<div class="photo-cat">', '、', '</div>' ); ?> <div class="photo-content"><?php the_content(); ?></div> </article> <?php endwhile; ?> <?php get_footer(); ?>
| 書き方 | 役目 |
|---|---|
| post_class() | 記事の種類などのクラスを付ける。CSS で見た目を分けやすい |
| has_post_thumbnail() | アイキャッチがあるときだけ、画像の枠を出す |
get_the_date( 'c' ) |
機械が読む日付(datetime)。画面には Y.m.d で出す |
| the_terms() | カテゴリーを全部、一覧へのリンクつきで出す。なければ何も出さない |
前のコードは、カテゴリーの 1 つ目の名前を、そのまま(esc_html() を通さずに)出していました。the_terms() を使うと、名前のエスケープもリンクも WordPress がしてくれます。
新しくページを作るときは、上の形をもとにして、テーマのレイアウト(ヘッダー・サイドバーなどの枠)に合わせてください。
一覧のページを作る:archive-photo.php
一覧のページは、has_archive => true のときだけ作られます。探す順は 1 件ずつのページと同じ考え方です。
| ページ | URL(パーマリンクの更新のあと) | 探すファイル(上から順) |
|---|---|---|
| フォトの一覧 | /photo/ | archive-photo.php → archive.php → index.php |
| フォトのカテゴリーの一覧 | /photo-cat/名前/ | taxonomy-photo_cat-名前.php → taxonomy-photo_cat.php → taxonomy.php → archive.php → index.php |
テーマに archive.php などの一覧のファイルがあれば、コピーして archive-photo.php を作るのがおすすめです
<?php get_header(); ?>
<h1 class="photo-archive-title"><?php post_type_archive_title(); ?></h1>
<?php if ( have_posts() ) : ?>
<ul class="photo-list">
<?php while ( have_posts() ) : the_post(); ?>
<li>
<a href="<?php the_permalink(); ?>">
<?php the_post_thumbnail( 'thumbnail' ); ?>
<?php
// カテゴリーは名前だけ(a の中に a を入れない)
$terms = get_the_terms( get_the_ID(), 'photo_cat' );
if ( $terms && ! is_wp_error( $terms ) ) {
echo '<span class="photo-cat">' . esc_html( $terms[0]->name ) . '</span>';
}
?>
<span class="photo-title"><?php echo esc_html( wp_trim_words( get_the_title(), 30, '…' ) ); ?></span>
<time datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>"><?php echo esc_html( get_the_date( 'Y.m.d' ) ); ?></time>
<span class="photo-excerpt"><?php echo esc_html( wp_trim_words( get_the_excerpt(), 60, '…' ) ); ?></span>
</a>
</li>
<?php endwhile; ?>
</ul>
<?php
the_posts_pagination( array(
'mid_size' => 2,
'prev_text' => '前へ',
'next_text' => '次へ',
) );
?>
<?php else : ?>
<p>まだ記事がありません。</p>
<?php endif; ?>
<?php get_footer(); ?>
| 書き方 | 役目 |
|---|---|
| if ( have_posts() ) … else | 記事がないときに「まだ記事がありません」と出す |
| カテゴリーは名前だけ | 記事へのリンク(a)の中に、カテゴリーのリンクを入れない(a の中に a は入れられない) |
wp_trim_words( …, 30, '…' ) |
長いタイトルを省く。日本語の WordPress では文字の数で数える |
| get_the_excerpt() | 抜粋。なければ本文から作る。ショートコードやブロックの印を消してくれる |
| the_posts_pagination() | ページの切り替えのリンク。前のコードのような、テーマのアイコンの文字( など)に頼らない |
前の書き方との違い
| 前のコード | 困ること | 直したこと |
|---|---|---|
| mb_strimwidth( strip_tags( get_the_content() ) … ) | ショートコードやブロックの印が、そのまま文字で出ることがある | get_the_excerpt() にした |
| 名前や文をエスケープせずに出す | 記号が入ると表示が崩れる | esc_html() を通した |
| 記事がないときの表示がない | 空のページになる | 「まだ記事がありません」と出す |
| paginate_links() とテーマのアイコンの文字 | ほかのテーマでは記号が出ない | the_posts_pagination() にした |
一覧の件数を変える
1 ページに出す件数は「設定」→「表示設定」の値(このサイトでは 10 件)になります。フォトの一覧だけ変えたいときは、テンプレートではなく functions.php で変えます。
/**
* フォトの一覧だけ、1 ページに 12 件ずつ出す
*/
function fs_photo_archive_per_page( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_post_type_archive( 'photo' ) || $query->is_tax( 'photo_cat' ) ) {
$query->set( 'posts_per_page', 12 );
}
}
add_action( 'pre_get_posts', 'fs_photo_archive_per_page' );
| 見たこと(作業環境) | 結果 |
|---|---|
| フォトの一覧(メインのクエリ) | 12 件 |
| ふつうの投稿の一覧 | 10 件のまま |
| テンプレートの中で別に作ったクエリ | 10 件のまま(変わらない) |
| タイトルの省き(30 文字) | 30 文字+「…」になった |
| テンプレートの 2 つのファイル | php -l で書き方の誤りなし(テーマには置いていない) |


