Хлебные крошки с произвольным типом записи без плагина
Я использовал следующую функцию для хлебных крошек на всех сайтах до настоящего времени, но сегодняшний сайт имеет 3 произвольных типа записей, и клиент хочет, чтобы хлебные крошки работали правильно. Например, вместо "главная / вина / naire blanco" мы получаем только "главная / / naire blanco".
function the_breadcrumb() {
echo '<ol class="breadcrumb" itemprop="breadcrumb">';
if (!is_home()) {
echo '<li><a href="';
echo get_option('home');
echo '">';
echo 'Главная';
echo '</a></li>';
if (is_category() || is_single() || is_post_type()) {
echo '<li>';
the_category(' </li><li> ');
echo get_post_type(' </li><li> ');
if (is_single()) {
echo '</li><li>';
the_title();
echo '</li>';
}
} elseif (is_page()) {
if($post->post_parent){
$anc = get_post_ancestors( $post->ID );
foreach ( $anc as $ancestor ) {
$output = $output . '<li><a href="'.get_permalink($ancestor).'" title="'.get_the_title($ancestor).'">'.get_the_title($ancestor).'</a></li> ';
}
echo $output;
echo '<strong title="'.$title.'"> '.$title.'</strong>';
} else {
echo '<li><a class="active" href="';
echo the_permalink();
echo '">';
echo the_title();
echo '</a></li>';
}
}
}
elseif (is_tag()) {single_tag_title();}
elseif (is_day()) {echo"<li>Архив за "; the_time('F jS, Y'); echo'</li>';}
elseif (is_month()) {echo"<li>Архив за "; the_time('F, Y'); echo'</li>';}
elseif (is_year()) {echo"<li>Архив за "; the_time('Y'); echo'</li>';}
elseif (is_author()) {echo"<li>Архив автора"; echo'</li>';}
elseif (isset($_GET['paged']) && !empty($_GET['paged'])) {echo "<li>Архивы блога"; echo'</li>';}
elseif (is_search()) {echo"<li>Результаты поиска"; echo'</li>';}
echo '</ol>';
}
Как видно из кода, я попытался добавить || is_post_type()
после is_category() || is_single()
с echo get_post_type()
, но это не дало никакого результата.
Буду признателен за помощь.
Спасибо

