WordPress でプラグインなしにカスタム投稿を足す方法:register_post_type と single・archive のテンプレート(WordPress 7.1 で確かめた)

広告WordPress・PHP を学べる本

Amazonのアソシエイトとして、WordPress functions.phpは適格販売により収入を得ています。

概要説明

プラグインを使わずに、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_)を付けた

コードを足すと、管理画面のメニューが増える

WordPress でプラグインなしにカスタム投稿を足す方法:register_post_type と single・archive のテンプレート(WordPress 7.1 で確かめた)
WordPress でプラグインなしにカスタム投稿を足す方法:register_post_type と single・archive のテンプレート(WordPress 7.1 で確かめた)

管理画面のメニューに「フォトギャラリー」が出て、記事を書けるようになれば、管理画面の側はできあがりです。

パーマリンクの更新を忘れない

コードを足しただけでは、/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 という名前にするのがおすすめです。子テーマがあれば子テーマに置きます

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 を作るのがおすすめです

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() ページの切り替えのリンク。前のコードのような、テーマのアイコンの文字(&#xe910; など)に頼らない

前の書き方との違い

前のコード 困ること 直したこと
mb_strimwidth( strip_tags( get_the_content() ) … ) ショートコードやブロックの印が、そのまま文字で出ることがある get_the_excerpt() にした
名前や文をエスケープせずに出す 記号が入ると表示が崩れる esc_html() を通した
記事がないときの表示がない 空のページになる 「まだ記事がありません」と出す
paginate_links() とテーマのアイコンの文字 ほかのテーマでは記号が出ない the_posts_pagination() にした

一覧の件数を変える

1 ページに出す件数は「設定」→「表示設定」の値(このサイトでは 10 件)になります。フォトの一覧だけ変えたいときは、テンプレートではなく functions.php で変えます。

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 で書き方の誤りなし(テーマには置いていない)

関連機能

  1. 編集画面にnoindex設定を追加 ( カスタム投稿に対応 )

  2. WordPress のショートコードの作り方:add_shortcode の基本と、広告・記事のリンクを出す例(echo で上に出る・下書きのタイトルが出る問題も直す)

  3. WordPress のダッシュボードに「更新」ボタンを足して、押した日を最終更新日として表示する:同じ日時で CSS のキャッシュも消す

サイドバー

よく使うカスタマイズ

最近の記事

Profile

PAGE TOP