Все страницы сайта – это PHP шаблоны, содержащие HTML разметку с PHP тегами. Здесь будет рассмотрено создание шаблона страницы для отдельной записи – начиная от вордпресс цикла и до построения элементов навигации…
Содержание
Что представляет собой запись?
Каждая запись это отдельный представитель типа поста. Из коробки, есть 2 типа постов – страницы и записи; у каждой свои приколы по отображению – у страниц шаблоны для отображения, записей – форматы

Для создания шаблона страницы, в начало файла достаточно добавить комментарий
<?php // Template Name: TemplateName
с форматами постов все немного сложнее…
Чтобы активировать возможность выбора формата, в functions.php на экшн after_setup_theme функцию со следующим содержимым
<?php add_theme_support( 'post-formats', array( 'aside', 'image', 'video', 'quote', 'link', 'gallery', 'audio', ) );
Данный код содержит все допустимые форматы для постов; чтобы получить формат текущего поста используйте функцию get_post_format без аргументов, если ты внутри цикла(о нем позже), или с указанием ID поста.
При создании шаблона для своего типа записей – движок позволяет использовать только схему с форматами. По наименованию: для страниц с заголовком требований нет(если стандартный шаблон, без заголовка – page.php). Для постов и нестандартных типов записей – схема такова single-{post_type}.php, ну или single.php для всех типов кроме страниц(page).
WordPress циклы
Будь-то страница или любой другой тип записи(с условием, что он публичен – смотрите “Создаем свой тип записи. Метадата, роли пользователей, таксономии“) его вывод будет начат с цикла. В принципе, тип записей может быть и непубличным, но тогда его нужно будет принудительно доставать, что, я считаю, нехорошо. Итак, цикл(иногда его называют loop, поскольку это перевод) будет выглядеть так
<?php
if (have_posts()) {
while (have_posts()) {
the_post();
// post content
}
}Естественно, что в начале выводится шапка(get_header) и подвал(get_footer) в конце. Касательно наполнения цикла – принято подключать кусочек шаблона(отдельный файл) при помощи get_template_part($patth), содержащий разметку страницы.
Элементы страницы
Здесь я рассмотрю основные элементы сингл-поста, которые применимы как к записям, так и страницам и прочим типам.
Изображение записи
Для начала – нужно включить их поддержку – это делает следующий код
<?php
add_action( 'after_setup_theme', 'themeslug_setup_theme' );
function themeslug_setup_theme(){
add_theme_support( 'post-thumbnails' );
}После этого, на странице редактирования записи появиться такой метабокс

