Файл single.php: как настроить вывод отдельной записи в WordPress
Как работает single.php в WordPress: от базового шаблона до расширенных сценариев
Файл single.php отвечает за отображение отдельной записи (поста) на сайте WordPress. Он входит в иерархию шаблонов и вызывается, когда пользователь переходит по постоянной ссылке на конкретную публикацию. Понимание структуры этого файла позволяет гибко управлять выводом контента, добавлять метаданные, менять оформление в зависимости от типа записи или рубрики.
Как построить классический шаблон отдельной записи?
Самый надёжный способ - использовать стандартный цикл WordPress (Loop). Внутри цикла вызываются теги шаблона для заголовка, контента, миниатюры, мета-информации. Ниже приведён минимальный рабочий код файла single.php.
<?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="entry-title">', '</h1>' ); ?>
<div class="entry-meta">
<?php
printf(
esc_html__( 'Опубликовано %s автором %s', 'textdomain' ),
get_the_date(),
get_the_author()
);
?>
</div>
</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(); ?>
<?php
wp_link_pages( array(
'before' => '<div class="page-links">' . esc_html__( 'Страницы:', 'textdomain' ),
'after' => '</div>',
) );
?>
</div>
<footer class="entry-footer">
<?php the_tags( '<span class="tags-links">' . esc_html__( 'Метки:', 'textdomain' ) . ' ', ', ', '</span>' ); ?>
<?php the_category( ', ', '<span class="cat-links">', '</span>' ); ?>
</footer>
</article>
<?php
// Если комментарии открыты, выводим их
if ( comments_open() || get_comments_number() ) :
comments_template();
endif;
?>
<?php endwhile; // end of the loop. ?>
</main>
<?php get_sidebar(); ?>
<?php get_footer(); ?>
Wp single php (файл single.php в wordpress)
Типичные проблемы и ошибки:
- Не сбрасывается цикл после основного запроса. Если после while используется второй запрос (например, WP_Query), необходимо вызвать wp_reset_postdata(), иначе могут появиться некорректные данные на странице.
- Использование
the_ID()вне цикла. Тегthe_ID()выводит ID только внутри цикла. Для получения ID за пределами цикла используйтеget_the_ID(). - Отсутствие fallback для
comments_template(). Если тема не поддерживает комментарии, вызов без проверки приведёт к ошибке. Всегда проверяйте comments_open(). - Проблемы с пагинацией контента. Если запись разбита на несколько страниц тегом
<!--nextpage-->, обязательно используйтеwp_link_pages()для навигации.
Этот вариант подходит для большинства стандартных блогов. Он использует встроенные функции WordPress, легко переопределяется в дочерних темах и полностью соответствует кодексу.
Как разделить вывод для разных типов записей с помощью get_template_part?
Если в теме используются произвольные типы записей (Custom Post Types), удобно разнести шаблоны их отображения в отдельные файлы и подключать их через get_template_part(). Это позволяет держать single.php чистым и избегать условий.
<?php
$post_type = get_post_type();
get_template_part( 'template-parts/single/content', $post_type );
?>
Wp blog header php (шаблоны header в wordpress)
В папке template-parts/single/ создаются файлы content-post.php (для стандартных записей), content-portfolio.php (для портфолио) и т.д. Каждый из них содержит свою разметку.
Возможные ошибки:
- Если файл content-{type}.php отсутствует,
get_template_part()не вызовет ошибку, но ничего не выведет. Рекомендуется создавать файл-заглушку content.php как fallback. - Путь к файлу должен указываться без расширения и без ведущей косой черты.
Как модифицировать вывод с помощью условных тегов внутри single.php?
Иногда требуется изменить блоки в зависимости от рубрики, метки или пользовательских данных. Условные теги (in_category(), has_tag(), is_single()) позволяют это сделать без создания дополнительных шаблонов.
<?php if ( in_category( 'news' ) ) : ?>
<div class="news-badge">Новость</div>
<?php endif; ?>
<?php the_content(); ?>
<?php if ( has_tag( 'sale' ) ) : ?>
<p class="sale-notice">Товар участвует в акции!</p>
<?php endif; ?>
Распространённые сложности:
- Условные теги работают только после установки глобального объекта
$post(после вызова the_post()). Использование их до цикла приведёт к непредсказуемым результатам. - При использовании нескольких условных проверок код может стать трудночитаемым. В таких случаях лучше вынести логику в отдельный файл или использовать шаблоны с get_template_part().
Как добавить кастомные поля и метабоксы в шаблон записи?
Для отображения произвольных полей (через Advanced Custom Fields или встроенную функцию get_post_meta()) достаточно разместить вызов внутри цикла.
<?php
$price = get_post_meta( get_the_ID(), 'price', true );
if ( $price ) : ?>
<div class="product-price">Цена: <?php echo esc_html( $price ); ?> руб.</div>
<?php endif; ?>
Типичные проблемы:
- Обращение к мета-полям до вызова the_post() (или без указания ID) вернёт данные предыдущей записи. Всегда используйте
get_the_ID()внутри цикла. - Не забывайте экранировать вывод мета-полей с помощью esc_html() или esc_attr() для защиты от XSS.
Как настроить отображение комментариев с поддержкой вложенности?
По умолчанию comments_template() подключает файл comments.php из папки темы. Можно передать аргументы для кастомизации.
<?php
if ( comments_open() || get_comments_number() ) :
$args = array(
'style' => 'div',
'callback' => 'my_theme_comment',
'max_depth' => 3,
);
comments_template( '', true, $args );
endif;
?>
Распространённые ошибки:
- Если файл comments.php отсутствует, WordPress подключит стандартный шаблон. Но для полного контроля его нужно создать.
- Передача третьего параметра (
$args) в comments_template() доступна начиная с WordPress 5.5. Для старых версий используйте фильтр comments_template_query_args.
Расширенные примеры использования single.php
Пример 1. single.php для произвольного типа записи «portfolio» с таксономиями и ACF
В этом примере выводятся данные портфолио: изображение проекта, описание, клиент (поле ACF) и список технологий (таксономия).
<?php
/* single-portfolio.php */
get_header();
while ( have_posts() ) : the_post();
$client = get_field( 'client' );
$url = get_field( 'project_url' );
?>
<article id="post-<?php the_ID(); ?>" <?php post_class( 'portfolio-item' ); ?>>
<?php the_post_thumbnail( 'portfolio-full' ); ?>
<h2 class="portfolio-title"><?php the_title(); ?></h2>
<div class="portfolio-meta">
<?php if ( $client ) : ?>
<span class="client">Клиент: <?php echo esc_html( $client ); ?></span>
<?php endif; ?>
<?php
$techs = get_the_term_list( get_the_ID(), 'technology', 'Технологии: ', ', ' );
if ( $techs ) : ?>
<span class="technologies"><?php echo $techs; ?></span>
<?php endif; ?>
</div>
<div class="portfolio-content"><?php the_content(); ?></div>
<?php if ( $url ) : ?>
<a href="<?php echo esc_url( $url ); ?>" class="btn" target="_blank">Открыть проект</a>
<?php endif; ?>
</article>
<?php endwhile;
get_footer();
?>
Результат: страница отдельного проекта портфолио с изображением, описанием, клиентом, технологиями и ссылкой. Файл single-portfolio.php автоматически используется для записей типа «portfolio» благодаря иерархии шаблонов.
Пример 2. Пагинация для записей, разбитых на части (nextpage)
Если запись содержит тег <!--nextpage-->, WordPress делит её на несколько страниц. Для корректной навигации используйте wp_link_pages() внутри цикла.
<?php the_content(); ?>
<?php
wp_link_pages( array(
'before' => '<div class="page-links">' . esc_html__( 'Страницы:', 'textdomain' ),
'after' => '</div>',
'link_before' => '<span class="page-number">',
'link_after' => '</span>',
'pagelink' => '%',
) );
?>
Результат: под контентом появляются ссылки вида «Страницы: 1 2 3». По умолчанию wp_link_pages() выводит только номера страниц. Для более сложной навигации (предыдущая/следующая) добавьте аргумент 'next_or_number' => 'both'.
Пример 3. Динамическая подгрузка похожих записей через WP_Query
После основного цикла можно вывести блок «Похожие записи», выбрав записи из тех же рубрик.
<?php
$categories = wp_get_post_categories( get_the_ID() );
if ( $categories ) {
$args = array(
'category__in' => $categories,
'post__not_in' => array( get_the_ID() ),
'posts_per_page' => 3,
'orderby' => 'rand',
);
$related = new WP_Query( $args );
if ( $related->have_posts() ) : ?>
<h3>Похожие записи</h3>
<ul>
<?php while ( $related->have_posts() ) : $related->the_post(); ?>
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php endwhile; ?>
</ul>
<?php endif;
wp_reset_postdata();
}
?>
Результат: внизу страницы отображается блок из трёх случайных записей из тех же категорий. Важно: вызов wp_reset_postdata() восстанавливает глобальный объект $post, иначе последующий код может работать неправильно.
Пример 4. Создание собственного шаблона через фильтр template_include
Иногда нужно подменить файл single.php на лету, например, для записей с определённым ID или значением произвольного поля.
// functions.php
add_filter( 'template_include', 'my_custom_single_template' );
function my_custom_single_template( $template ) {
if ( is_single() && get_post_meta( get_the_ID(), '_featured', true ) == 'yes' ) {
$new_template = locate_template( array( 'single-featured.php' ) );
if ( $new_template ) {
return $new_template;
}
}
return $template;
}
Теперь для записей с мета-полем _featured = yes будет использоваться файл single-featured.php. Это даёт гибкость без изменения иерархии шаблонов.
Пример 5. Интеграция Schema.org разметки в single.php
Для SEO можно добавить микроразметку Article. Обёртка с атрибутами itemscope и itemtype.
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?> itemscope itemtype="http://schema.org/Article">
<meta itemprop="mainEntityOfPage" content="<?php the_permalink(); ?>" />
<span itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="<?php echo esc_attr( get_the_author() ); ?>" />
</span>
<meta itemprop="datePublished" content="<?php echo get_the_date( 'c' ); ?>" />
<meta itemprop="dateModified" content="<?php echo get_the_modified_date( 'c' ); ?>" />
<div itemprop="image" itemscope itemtype="http://schema.org/ImageObject">
<?php the_post_thumbnail( 'large', array( 'itemprop' => 'url' ) ); ?>
</div>
<div itemprop="articleBody"><?php the_content(); ?></div>
</article>
Результат: страница записи содержит структурированные данные, которые помогают поисковым системам лучше понимать контент. Проверить разметку можно через тестер Google Rich Results.