Шаблон страницы записи WordPress

Все страницы сайта — это PHP шаблоны, содержащие HTML разметку с PHP тегами. Здесь будет рассмотрено создание шаблона страницы для отдельной записи — начиная от вордпресс цикла и до построения элементов навигации…

Содержание

Что представляет собой запись?

Каждая запись это отдельный представитель типа поста. Из коробки, есть 2 типа постов — страницы и записи; у каждой свои приколы по отображению — у страниц шаблоны для отображения, записей — форматы

шаблоны и форматы
1 — шаблоны страниц; 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' );
}

После этого, на странице редактирования записи появиться такой метабокс

thumbnail metabox Шаблон страницы записи WordPress

Теперь о том, как сохраняется загруженное изображение. По умолчанию, при сохранении , сохраняется оригинал и три нарезанных картинки из оригинала. Перейдя по «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>

это анкор для ссылки в шапке. Считаю, что рассказал вам обо всех элементах сих страниц, так как дополнительные — это виджеты, вставляемые в сайдбар. И кстати, некоторые из этих элементов встречаются и на страницах других типов.