Основная проблема большинства функций для хлебных крошек заключается в том, что они полагаются на глобальный объект $post
на всех страницах. Глобальный объект $post
не только крайне ненадежен (см. мою статью здесь), но он также может не содержать нужных нам данных, даже если сам объект не был скомпрометирован.
Глобальный объект $post
на архивных страницах (включая категории, метки, даты, таксономии, авторов и архивы пользовательских типов записей) содержит первую запись в цикле до начала самого цикла. Нет гарантии, что первая запись будет из выбранного архива (страницы, на которой вы находитесь), что означает, что информация может быть неверной, даже если глобальный объект $post
не был скомпрометирован. Ситуации, когда первая запись может не относиться к выбранному архиву, включают кастомные закрепленные записи и фильтр the_posts
.
Я недавно переписал свою старую функцию для хлебных крошек, которая использует объект $GLOBALS['wp_the_query']
и сохраненный в нем queried object. Таким образом, мы получаем 99.999% надежные хлебные крошки, не зависящие от глобального объекта $post
или каких-либо скомпрометированных данных.
Вот сама функция: (Требуется PHP 5.4+. Можете свободно изменять по необходимости)
function get_hansel_and_gretel_breadcrumbs()
{
// Устанавливаем переменные для дальнейшего использования
$here_text = __( 'Вы находитесь здесь!' );
$home_link = home_url('/');
$home_text = __( 'Главная' );
$link_before = '<span typeof="v:Breadcrumb">';
$link_after = '</span>';
$link_attr = ' rel="v:url" property="v:title"';
$link = $link_before . '<a' . $link_attr . ' href="%1$s">%2$s</a>' . $link_after;
$delimiter = ' » '; // Разделитель между крошками
$before = '<span class="current">'; // Тег перед текущей крошкой
$after = '</span>'; // Тег после текущей крошки
$page_addon = ''; // Добавляет номер страницы, если запрос разбит на страницы
$breadcrumb_trail = '';
$category_links = '';
/**
* Устанавливаем собственную переменную $wp_the_query. Не используем глобальную версию
* из-за ненадежности
*/
$wp_the_query = $GLOBALS['wp_the_query'];
$queried_object = $wp_the_query->get_queried_object();
// Обрабатываем одиночные записи, включая страницы, посты и вложения
if ( is_singular() )
{
/**
* Устанавливаем собственную переменную $post. Не используем глобальную версию
* из-за ненадежности. Устанавливаем $post_object в $GLOBALS['wp_the_query']
*/
$post_object = sanitize_post( $queried_object );
// Устанавливаем переменные
$title = apply_filters( 'the_title', $post_object->post_title );
$parent = $post_object->post_parent;
$post_type = $post_object->post_type;
$post_id = $post_object->ID;
$post_link = $before . $title . $after;
$parent_string = '';
$post_type_link = '';
if ( 'post' === $post_type )
{
// Получаем категории поста
$categories = get_the_category( $post_id );
if ( $categories ) {
// Берем первую категорию
$category = $categories[0];
$category_links = get_category_parents( $category, true, $delimiter );
$category_links = str_replace( '<a', $link_before . '<a' . $link_attr, $category_links );
$category_links = str_replace( '</a>', '</a>' . $link_after, $category_links );
}
}
if ( !in_array( $post_type, ['post', 'page', 'attachment'] ) )
{
$post_type_object = get_post_type_object( $post_type );
$archive_link = esc_url( get_post_type_archive_link( $post_type ) );
$post_type_link = sprintf( $link, $archive_link, $post_type_object->labels->singular_name );
}
// Получаем родительские посты, если $parent !== 0
if ( 0 !== $parent )
{
$parent_links = [];
while ( $parent ) {
$post_parent = get_post( $parent );
$parent_links[] = sprintf( $link, esc_url( get_permalink( $post_parent->ID ) ), get_the_title( $post_parent->ID ) );
$parent = $post_parent->post_parent;
}
$parent_links = array_reverse( $parent_links );
$parent_string = implode( $delimiter, $parent_links );
}
// Собираем цепочку хлебных крошек
if ( $parent_string ) {
$breadcrumb_trail = $parent_string . $delimiter . $post_link;
} else {
$breadcrumb_trail = $post_link;
}
if ( $post_type_link )
$breadcrumb_trail = $post_type_link . $delimiter . $breadcrumb_trail;
if ( $category_links )
$breadcrumb_trail = $category_links . $breadcrumb_trail;
}
// Обрабатываем архивы, включая категории, метки, таксономии, даты, пользовательские типы записей и архивы авторов
if( is_archive() )
{
if ( is_category()
|| is_tag()
|| is_tax()
) {
// Устанавливаем переменные для этого раздела
$term_object = get_term( $queried_object );
$taxonomy = $term_object->taxonomy;
$term_id = $term_object->term_id;
$term_name = $term_object->name;
$term_parent = $term_object->parent;
$taxonomy_object = get_taxonomy( $taxonomy );
$current_term_link = $before . $taxonomy_object->labels->singular_name . ': ' . $term_name . $after;
$parent_term_string = '';
if ( 0 !== $term_parent )
{
// Получаем всех предков текущего элемента таксономии
$parent_term_links = [];
while ( $term_parent ) {
$term = get_term( $term_parent, $taxonomy );
$parent_term_links[] = sprintf( $link, esc_url( get_term_link( $term ) ), $term->name );
$term_parent = $term->parent;
}
$parent_term_links = array_reverse( $parent_term_links );
$parent_term_string = implode( $delimiter, $parent_term_links );
}
if ( $parent_term_string ) {
$breadcrumb_trail = $parent_term_string . $delimiter . $current_term_link;
} else {
$breadcrumb_trail = $current_term_link;
}
} elseif ( is_author() ) {
$breadcrumb_trail = __( 'Архив автора: ') . $before . $queried_object->data->display_name . $after;
} elseif ( is_date() ) {
// Устанавливаем стандартные переменные
$year = $wp_the_query->query_vars['year'];
$monthnum = $wp_the_query->query_vars['monthnum'];
$day = $wp_the_query->query_vars['day'];
// Получаем название месяца, если $monthnum имеет значение
if ( $monthnum ) {
$date_time = DateTime::createFromFormat( '!m', $monthnum );
$month_name = $date_time->format( 'F' );
}
if ( is_year() ) {
$breadcrumb_trail = $before . $year . $after;
} elseif( is_month() ) {
$year_link = sprintf( $link, esc_url( get_year_link( $year ) ), $year );
$breadcrumb_trail = $year_link . $delimiter . $before . $month_name . $after;
} elseif( is_day() ) {
$year_link = sprintf( $link, esc_url( get_year_link( $year ) ), $year );
$month_link = sprintf( $link, esc_url( get_month_link( $year, $monthnum ) ), $month_name );
$breadcrumb_trail = $year_link . $delimiter . $month_link . $delimiter . $before . $day . $after;
}
} elseif ( is_post_type_archive() ) {
$post_type = $wp_the_query->query_vars['post_type'];
$post_type_object = get_post_type_object( $post_type );
$breadcrumb_trail = $before . $post_type_object->labels->singular_name . $after;
}
}
// Обрабатываем страницу поиска
if ( is_search() ) {
$breadcrumb_trail = __( 'Результаты поиска для: ' ) . $before . get_search_query() . $after;
}
// Обрабатываем 404 ошибки
if ( is_404() ) {
$breadcrumb_trail = $before . __( 'Ошибка 404' ) . $after;
}
// Обрабатываем страницы с пагинацией
if ( is_paged() ) {
$current_page = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : get_query_var( 'page' );
$page_addon = $before . sprintf( __( ' (Страница %s )' ), number_format_i18n( $current_page ) ) . $after;
}
$breadcrumb_output_link = '';
$breadcrumb_output_link .= '<div class="breadcrumb">';
if ( is_home()
|| is_front_page()
) {
// Не показываем хлебные крошки на первой странице главной и фронтпейджа
if ( is_paged() ) {
$breadcrumb_output_link .= $here_text . $delimiter;
$breadcrumb_output_link .= '<a href="' . $home_link . '">' . $home_text . '</a>';
$breadcrumb_output_link .= $page_addon;
}
} else {
$breadcrumb_output_link .= $here_text . $delimiter;
$breadcrumb_output_link .= '<a href="' . $home_link . '" rel="v:url" property="v:title">' . $home_text . '</a>';
$breadcrumb_output_link .= $delimiter;
$breadcrumb_output_link .= $breadcrumb_trail;
$breadcrumb_output_link .= $page_addon;
}
$breadcrumb_output_link .= '</div><!-- .breadcrumbs -->';
return $breadcrumb_output_link;
}
Вы можете вызвать её следующим образом
echo get_hansel_and_gretel_breadcrumbs();
в нужном месте
Эта функция также корректно обрабатывает пользовательские типы записей.

