single.phpとは、WordPressで個別の投稿記事を表示するためのテンプレートファイルです。ブログ記事やニュース記事など、1つの投稿を単体で表示するページのレイアウトを制御します。
この記事では、single.phpの基本的な書き方からカスタマイズ方法まで、実践的なコード例を交えて解説します。前後の記事リンク、関連記事の表示、カスタム投稿タイプ別テンプレートの作成方法など、個別投稿ページを自由に設計するために必要な知識を網羅しています。
WordPressテーマのテンプレートファイル全体の構成については、WordPressテーマのテンプレートファイル一覧と役割で詳しく解説しています。
single.phpとは?テンプレート階層での役割
single.phpは、WordPressのテンプレート階層(Template Hierarchy)において、個別の投稿記事を表示する際に使用されるテンプレートファイルです。ユーザーがブログ記事のURLにアクセスしたとき、WordPressは以下の優先順位でテンプレートファイルを検索します。
| 優先順位 | テンプレートファイル | 説明 |
|---|---|---|
| 1(最優先) | single-{post_type}.php | カスタム投稿タイプ別テンプレート(例: single-product.php) |
| 2 | single.php | すべての投稿タイプ共通の個別投稿テンプレート |
| 3 | singular.php | 投稿・固定ページ共通のテンプレート |
| 4(最終) | index.php | すべてのページの最終フォールバック |
たとえば「product」というカスタム投稿タイプがある場合、WordPressはまずsingle-product.phpを探し、なければsingle.php、それもなければsingular.php、最終的にはindex.phpを使用します。
通常の投稿(post)の場合はsingle-post.phpが最優先ですが、ほとんどのテーマではsingle.phpを作成して対応しています。一覧ページのテンプレートであるarchive.phpが複数の投稿をループ表示するのに対し、single.phpは1つの投稿を詳細に表示する役割を持ちます。
single.phpの基本的な書き方
single.phpの基本構造は、WordPressループの中で投稿データを取得・表示するというシンプルなものです。以下に、最小限の構成から実用的な構成まで順を追って解説します。
最小構成のsingle.php
まずは最もシンプルなsingle.phpの例です。ヘッダー・フッターの読み込みと、WordPressループ内でのタイトル・本文の表示が基本になります。
<?php get_header(); ?>
<main>
<?php while ( have_posts() ) : the_post(); ?>
<article>
<h1><?php the_title(); ?></h1>
<div class="post-content">
<?php the_content(); ?>
</div>
</article>
<?php endwhile; ?>
</main>
<?php get_footer(); ?>have_posts()とthe_post()はWordPressループの基本です。single.phpでは投稿が1件だけですが、ループ構文は必須です。the_post()を呼ぶことでグローバルな投稿データがセットアップされ、the_title()やthe_content()などのテンプレートタグが正しく動作するようになります。
実用的な構成のsingle.php
実際のテーマでは、タイトルと本文に加えて、アイキャッチ画像、投稿日、著者名、カテゴリーなどのメタ情報も表示します。以下は実用的なsingle.phpの例です。
<?php get_header(); ?>
<main class="site-main">
<?php while ( have_posts() ) : the_post(); ?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<!-- 記事ヘッダー -->
<header class="post-header">
<h1 class="post-title"><?php the_title(); ?></h1>
<div class="post-meta">
<time datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>">
<?php echo esc_html( get_the_date() ); ?>
</time>
<span class="post-author">
<?php the_author(); ?>
</span>
<span class="post-category">
<?php the_category( ', ' ); ?>
</span>
</div>
</header>
<!-- アイキャッチ画像 -->
<?php if ( has_post_thumbnail() ) : ?>
<div class="post-thumbnail">
<?php the_post_thumbnail( 'large' ); ?>
</div>
<?php endif; ?>
<!-- 記事本文 -->
<div class="post-content">
<?php the_content(); ?>
</div>
<!-- タグ表示 -->
<?php if ( has_tag() ) : ?>
<div class="post-tags">
<?php the_tags( '<span>', '</span><span>', '</span>' ); ?>
</div>
<?php endif; ?>
</article>
<?php endwhile; ?>
</main>
<?php get_sidebar(); ?>
<?php get_footer(); ?>各テンプレートタグの役割は次のとおりです。
| テンプレートタグ | 出力内容 | 補足 |
|---|---|---|
| the_title() | 投稿タイトル | h1タグ内で使用する |
| the_content() | 投稿本文 | ブロックエディタの内容がそのまま出力される |
| the_post_thumbnail() | アイキャッチ画像 | サイズ指定可能(thumbnail, medium, large, full) |
| get_the_date() | 公開日 | 引数でフォーマット指定可能(例: ‘Y年n月j日’) |
| the_author() | 著者名 | リンク付きはthe_author_posts_link()を使用 |
| the_category() | カテゴリー名 | 引数でセパレータを指定可能 |
| the_tags() | タグ名 | 3つの引数で前後の文字列を指定可能 |
| post_class() | 投稿のCSSクラス | 投稿タイプやカテゴリーに応じたクラスが自動付与される |
WordPress関数の詳細については、WordPress関数一覧も参考にしてください。
前後の記事リンクを表示する
個別投稿ページでは、前後の記事へのナビゲーションリンクを設置するのが一般的です。WordPressにはprevious_post_link()とnext_post_link()という専用のテンプレートタグが用意されています。
基本的な前後リンクの実装
<nav class="post-navigation">
<div class="nav-previous">
<?php previous_post_link( '%link', '« %title' ); ?>
</div>
<div class="nav-next">
<?php next_post_link( '%link', '%title »' ); ?>
</div>
</nav>%linkはリンクタグに置き換わり、%titleは前後の記事のタイトルに置き換わります。最初の記事には「前の記事」リンクが、最後の記事には「次の記事」リンクが自動的に非表示になります。
同一カテゴリー内での前後リンク
第3引数にtrueを渡すことで、同じカテゴリーの記事だけを対象に前後リンクを生成できます。カテゴリーごとにコンテンツの連続性を保ちたい場合に有効です。
<nav class="post-navigation">
<div class="nav-previous">
<?php previous_post_link( '%link', '« %title', true ); ?>
</div>
<div class="nav-next">
<?php next_post_link( '%link', '%title »', true ); ?>
</div>
</nav>また、WordPress 4.1以降ではthe_post_navigation()関数も使えます。こちらはラッパーとなるHTMLも含めて出力してくれるため、シンプルに実装できます。
<?php
the_post_navigation( array(
'prev_text' => '« %title',
'next_text' => '%title »',
'in_same_term' => true,
'taxonomy' => 'category',
) );
?>関連記事を表示する方法
関連記事を表示することで、読者のサイト内回遊率を向上させることができます。プラグインを使わずに、同じカテゴリーの記事をWP_Queryで取得して表示する方法を紹介します。
同カテゴリーから関連記事を取得する
以下のコードでは、現在表示中の記事と同じカテゴリーから最新3件の関連記事を取得し、タイトルとアイキャッチ画像をリンク付きで表示します。
<?php
// 現在の投稿のカテゴリーIDを取得
$categories = wp_get_post_categories( get_the_ID() );
if ( ! empty( $categories ) ) :
$related_query = new WP_Query( array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => 3,
'post__not_in' => array( get_the_ID() ),
'category__in' => $categories,
'orderby' => 'date',
'order' => 'DESC',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
) );
if ( $related_query->have_posts() ) : ?>
<section class="related-posts">
<h2>関連記事</h2>
<div class="related-posts-grid">
<?php while ( $related_query->have_posts() ) : $related_query->the_post(); ?>
<a href="<?php the_permalink(); ?>" class="related-post-card">
<?php if ( has_post_thumbnail() ) : ?>
<?php the_post_thumbnail( 'medium' ); ?>
<?php endif; ?>
<h3><?php the_title(); ?></h3>
</a>
<?php endwhile; ?>
</div>
</section>
<?php endif;
wp_reset_postdata();
endif;
?>このコードのポイントは以下の3つです。
- post__not_in: 現在の記事自身を除外する
- no_found_rows: ページネーションが不要な場合は
trueにすることでクエリを高速化できる - wp_reset_postdata(): サブクエリの後は必ず呼び出し、グローバルな投稿データを元に戻す
タグベースで関連記事を取得する
カテゴリーの代わりにタグを基準にすることもできます。category__inをtag__inに変更するだけです。
<?php
$tags = wp_get_post_tags( get_the_ID(), array( 'fields' => 'ids' ) );
if ( ! empty( $tags ) ) :
$related_query = new WP_Query( array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => 3,
'post__not_in' => array( get_the_ID() ),
'tag__in' => $tags,
'orderby' => 'rand',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
) );
// ... 表示処理はカテゴリーベースと同様 ...
wp_reset_postdata();
endif;
?>single-{post_type}.phpでカスタム投稿タイプ別にテンプレートを分ける
WordPressでは、カスタム投稿タイプごとに専用のsingle.phpを作成できます。テンプレートファイル名をsingle-{post_type}.phpの形式にするだけで、WordPressが自動的に認識します。
カスタム投稿タイプの登録
まず、functions.phpでカスタム投稿タイプを登録します。ここでは「portfolio」(制作実績)を例にします。
function my_register_portfolio_post_type() {
register_post_type( 'portfolio', array(
'labels' => array(
'name' => '制作実績',
'singular_name' => '制作実績',
),
'public' => true,
'has_archive' => true,
'supports' => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
'menu_icon' => 'dashicons-portfolio',
'rewrite' => array( 'slug' => 'portfolio' ),
) );
}
add_action( 'init', 'my_register_portfolio_post_type' );single-portfolio.phpの作成
テーマフォルダにsingle-portfolio.phpを作成すると、portfolioの個別投稿ページではこのテンプレートが自動的に使われます。通常の投稿とは異なるレイアウトを適用できます。
<?php
/**
* 制作実績のカスタム投稿タイプ用テンプレート
* テンプレートファイル名: single-portfolio.php
*/
get_header(); ?>
<main class="portfolio-detail">
<?php while ( have_posts() ) : the_post(); ?>
<article class="portfolio-item">
<h1><?php the_title(); ?></h1>
<?php if ( has_post_thumbnail() ) : ?>
<div class="portfolio-hero">
<?php the_post_thumbnail( 'full' ); ?>
</div>
<?php endif; ?>
<!-- カスタムフィールドで追加情報を表示 -->
<div class="portfolio-meta">
<?php
$client = get_post_meta( get_the_ID(), 'client_name', true );
$url = get_post_meta( get_the_ID(), 'project_url', true );
?>
<?php if ( $client ) : ?>
<p><strong>クライアント:</strong> <?php echo esc_html( $client ); ?></p>
<?php endif; ?>
<?php if ( $url ) : ?>
<p><strong>URL:</strong> <a href="<?php echo esc_url( $url ); ?>"><?php echo esc_html( $url ); ?></a></p>
<?php endif; ?>
</div>
<div class="portfolio-content">
<?php the_content(); ?>
</div>
</article>
<?php endwhile; ?>
</main>
<?php get_footer(); ?>カスタムフィールドの扱い方についてさらに詳しく知りたい場合は、WordPressカスタムフィールド解説をご覧ください。
よく使われるカスタム投稿タイプ別テンプレートの対応表をまとめます。
| 投稿タイプ(slug) | テンプレートファイル名 | 用途例 |
|---|---|---|
| post | single-post.php(または single.php) | 通常のブログ記事 |
| portfolio | single-portfolio.php | 制作実績 |
| product | single-product.php | 商品ページ(WooCommerce等) |
| event | single-event.php | イベント情報 |
| faq | single-faq.php | よくある質問の個別表示 |
functions.phpとの連携
single.phpで使用する機能の中には、functions.phpでの事前設定が必要なものがあります。ここでは、single.phpと密接に関連するfunctions.phpの設定を紹介します。
アイキャッチ画像のサポートを有効化する
single.phpでthe_post_thumbnail()を使用するには、functions.phpでアイキャッチ画像のサポートを有効化する必要があります。
function my_theme_setup() {
// アイキャッチ画像のサポートを有効化
add_theme_support( 'post-thumbnails' );
// カスタム画像サイズの追加(任意)
add_image_size( 'single-hero', 1200, 630, true );
}
add_action( 'after_setup_theme', 'my_theme_setup' );カスタム画像サイズを追加した場合、single.phpではthe_post_thumbnail( 'single-hero' )のように指定して使用できます。
抜粋文(excerpt)のカスタマイズ
single.php自体では抜粋文を使う場面は少ないですが、関連記事の表示で抜粋文を使いたい場合は、文字数やデフォルトの「続きを読む」テキストをカスタマイズできます。
// 抜粋文の文字数を変更(デフォルト: 55語)
function my_excerpt_length( $length ) {
return 40;
}
add_filter( 'excerpt_length', 'my_excerpt_length' );
// 抜粋文の「続きを読む」テキストを変更
function my_excerpt_more( $more ) {
return '...';
}
add_filter( 'excerpt_more', 'my_excerpt_more' );投稿ページ専用のCSSやJSを読み込む
single.phpでのみ必要なCSSやJavaScriptファイルがある場合、is_single()で条件分岐して読み込むことで、他のページへの影響を防ぎつつパフォーマンスを最適化できます。
function my_single_scripts() {
if ( is_single() ) {
wp_enqueue_style(
'single-style',
get_template_directory_uri() . '/assets/css/single.css',
array(),
'1.0.0'
);
wp_enqueue_script(
'single-script',
get_template_directory_uri() . '/assets/js/single.js',
array(),
'1.0.0',
true
);
}
}
add_action( 'wp_enqueue_scripts', 'my_single_scripts' );functions.phpの便利なカスタマイズについては、functions.php便利コード集でさらに多くの例を紹介しています。
よくある質問(FAQ)
Q. single.phpとpage.phpの違いは何ですか?
single.phpは「投稿(post)」やカスタム投稿タイプの個別ページに使用されるテンプレートです。一方、page.phpは「固定ページ」に使用されます。投稿にはカテゴリーやタグがあり時系列で表示されるのに対し、固定ページは「会社概要」「お問い合わせ」のような独立したコンテンツに使います。
Q. single.phpがないテーマではどうなりますか?
テーマにsingle.phpが存在しない場合、WordPressはテンプレート階層に従ってsingular.phpを探し、それもなければindex.phpを使用して投稿を表示します。ただし、index.phpは汎用テンプレートであるため、投稿に適したレイアウトにならない可能性が高いです。投稿を表示するテーマではsingle.phpの作成を推奨します。
Q. single.phpでサイドバーを表示するにはどうすればよいですか?
single.phpの中でget_sidebar()を呼び出すことでサイドバーを表示できます。通常はメインコンテンツの後、get_footer()の前に配置します。CSSでメインコンテンツとサイドバーを横並びにレイアウトするために、親要素にFlexboxやCSS Gridを適用するのが一般的です。
Q. wp_reset_postdata()を呼び忘れるとどうなりますか?
関連記事の表示などでサブクエリ(WP_Query)を使った後にwp_reset_postdata()を呼ばないと、グローバルな$post変数がサブクエリの最後の投稿のままになります。その結果、それ以降のテンプレートタグ(the_title()やthe_permalink()など)が本来の投稿ではなくサブクエリの投稿のデータを返してしまい、意図しない表示になります。
Q. 特定のカテゴリーの投稿だけレイアウトを変えることはできますか?
はい、可能です。single.php内でin_category()を使って条件分岐する方法と、get_template_part()でカテゴリー別のテンプレートパーツを読み込む方法があります。たとえばif ( in_category( 'news' ) ) { get_template_part( 'template-parts/single', 'news' ); }のように記述すると、「news」カテゴリーの投稿だけ別のテンプレートパーツで表示できます。
Q. single.phpの中でget_template_part()を使うメリットは何ですか?
get_template_part()を使うことで、single.phpのコードを複数の小さなファイルに分割できます。たとえば記事ヘッダー、記事本文、関連記事、著者情報などをそれぞれ別ファイルにすることで、コードの見通しがよくなり保守性が向上します。また、分割したパーツを他のテンプレート(page.phpなど)でも再利用できるメリットもあります。
まとめ
single.phpは、WordPressの個別投稿ページを制御する中核のテンプレートファイルです。この記事で解説した内容をまとめます。
- テンプレート階層: single-{post_type}.php → single.php → singular.php → index.phpの順に検索される
- 基本構造: WordPressループの中でthe_title()、the_content()、the_post_thumbnail()などのテンプレートタグで投稿データを表示する
- 前後リンク: previous_post_link() / next_post_link()で記事間のナビゲーションを実装する
- 関連記事: WP_Queryで同カテゴリー・同タグの記事を取得し、サイト内回遊率を向上させる
- カスタム投稿タイプ: single-{post_type}.phpで投稿タイプごとに専用レイアウトを作成できる
- functions.phpとの連携: アイキャッチ画像のサポート有効化、抜粋文カスタマイズ、条件付きCSS/JS読み込み
single.phpを理解し使いこなすことで、投稿ページのデザインと機能を自由にカスタマイズできるようになります。テンプレートファイルの全体像を把握するために、WordPressテーマのテンプレートファイル一覧と役割もあわせてご覧ください。