Теперь о том, как сохраняется загруженное изображение. По умолчанию, при сохранении , сохраняется оригинал и три нарезанных картинки из оригинала. Перейдя по “Settongs > Media” размеры этих изображений можно менять. Чтобы отключить нарезку достаточно выставить размеры в 0. Также, можно задать свой размер для кадрирования
<?php
add_action( 'after_setup_theme', 'custom_size' );
function custom_size() {
add_image_size( 'custom-size', 100, 100 );
}Теперь мы имеем 2 варианта по получению установленного изображения; первый – это получение url на оригинал изображения; второй – получение всего img-тега
<?php // Получение url изображения $src = esc_url(get_the_post_thumbnail_url()); // вывод url изображения the_post_thumbnail_url(); // Получение img-тега $img = get_the_post_thumbnail(); // вывод img-тега the_post_thumbnail();
Приведенный выше код работает только внутри вордпресс-цикла, иначе, в качестве первого аргумента, следует указать ID поста. Но функции вывода работают только в цикле. Размеры кадров я упомянул в предыдущем абзаце к тому, что в данных функциях(получение во втором, вывод первым) можно указывать в виде аргумента название размеров. Стандартно full(оригинал), large(1024px*1024px), medium(300px*300px) и thumbnail(150px*150px). Также, вместо строки с названием размера, можно указать массив с шириной и высотой; при этом, при первой загрузке страницы с новым размером, будет создан и сохранен новый кадр.
Касательно получения и выведения img-тега. Последним аргументом в функцию можно передать массив атрибутов тега. Поскольку, в данной функции задается дефолтное значение этого аргумента, то задать можно только такие атрибуты – src, class и alt.
Таксономии
Будь-то стандартные, либо же кастомные таксономии, древовидные они или нет – вывод их одинаков
<?php $terms = wp_get_post_terms( $post_id, $taxonomy, $args );
Первые 2 аргумента понятны, третий это поля, которые будут возвращены; по умолчанию all, возможные значения – names ids.
В случаи, если нужны особые условия применить к получаемым данным, можно применить
<?php $terms = wp_get_object_terms($object_ids, $taxonomies, $args);
здесь $object_ids это ID записи, а вот на $args, пожалуй, поподробнее:
orderby -поле, по которому сортировать порядок вывода:
- поиск в полях терминов – term_id, name, slug, term_group;
- по полям таксономии терминов – id(поле term_taxonomy_id в таблице БД), description, parent, count(количество терминов в таксономии);
- среди полей таблицы term_relationships – term_order;
- по полям меты – meta_value_num(количеству полей meta_value) и по значению meta_key
Можно и вообще отключить сортировку установив значение в ‘none’. Ключ order устанавливает порядок сортировки(ASK/DESC).
Также, в этом массиве можно задать множество других параметров поиска. К примеру, наличие/отсутствие/равенство метаполей, вложенность, древовидность и т.д. Получаем ссылку на архив термина
<?php $url = esc_url(get_term_link( $term, $taxonomy ));
Контент и метадата
Для начала выведем заголовок записи
<?php
// вывод
the_title('<h1>', '</h1>');
// получение в переменную
$title = esc_html(get_the_title());В случаи с выведением на страницу, указывать аргументы не обязательно, но в такой записи заголовок будет обрамлен H1-тегом. Не много о том, почему не стоит вытягивать post_title с обьекта WP_Post – потому, что запись может быть личной или защищенной паролем; при правильном выводе эти статусы являются частью заголовка, при неправильном – этот функционал придется дублировать… а это плохо!
Что касается контента, там все аналогично(почти)
<?php // вывод the_content(); // получение в переменную $content = get_the_content();
Опять таки, можно вытянуть контент с обьекта WP_Post, но в функции get_the_content происходит ряд об робок другими фильтрами…
Следующее, это вывод/получение даты, а также ссылки на ее архив
<?php $format = get_option( 'date_format' ); //вывод the_date( $format, $before, $after ); // получение $date = the_date( $format, $before, $after, false ); // или $date = get_the_date( $format, $post ); // ссылка на архив $url = esc_url(get_day_link( $year, $month, $day ));
Здесь, как и в случаи с заголовком, $before и $after это текст(включая html) до и после даты.
Пару слов о метадате поста. Вся дополнительная информация к посту – это метадата. Чтоб ее достать достаточно знать метаключ за коим она храниться
<?php $metadata = get_post_meta($post_id, $metakey, true);
поскольку одинаковых метаключей к одной записи может быть много, то данные извлекаются в виде массива; если поле одно – можно сразу его извлечь, указав true третьим аргументом – иначе, по умолчанию, будет false.
Навигация
Под контентом поста принято делать ссылки на предыдущий и следующий посты. Делается это следующим кодом
<?php $nav = get_the_post_navigation($args);
Переменная будет содержать следующую разметку
<nav class="navigation post-navigation" role="navigation" aria-label="Posts">
<h2 class="screen-reader-text">Post navigation</h2>
<div class="nav-links">
<div class="nav-previous">
<a href="{prevPostLink}" rel="prev">Prev Post Title</a>
</div>
<div class="nav-next">
<a href="{nextPostLink}" rel="next">Next Post Title</a>
</div>
</div>
</nav>Через аргумент функции моно кастомизировать только атрибуты class у и aria-label тега nav, а также то, что в ссылке. Для этого в массиве $args есть ключи prev_text(может содержать html-теги), next_text(тоже), aria_label и class.
Если же такой уровень кастомизации не достаточен и нужна своя разметка – делаем следующее: получаем а-теги
<?php $prevLink = get_previous_post_link(); $nextLink = get_next_post_link();
Комментарии
В вордпресс принято поключать комментарии таким образом
<?php if ( comments_open() || get_comments_number() ) : comments_template(); endif;
Здесь проверяется открытые ли комментарии у поста и их наличие. При выполнении хотя б одного условия – будут загружены стандартные форма комментирования и список комментов, которые можно перезаписать в файле comments.php темы.
Рекомендую сделать также, это избавит от некоторых проблем. Дальше, создаем файл comments.php и пишем в него.
Форма комментариев
Здесь ситуация похожа на ситуацию с навигацией: форму комментария строит отдельная функция.
<?php comment_form( $args, $post_id );
внутри цикла $post_id можно пропустить. При пустом $args получим следующий html
<div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-reply-title">Leave a Reply <small><a rel="nofollow" id="cancel-comment-reply-link" href="{cancelReplyLink}" style="display:none;">Cancel reply</a></small></h3>
<form action="{actionLink}" method="post" id="commentform" class="comment-form" novalidate="">
<!-- if current user logged in -->
<p class="logged-in-as">
<a href="{profileLink}" aria-label="Logged in as admin. Edit your profile.">Logged in as admin</a>. <a href="nonce=dc4f1aa958">Log out?</a>
</p>
<!-- endid -->
<p class="comment-form-comment">
<label for="comment">Comment</label>
<textarea id="comment" name="comment" cols="45" rows="8" maxlength="65525" required="required"></textarea>
</p>
<!-- if current user not logged in -->
<p class="comment-form-author">
<label for="author">Name <span class="required">*</span></label>
<input id="author" name="author" type="text" value="" size="30" maxlength="245" required="required">
</p>
<p class="comment-form-email">
<label for="email">Email <span class="required">*</span></label>
<input id="email" name="email" type="email" value="" size="30" maxlength="100" aria-describedby="email-notes" required="required">
</p>
<p class="comment-form-url">
<label for="url">Website</label>
<input id="url" name="url" type="url" value="" size="30" maxlength="200">
</p>
<p class="comment-form-cookies-consent">
<input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes"> <label for="wp-comment-cookies-consent">Save my name, email, and website in this browser for the next time I comment.</label>
</p>
<!-- endif -->
<p class="form-submit">
<input name="submit" type="submit" id="submit" class="submit" value="Post Comment">
<input type="hidden" name="comment_post_ID" value="10" id="comment_post_ID">
<input type="hidden" name="comment_parent" id="comment_parent" value="0">
</p>
<input type="hidden" id="_wp_unfiltered_html_comment_disabled" name="_wp_unfiltered_html_comment" value="f6a52e0808"><script>(function(){if(window===window.parent){document.getElementById('_wp_unfiltered_html_comment_disabled').name='_wp_unfiltered_html_comment';}})();</script>
</form>
</div>Но, в отличие от навигации, здесь можно провести полную кастомизацию через функцию. Для этого заполним массив $args:
- ключ fields – массив шаблонов(начиная с тега р), содержащий поля формы комментария за ключами author, email и url;
- comment_field – содержит шаблон, непосредственно, поля ввода комментария;
- must_log_in – текст для не залогиненных пользователей при обязательном логировании, чтоб оставить коммент;
- logged_in_as – текст для залогиненного пользователя;
- submit_field – шаблон поля отправки формы.
В принципе, для полной кастомизации этого достаточно, однако, в аргументе есть еще дополнительные поля для более тонкой настройки… и не только вида.
Немного о добавлении своих полей в форму комментирования: для добаления поля – фильтром comment_form_fields изменяй массив полей
<?php
add_filter('comment_form_fields', 'custom_comment_form_field');
function custom_comment_form_field($comment_fields)
{
$custom_field['custom_field'] = '<p><label>My query<input name="my_query"></label></p>';
// insert $custom_field after name(index = 1)
return array_merge(
array_slice($comment_fields, 0, 2),
$custom_field,
array_slice($comment_fields, 2)
);
}после этого экшном wp_insert_comment нужно сохранить данное поле
<?php
add_action('wp_insert_comment', 'wp_insert_custom_comment_field', 10, 2);
function wp_insert_custom_comment_field($id, $comment)
{
if (isset($_POST['my_query'])) {
update_comment_meta($id, 'my_query', sanitize_text_field($_POST['my_query']));
}
}Список комментариев
Следующая вещь, которая, по сути, есть продолжением формы комментариев – это список ранее оставленных комментариев. Он также выводится функцией
<?php wp_list_comments($args, $comments);
При использовании стандартной схемы аргумент $comments указывать не нужно. Иначе, $comments = get_comments() в котором нужно формировать свой массив аргументов. При пустом $args wp_list_comments выведет следующую разметку для каждого коммента
<li id="comment-1" class="comment even thread-even depth-1">
<article id="div-comment-1" class="comment-body">
<footer class="comment-meta">
<div class="comment-author vcard">
<img alt="" src="{avatar}" srcset="{avatar}" class="avatar avatar-32 photo" height="32" width="32" loading="lazy">
<b class="fn"><a href="https://wordpress.org/" rel="external nofollow ugc" class="url">A WordPress Commenter</a></b>
<span class="says">says:</span>
</div><!-- .comment-author -->
<div class="comment-metadata">
<a href="{linkToComment}">
<time datetime="{commentTime}">
Comment Time
</time>
</a>
<span class="edit-link">
<a class="comment-edit-link" href="{linkToEditComment}">Edit</a>
</span>
</div><!-- .comment-metadata -->
</footer><!-- .comment-meta -->
<div class="comment-content">
<p>Comment Text</p>
</div><!-- .comment-content -->
<div class="reply">
<a rel="nofollow" class="comment-reply-link" href="{linkToReply}" data-commentid="1" data-postid="1" data-belowelement="div-comment-1" data-respondelement="respond" data-replyto="Reply to A WordPress Commenter" aria-label="Reply to A WordPress Commenter">Reply</a>
</div>
</article><!-- .comment-body -->
</li>Здесь самый широкий, даже, наверное, излишний, способ кастомизации – и все доступно через функцию:
- max_depth – максимально разрешенная вложенность комментов;
- style – стиль вывода дерева – ul, ol(по умолчанию) или div. Данный аргумент имеет значение для вложенных комментариев, поскольку сама функция должна быть при вызове обнесенная контейнером отдельно;
- type – отображаемый тип комментариев;
- per_page – в случаи необходимости пагинации – количество комментариев на странице;
- page – страница пагинации коммента;
- avatar_size – размер аватара комментатора
- callback – функция-строитель каждого комментария, без закрывающего тега;
- end-callback – строитель закрывающего тега;
- walker экземпляр класса-строителя дерева комментариев.
Есть еще много ключей для “мелкой настройки” этой функции, но главное уже приведено. По поводу того, что функции открывающего и закрывающего тега разделены – тут, как и в меню, есть вложенность, и для ее реализации сделано так.
Сайдбар
Сайдбар – это место для вывода виджетов, виджет-зона. У нее, также как и у комментов, свой стандарт для выведения. В шаблоне страницы ставят такой код
<?php get_sidebar($slug);
данный код подключит sidebar.php из темы; если $slug не пустая строка, то подключит sidebar-{$slug}.php
<?php
if (is_active_sidebar($slug)) {
dynamic_sidebar($slug);
}$slug здесь это идентификатор виджет-зоны, который был указан при ее регистраци в функции register_sidebar().
Заключение
Рассмотрены все стандартные элементы шаблон страницы записи WordPress. О чем стоит упомянуть еще – так это о том, что в обертке каждого поста должны быть теги
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<!-- post content -->
</article>это анкор для ссылки в шапке. Считаю, что рассказал вам обо всех элементах сих страниц, так как дополнительные – это виджеты, вставляемые в сайдбар. И кстати, некоторые из этих элементов встречаются и на страницах других типов.