Ваш код работает отлично, но Google скоро прекратит поддержку разметки data-vocabulary (https://webmasters.googleblog.com/2020/01/data-vocabulary.html), и код начнет выдавать ошибки в Search Console. Я изменил его, чтобы сделать совместимым с schema.org (https://schema.org/BreadcrumbList). Я почти закончил, но не могу реализовать значение для itemprop="position". Я новичок в сообществе StackExchange и пока не знаю, как вставлять код, поэтому использовал gist, чтобы показать свой код: https://gist.github.com/Marinski/4cefcbb7ded15582c4adfa0dc1c53946. Есть ли у вас предложения, как это сделать? Спасибо.

Отлично! Код из принятого ответа (от Pieter Goosen) работает.
Примечание: Я добавил небольшую дополнительную задачу. Сделайте проверку существования функции перед её вызовом, вот так:
<?php
if(function_exists('get_hansel_and_gretel_breadcrumbs')):
echo get_hansel_and_gretel_breadcrumbs();
endif;
?>
Дополнительно, я сохранил эту функцию в файл с именем breadcrumbs.php
в корневой папке проекта и подключил его в файл functions.php с помощью include_once('breadcrumbs.php');
для лучшей организации кода.

function get_hansel_and_gretel_breadcrumbs()
{
// Установка переменных для последующего использования
$here_text = __( 'Вы находитесь здесь!' );
$home_link = home_url('/');
$home_text = __( 'Главная' );
$link_before = '<span typeof="v:Breadcrumb">';
$link_after = '</span>';
$link_attr = ' rel="v:url" property="v:title"';
$link = $link_before . '<a' . $link_attr . ' href="%1$s">%2$s</a>' . $link_after;
$delimiter = ' » '; // Разделитель между элементами
$before = '<span class="current">'; // Тег перед текущим элементом
$after = '</span>'; // Тег после текущего элемента
$page_addon = ''; // Добавляет номер страницы, если запрос разбит на страницы
$breadcrumb_trail = '';
$category_links = '';
/**
* Устанавливаем свою переменную $wp_the_query. Не используем глобальную переменную
* из-за проблем с надежностью
*/
$wp_the_query = $GLOBALS['wp_the_query'];
$queried_object = $wp_the_query->get_queried_object();
// Обработка одиночных записей, включая страницы, записи и вложения
if ( is_singular() )
{
/**
* Устанавливаем свою переменную $post. Не используем глобальную переменную
* из-за проблем с надежностью. Устанавливаем $post_object в $GLOBALS['wp_the_query']
*/
$post_object = sanitize_post( $queried_object );
// Установка переменных
$title = apply_filters( 'the_title', $post_object->post_title );
$parent = $post_object->post_parent;
$post_type = $post_object->post_type;
$post_id = $post_object->ID;
$post_link = $before . $title . $after;
$parent_string = '';
$post_type_link = '';
if ( 'post' === $post_type )
{
// Получаем категории записи
$categories = get_the_category( $post_id );
if ( $categories ) {
// Берем первую категорию
$category = $categories[0];
$category_links = get_category_parents( $category, true, $delimiter );
$category_links = str_replace( '<a', $link_before . '<a' . $link_attr, $category_links );
$category_links = str_replace( '</a>', '</a>' . $link_after, $category_links );
}
}
if ( !in_array( $post_type, ['post', 'page', 'attachment'] ) )
{
$post_type_object = get_post_type_object( $post_type );
$archive_link = esc_url( get_post_type_archive_link( $post_type ) );
$post_type_link = sprintf( $link, $archive_link, $post_type_object->labels->singular_name );
}
// Получаем родительские записи, если $parent !== 0
if ( 0 !== $parent )
{
$parent_links = [];
while ( $parent ) {
$post_parent = get_post( $parent );
$parent_links[] = sprintf( $link, esc_url( get_permalink( $post_parent->ID ) ), get_the_title( $post_parent->ID ) );
$parent = $post_parent->post_parent;
}
$parent_links = array_reverse( $parent_links );
$parent_string = implode( $delimiter, $parent_links );
}
// Формируем цепочку навигации
if ( $parent_string ) {
$breadcrumb_trail = $parent_string . $delimiter . $post_link;
} else {
$breadcrumb_trail = $post_link;
}
if ( $post_type_link )
$breadcrumb_trail = $post_type_link . $delimiter . $breadcrumb_trail;
if ( $category_links )
$breadcrumb_trail = $category_links . $delimiter . $breadcrumb_trail;
}
// Обработка архивов, включая категории, метки, таксономии, даты, пользовательские типы записей и архивы авторов
if( is_archive() )
{
if ( is_category()
|| is_tag()
|| is_tax()
) {
// Установка переменных для этого раздела
$term_object = get_term( $queried_object );
$taxonomy = $term_object->taxonomy;
$term_id = $term_object->term_id;
$term_name = $term_object->name;
$term_parent = $term_object->parent;
$taxonomy_object = get_taxonomy( $taxonomy );
$current_term_link = $before . $taxonomy_object->labels->singular_name . ': ' . $term_name . $after;
$parent_term_string = '';
if ( 0 !== $term_parent )
{
// Получаем всех предков текущего элемента таксономии
$parent_term_links = [];
while ( $term_parent ) {
$term = get_term( $term_parent, $taxonomy );
$parent_term_links[] = sprintf( $link, esc_url( get_term_link( $term ) ), $term->name );
$term_parent = $term->parent;
}
$parent_term_links = array_reverse( $parent_term_links );
$parent_term_string = implode( $delimiter, $parent_term_links );
}
if ( $parent_term_string ) {
$breadcrumb_trail = $parent_term_string . $delimiter . $current_term_link;
} else {
$breadcrumb_trail = $current_term_link;
}
} elseif ( is_author() ) {
$breadcrumb_trail = __( 'Архив автора: ') . $before . $queried_object->data->display_name . $after;
} elseif ( is_date() ) {
// Установка переменных по умолчанию
$year = $wp_the_query->query_vars['year'];
$monthnum = $wp_the_query->query_vars['monthnum'];
$day = $wp_the_query->query_vars['day'];
// Получаем название месяца, если $monthnum имеет значение
if ( $monthnum ) {
$date_time = DateTime::createFromFormat( '!m', $monthnum );
$month_name = $date_time->format( 'F' );
}
if ( is_year() ) {
$breadcrumb_trail = $before . $year . $after;
} elseif( is_month() ) {
$year_link = sprintf( $link, esc_url( get_year_link( $year ) ), $year );
$breadcrumb_trail = $year_link . $delimiter . $before . $month_name . $after;
} elseif( is_day() ) {
$year_link = sprintf( $link, esc_url( get_year_link( $year ) ), $year );
$month_link = sprintf( $link, esc_url( get_month_link( $year, $monthnum ) ), $month_name );
$breadcrumb_trail = $year_link . $delimiter . $month_link . $delimiter . $before . $day . $after;
}
} elseif ( is_post_type_archive() ) {
$post_type = $wp_the_query->query_vars['post_type'];
$post_type_object = get_post_type_object( $post_type );
$breadcrumb_trail = $before . $post_type_object->labels->singular_name . $after;
}
}
// Обработка страницы поиска
if ( is_search() ) {
$breadcrumb_trail = __( 'Результаты поиска для: ' ) . $before . get_search_query() . $after;
}
// Обработка 404 ошибки
if ( is_404() ) {
$breadcrumb_trail = $before . __( 'Ошибка 404' ) . $after;
}
// Обработка страниц с пагинацией
if ( is_paged() ) {
$current_page = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : get_query_var( 'page' );
$page_addon = $before . sprintf( __( ' (Страница %s)' ), number_format_i18n( $current_page ) ) . $after;
}
$breadcrumb_output_link = '';
$breadcrumb_output_link .= '<div class="breadcrumb">';
if ( is_home()
|| is_front_page()
) {
// Не показывать хлебные крошки на первой странице главной или лендинга
if ( is_paged() ) {
$breadcrumb_output_link .= $here_text . $delimiter;
$breadcrumb_output_link .= '<a href="' . $home_link . '">' . $home_text . '</a>';
$breadcrumb_output_link .= $page_addon;
}
} else {
$breadcrumb_output_link .= $here_text . $delimiter;
$breadcrumb_output_link .= '<a href="' . $home_link . '" rel="v:url" property="v:title">' . $home_text . '</a>';
$breadcrumb_output_link .= $delimiter;
$breadcrumb_output_link .= $breadcrumb_trail;
$breadcrumb_output_link .= $page_addon;
}
$breadcrumb_output_link .= '</div><!-- .breadcrumbs -->';
return $breadcrumb_output_link;
}
