2026年7月5日

2026年7月5日

WordPressのsingle.phpをカスタマイズする方法:子テーマ・投稿情報・関連記事

single.php は、WordPress のクラシックテーマで投稿の個別ページを表示するテンプレートファイルです。記事タイトルの見せ方を変える、投稿日と更新日を並べる、記事の下に関連記事を出す、といった変更はこのファイル(と functions.php)で行います。

この記事では、子テーマに single.php を用意するところから、投稿情報・関連記事の追加、投稿タイプ別のテンプレートまでを順に説明します。コードは WordPress 6.9.4・PHP 8.3 のクラシックテーマで表示を確かめています(2026年10月時点)。

Twenty Twenty-Four や Twenty Twenty-Five などのブロックテーマには single.php がなく、個別投稿のテンプレートは「外観 → エディター」(サイトエディター)で編集します。この記事はクラシックテーマ向けです。

single.php が使われる条件(テンプレート階層)

投稿の個別ページを表示するとき、WordPress は次の順にテンプレートを探し、最初に見つかったものを使います(公式ドキュメント)。

順番 ファイル名 使われる場面
1 single-{投稿タイプ}-{スラッグ}.php 特定の1記事だけ(例: single-post-hello-world.php)
2 single-{投稿タイプ}.php 投稿タイプごと(例: single-product.php)
3 single.php すべての投稿タイプの個別ページ
4 singular.php 個別ページと固定ページの共通
5 index.php 最後の受け皿

記事の編集画面でテンプレートを選んでいる場合は、そのテンプレートが最優先です(後述)。固定ページは page.php 系の別の階層を使うので、single.php を変えても固定ページの表示は変わりません。テンプレート階層の全体像は「WordPressのテンプレート階層」も参考にしてください。

手順1: 子テーマに single.php を用意する

配布されているテーマの single.php を直接書き換えると、テーマを更新したときに変更が消えます。子テーマを作り、親テーマの single.php を子テーマにコピーしてから編集してください。子テーマに同じ名前のファイルがあれば、そちらが優先されます。子テーマの作り方は「WordPress子テーマの作り方」を参照してください。

SSH が使える場合の例です(parent-theme・my-child-theme は実際のフォルダー名に置き換えます)。

cd /path/to/wordpress/wp-content/themes
cp parent-theme/single.php my-child-theme/single.php

FTP やファイルマネージャーの場合も、親テーマのフォルダーから single.php をダウンロードし、子テーマのフォルダーにアップロードすれば同じです。編集前のファイルは手元に残しておき、表示が崩れたらそのファイルに戻します。

親テーマの single.php の中身が get_template_part( 'template-parts/content', 'single' ) のように別ファイルを呼ぶだけになっていることもあります。その場合、本文まわりの変更は呼ばれている側のファイル(この例では template-parts/content-single.php)を子テーマにコピーして編集します。

手順2: single.php の基本の構成

自分で書く場合の基本形です。投稿ループの中でタイトル・投稿情報・アイキャッチ画像・本文を出し、その後に関連記事、前後の記事へのリンク、コメント欄を続けています。mytheme_post_meta() と mytheme_related_posts() は手順3・4で functions.php に定義する関数です。

<?php
/**
 * 投稿の個別ページ(single.php)
 */
get_header();
?>
<main id="primary" class="site-main">
	<?php
	while ( have_posts() ) :
		the_post();
		?>
		<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
			<header class="entry-header">
				<?php the_title( '<h1 class="post-title">', '</h1>' ); ?>
				<?php mytheme_post_meta(); ?>
			</header>

			<?php if ( has_post_thumbnail() ) : ?>
				<div class="post-thumbnail"><?php the_post_thumbnail( 'large' ); ?></div>
			<?php endif; ?>

			<div class="entry-content">
				<?php
				the_content();
				wp_link_pages();
				?>
			</div>
		</article>

		<?php
		mytheme_related_posts();

		the_post_navigation(
			array(
				'prev_text' => '&laquo; %title',
				'next_text' => '%title &raquo;',
			)
		);

		if ( comments_open() || get_comments_number() ) {
			comments_template();
		}
	endwhile;
	?>
</main>
<?php
get_sidebar();
get_footer();

要点は次のとおりです。

  • the_title( '<h1 class="post-title">', '</h1>' ) のように、タイトルの前後に付けるタグを引数で渡せます。
  • wp_link_pages() は、本文を「ページ区切り」ブロックで分けた記事のページ送りを出します。
  • the_post_navigation() の %title は前後の記事のタイトルに置き換わります。
  • コメント欄は comments_template() がテーマの comments.php を読み込んで表示します。

<title> タグはここに書きません。テーマが add_theme_support( 'title-tag' ) を宣言していれば、wp_head() が出力します(SEO プラグインを使っている場合はその設定が反映されます)。

手順3: 投稿日・更新日・カテゴリー・タグを表示する

子テーマの functions.php に次の関数を追加します。更新日は、投稿日より後に更新されている場合だけ表示します。

<?php
/**
 * 投稿日・更新日・カテゴリー・タグを表示する。
 */
function mytheme_post_meta() {
	$published = sprintf(
		'<time class="entry-date published" datetime="%1$s">%2$s</time>',
		esc_attr( get_the_date( DATE_W3C ) ),
		esc_html( get_the_date() )
	);

	$updated = '';
	if ( get_the_modified_time( 'U' ) > get_the_time( 'U' ) ) {
		$updated = sprintf(
			' <span class="updated-label">最終更新日:</span> <time class="updated" datetime="%1$s">%2$s</time>',
			esc_attr( get_the_modified_date( DATE_W3C ) ),
			esc_html( get_the_modified_date() )
		);
	}

	echo '<div class="entry-meta">';
	echo '<span class="posted-on">投稿日: ' . $published . $updated . '</span>';

	$categories = get_the_category_list( ', ' );
	if ( $categories ) {
		echo ' <span class="cat-links">カテゴリー: ' . $categories . '</span>';
	}

	$tags = get_the_tag_list( '', ', ' );
	if ( $tags && ! is_wp_error( $tags ) ) {
		echo ' <span class="tag-links">タグ: ' . $tags . '</span>';
	}
	echo '</div>';
}
  • get_the_date() と get_the_modified_date() は、引数を省くと「設定 → 一般」の日付形式で表示します。datetime 属性には機械が読める DATE_W3C 形式を入れています。
  • get_the_category_list() と get_the_tag_list() は、リンク付きの HTML を返します。タグが1つもない記事では何も表示しません。
  • sprintf() の %1$s・%2$s は引数の番号です。番号と引数の数がずれると表示が崩れるので、追加・削除するときは両方を合わせてください。

手順4: 同じタグの関連記事を表示する

同じタグが付いた投稿を最大3件表示する関数です。これも子テーマの functions.php に追加します。

<?php
/**
 * 同じタグを持つ投稿を最大3件表示する。
 */
function mytheme_related_posts( $number = 3 ) {
	$tag_ids = wp_get_post_tags( get_the_ID(), array( 'fields' => 'ids' ) );
	if ( empty( $tag_ids ) ) {
		return;
	}

	$related = new WP_Query(
		array(
			'tag__in'             => $tag_ids,
			'post__not_in'        => array( get_the_ID() ),
			'posts_per_page'      => $number,
			'ignore_sticky_posts' => true,
			'no_found_rows'       => true,
		)
	);

	if ( ! $related->have_posts() ) {
		return;
	}

	echo '<section class="related-posts"><h2>関連記事</h2><ul>';
	while ( $related->have_posts() ) {
		$related->the_post();
		printf(
			'<li><a href="%1$s">%2$s</a></li>',
			esc_url( get_permalink() ),
			esc_html( get_the_title() )
		);
	}
	echo '</ul></section>';

	wp_reset_postdata();
}
  • post__not_in で表示中の記事を除き、ignore_sticky_posts で先頭固定の投稿が割り込まないようにしています。
  • ページ送りを使わないので、no_found_rows で総件数の計算を省いています。
  • 最後の wp_reset_postdata() を忘れると、その後のコメント欄などが関連記事側の投稿を参照してしまいます。

カテゴリーで探したい場合は、wp_get_post_tags() の代わりに wp_get_post_categories( get_the_ID() ) で ID を取り、tag__in を category__in に変えます。関連記事の考え方やプラグインでの方法は「WordPressで関連記事を表示する方法」も参考にしてください。

手順5: タイトルのデザインを CSS で変える

手順2で <h1> に post-title というクラスを付けたので、子テーマの style.css で見た目を指定できます。

.single .post-title {
	font-size: 2rem;
	line-height: 1.4;
	margin-bottom: 0.5em;
}

.single .entry-meta {
	font-size: 0.875rem;
	color: #555;
}

.related-posts ul {
	padding-left: 1.2em;
}

.single は、投稿の個別ページで <body> に付くクラスです(body_class() が出力します)。固定ページには付かないので、投稿だけにデザインを当てられます。

投稿タイプ別・記事別のテンプレート

投稿タイプごとに分ける

カスタム投稿タイプ product の個別ページだけレイアウトを変えたいときは、single.php をコピーして single-product.php という名前で保存し、そのファイルを編集します。single.php は通常の投稿用にそのまま使えます。

記事ごとに選べるテンプレートを作る

ファイルの先頭に Template Name と Template Post Type を書くと、投稿の編集画面のテンプレート選択に表示されます(WordPress 4.7 以降)。

<?php
/*
 * Template Name: 幅広レイアウト
 * Template Post Type: post
 */
get_header();

このファイル(例: template-wide.php)を子テーマに置くと、投稿の編集画面のサイドバーにある「テンプレート」で「幅広レイアウト」を選べるようになります。2行目以降は single.php と同じように書きます。Template Post Type に post, page のように複数書くと、固定ページでも選べます。

うまく表示されないとき

症状 確認すること
変更が反映されない 編集したのが有効なテーマ(子テーマ)のファイルか。より優先される single-post.php などが無いか。キャッシュ系プラグインやサーバーのキャッシュを削除したか
画面が真っ白・「重大なエラー」 直前に追加したコードの構文誤り。FTP などで子テーマの single.php を編集前のファイルに戻すか、functions.php に追加した部分を削除する。関数名が親テーマやプラグインと重なっていないかも確認する
関連記事が出ない 表示中の記事にタグが付いているか。同じタグの公開済み記事が他にあるか
コメント欄が関連記事の内容になる mytheme_related_posts() の最後に wp_reset_postdata() があるか

関数名は、ほかのテーマやプラグインと重ならないよう、この記事の mytheme_ のように独自の接頭辞を付けてください。

まとめ

  • single.php は投稿の個別ページ用のテンプレートで、single-{投稿タイプ}.php などがあればそちらが優先されます。
  • 配布テーマを変更するときは、子テーマに single.php をコピーして編集します。
  • 投稿情報や関連記事のような処理は functions.php の関数にまとめ、single.php からは呼び出すだけにすると管理しやすくなります。
  • 出力する値は esc_html()・esc_url()・esc_attr() でエスケープし、サブクエリの後は wp_reset_postdata() で元に戻します。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