adminvps

«Хлебные крошки» для WordPress без использования плагина

«Хлебные крошки» — это важный элемент навигации веб-сайта, который повышает его юзабилити. Особенно это касается сайтов со сложной структурой. Я, к сожалению (а, может, и не к сожалению), не использую их на большинстве своих сайтов, возможно, потому, что у них слишком простая структура (для такого сайта, как этот, они, вроде бы, и не нужны).

Когда я решаю какую-либо задачу на WordPress-сайте, то всегда стараюсь обходится без плагинов, где это бывает возможно. Зачастую одна и та же задача может быть решена с меньшим количеством кода и бывает менее ресурсоемкой по сравнению с применением плагинов.

В Интернете я находил разные варианты реализации «хлебных крошек» без использования плагинов, но ни один из них меня не устроил, поскольку все они не показывали полную цепочку ссылок. Поэтому я создал свою функцию «хлебных крошек» для WordPress. И в данном посте хочу поделиться этой функцией с вами.

Особенности функции

  • Отображается полная цепочка ссылок до текущей страницы. Например, если текущая страница находится в рубрике второго уровня, то цепочка будет выглядеть следующим образом:

    Главная > Рубрика > Подрубрика > Название статьи

    Во всех решениях, которые я встречал (за исключением плагинов), такая цепочка выглядела вот так:

    Главная > Подрубрика > Название статьи

    Т.е. одно звено потеряно.

    Аналогично у меня выглядят и «крошки» для страниц. К примеру, для страницы 3-го уровня вложенности цепочка будет такой:

    Главная > Страница 1-го уровня > Страница 2-го уровня > Страница 3-го уровня

  • «Хлебные крошки» выводятся для следующих типов страниц WordPress-сайта:

    • постраничная навигация с главной страницы (вида site.ru/page/2/);
    • архив рубрики;
    • архив тега;
    • архив за день;
    • архив за месяц;
    • архив за год;
    • архив автора;
    • произвольный тип записи;
    • страница;
    • пост;
    • результаты поиска;
    • страница с ошибкой 404.
  • Добавляется порядковый номер страницы, если это 2-я или больше страница архивов.
  • Можно задать любой символ разделителя между ссылками.
  • Можно задать текст для ссылка «Главная».
  • Интегрирована микроразметка Schema.org.

Функция «Хлебные крошки» для WordPress (обновлено: 05.10.2018)

/*
 * "Хлебные крошки" для WordPress
 * автор: Dimox
 * версия: 2018.10.05
 * лицензия: MIT
*/
function dimox_breadcrumbs() {

	/* === ОПЦИИ === */
	$text['home'] = 'Главная'; // текст ссылки "Главная"
	$text['category'] = '%s'; // текст для страницы рубрики
	$text['search'] = 'Результаты поиска по запросу "%s"'; // текст для страницы с результатами поиска
	$text['tag'] = 'Записи с тегом "%s"'; // текст для страницы тега
	$text['author'] = 'Статьи автора %s'; // текст для страницы автора
	$text['404'] = 'Ошибка 404'; // текст для страницы 404
	$text['page'] = 'Страница %s'; // текст 'Страница N'
	$text['cpage'] = 'Страница комментариев %s'; // текст 'Страница комментариев N'

	$wrap_before = '<div class="breadcrumbs" itemscope itemtype="http://schema.org/BreadcrumbList">'; // открывающий тег обертки
	$wrap_after = '</div><!-- .breadcrumbs -->'; // закрывающий тег обертки
	$sep = '<span class="breadcrumbs__separator"> › </span>'; // разделитель между "крошками"
	$before = '<span class="breadcrumbs__current">'; // тег перед текущей "крошкой"
	$after = '</span>'; // тег после текущей "крошки"

	$show_on_home = 0; // 1 - показывать "хлебные крошки" на главной странице, 0 - не показывать
	$show_home_link = 1; // 1 - показывать ссылку "Главная", 0 - не показывать
	$show_current = 1; // 1 - показывать название текущей страницы, 0 - не показывать
	$show_last_sep = 1; // 1 - показывать последний разделитель, когда название текущей страницы не отображается, 0 - не показывать
	/* === КОНЕЦ ОПЦИЙ === */

	global $post;
	$home_url = home_url('/');
	$link = '<span itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">';
	$link .= '<a class="breadcrumbs__link" href="%1$s" itemprop="item"><span itemprop="name">%2$s</span></a>';
	$link .= '<meta itemprop="position" content="%3$s" />';
	$link .= '</span>';
	$parent_id = ( $post ) ? $post->post_parent : '';
	$home_link = sprintf( $link, $home_url, $text['home'], 1 );

	if ( is_home() || is_front_page() ) {

		if ( $show_on_home ) echo $wrap_before . $home_link . $wrap_after;

	} else {

		$position = 0;

		echo $wrap_before;

		if ( $show_home_link ) {
			$position += 1;
			echo $home_link;
		}

		if ( is_category() ) {
			$parents = get_ancestors( get_query_var('cat'), 'category' );
			foreach ( array_reverse( $parents ) as $cat ) {
				$position += 1;
				if ( $position > 1 ) echo $sep;
				echo sprintf( $link, get_category_link( $cat ), get_cat_name( $cat ), $position );
			}
			if ( get_query_var( 'paged' ) ) {
				$position += 1;
				$cat = get_query_var('cat');
				echo $sep . sprintf( $link, get_category_link( $cat ), get_cat_name( $cat ), $position );
				echo $sep . $before . sprintf( $text['page'], get_query_var( 'paged' ) ) . $after;
			} else {
				if ( $show_current ) {
					if ( $position >= 1 ) echo $sep;
					echo $before . sprintf( $text['category'], single_cat_title( '', false ) ) . $after;
				} elseif ( $show_last_sep ) echo $sep;
			}

		} elseif ( is_search() ) {
			if ( $show_home_link && $show_current || ! $show_current && $show_last_sep ) echo $sep;
			if ( $show_current ) echo $before . sprintf( $text['search'], get_search_query() ) . $after;

		} elseif ( is_year() ) {
			if ( $show_home_link && $show_current ) echo $sep;
			if ( $show_current ) echo $before . get_the_time('Y') . $after;
			elseif ( $show_home_link && $show_last_sep ) echo $sep;

		} elseif ( is_month() ) {
			if ( $show_home_link ) echo $sep;
			$position += 1;
			echo sprintf( $link, get_year_link( get_the_time('Y') ), get_the_time('Y'), $position );
			if ( $show_current ) echo $sep . $before . get_the_time('F') . $after;
			elseif ( $show_last_sep ) echo $sep;

		} elseif ( is_day() ) {
			if ( $show_home_link ) echo $sep;
			$position += 1;
			echo sprintf( $link, get_year_link( get_the_time('Y') ), get_the_time('Y'), $position ) . $sep;
			$position += 1;
			echo sprintf( $link, get_month_link( get_the_time('Y'), get_the_time('m') ), get_the_time('F'), $position );
			if ( $show_current ) echo $sep . $before . get_the_time('d') . $after;
			elseif ( $show_last_sep ) echo $sep;

		} elseif ( is_single() && ! is_attachment() ) {
			if ( get_post_type() != 'post' ) {
				$position += 1;
				$post_type = get_post_type_object( get_post_type() );
				if ( $position > 1 ) echo $sep;
				echo sprintf( $link, get_post_type_archive_link( $post_type->name ), $post_type->labels->name, $position );
				if ( $show_current ) echo $sep . $before . get_the_title() . $after;
				elseif ( $show_last_sep ) echo $sep;
			} else {
				$cat = get_the_category(); $catID = $cat[0]->cat_ID;
				$parents = get_ancestors( $catID, 'category' );
				$parents = array_reverse( $parents );
				$parents[] = $catID;
				foreach ( $parents as $cat ) {
					$position += 1;
					if ( $position > 1 ) echo $sep;
					echo sprintf( $link, get_category_link( $cat ), get_cat_name( $cat ), $position );
				}
				if ( get_query_var( 'cpage' ) ) {
					$position += 1;
					echo $sep . sprintf( $link, get_permalink(), get_the_title(), $position );
					echo $sep . $before . sprintf( $text['cpage'], get_query_var( 'cpage' ) ) . $after;
				} else {
					if ( $show_current ) echo $sep . $before . get_the_title() . $after;
					elseif ( $show_last_sep ) echo $sep;
				}
			}

		} elseif ( is_post_type_archive() ) {
			$post_type = get_post_type_object( get_post_type() );
			if ( get_query_var( 'paged' ) ) {
				$position += 1;
				if ( $position > 1 ) echo $sep;
				echo sprintf( $link, get_post_type_archive_link( $post_type->name ), $post_type->label, $position );
				echo $sep . $before . sprintf( $text['page'], get_query_var( 'paged' ) ) . $after;
			} else {
				if ( $show_home_link && $show_current ) echo $sep;
				if ( $show_current ) echo $before . $post_type->label . $after;
				elseif ( $show_home_link && $show_last_sep ) echo $sep;
			}

		} elseif ( is_attachment() ) {
			$parent = get_post( $parent_id );
			$cat = get_the_category( $parent->ID ); $catID = $cat[0]->cat_ID;
			$parents = get_ancestors( $catID, 'category' );
			$parents = array_reverse( $parents );
			$parents[] = $catID;
			foreach ( $parents as $cat ) {
				$position += 1;
				if ( $position > 1 ) echo $sep;
				echo sprintf( $link, get_category_link( $cat ), get_cat_name( $cat ), $position );
			}
			$position += 1;
			echo $sep . sprintf( $link, get_permalink( $parent ), $parent->post_title, $position );
			if ( $show_current ) echo $sep . $before . get_the_title() . $after;
			elseif ( $show_last_sep ) echo $sep;

		} elseif ( is_page() && ! $parent_id ) {
			if ( $show_home_link && $show_current ) echo $sep;
			if ( $show_current ) echo $before . get_the_title() . $after;
			elseif ( $show_home_link && $show_last_sep ) echo $sep;

		} elseif ( is_page() && $parent_id ) {
			$parents = get_post_ancestors( get_the_ID() );
			foreach ( array_reverse( $parents ) as $pageID ) {
				$position += 1;
				if ( $position > 1 ) echo $sep;
				echo sprintf( $link, get_page_link( $pageID ), get_the_title( $pageID ), $position );
			}
			if ( $show_current ) echo $sep . $before . get_the_title() . $after;
			elseif ( $show_last_sep ) echo $sep;

		} elseif ( is_tag() ) {
			if ( get_query_var( 'paged' ) ) {
				$position += 1;
				$tagID = get_query_var( 'tag_id' );
				echo $sep . sprintf( $link, get_tag_link( $tagID ), single_tag_title( '', false ), $position );
				echo $sep . $before . sprintf( $text['page'], get_query_var( 'paged' ) ) . $after;
			} else {
				if ( $show_home_link && $show_current ) echo $sep;
				if ( $show_current ) echo $before . sprintf( $text['tag'], single_tag_title( '', false ) ) . $after;
				elseif ( $show_home_link && $show_last_sep ) echo $sep;
			}

		} elseif ( is_author() ) {
			$author = get_userdata( get_query_var( 'author' ) );
			if ( get_query_var( 'paged' ) ) {
				$position += 1;
				echo $sep . sprintf( $link, get_author_posts_url( $author->ID ), sprintf( $text['author'], $author->display_name ), $position );
				echo $sep . $before . sprintf( $text['page'], get_query_var( 'paged' ) ) . $after;
			} else {
				if ( $show_home_link && $show_current ) echo $sep;
				if ( $show_current ) echo $before . sprintf( $text['author'], $author->display_name ) . $after;
				elseif ( $show_home_link && $show_last_sep ) echo $sep;
			}

		} elseif ( is_404() ) {
			if ( $show_home_link && $show_current ) echo $sep;
			if ( $show_current ) echo $before . $text['404'] . $after;
			elseif ( $show_last_sep ) echo $sep;

		} elseif ( has_post_format() && ! is_singular() ) {
			if ( $show_home_link && $show_current ) echo $sep;
			echo get_post_format_string( get_post_format() );
		}

		echo $wrap_after;

	}
} // end of dimox_breadcrumbs()

Функцию необходимо поместить в файл functions.php вашей WordPress-темы. После этого в то место шаблона, где хотите выводить «хлебные крошки», добавьте следующий код:

<?php if ( function_exists( 'dimox_breadcrumbs' ) ) dimox_breadcrumbs(); ?>

Единственное, что теперь останется сделать — оформить их с помощью CSS. Для этого к блоку «хлебных крошек» предусмотрен класс .breadcrumbs, для разделителя — .breadcrumbs__separator а для текущей «крошки» — .breadcrumbs__current.

Полезные комментарии (2)
Комментарии (617)
  1. 1
    Алексей

    И сразу еще один вопрос, если по тегам можно выводить, то можно сделать так, чтобы выводился не один тег, а три. Дело в том, что у меня почти каждая статья привязана к трем тегам. Еще раз спасибо!

  2. 2
    Евгений

    А никого за столько лет не смутило, что микроразметка неверно сделана? Размечается только первый элемент (главная), дальше wrap_before и wrap_after не используются

  3. 4
    Петр
    @

    Null возвращает.. что может быть не так?

  4. 6
    Женя Домаскин
    @

    Каким образом можно убрать рубрики с данных крошек.

  5. 7
    Сергей

    Валидатор микроразметки в Google показывает ошибку, чего-то не хватает.

  6. 10
    Денис

    Добрый день,
    Помогите решить проблему, нужно чтобы в хлебных крошках вместо стандартного названия категорий выводилось название с произвольного поля через плагин ACF.

  7. 12
    Павел
    @

    Здравствуйте!
    Как-то можно исключить вложенные категории из цепочки?
    Сейчас выводится как site.ru/cat/podcat, а нужно site.ru/cat

  8. 14

    Функция обновлена. Добавлен тег <meta itemprop="position" content="" />, и теперь в инструменте проверки структурированных данных Гугла ошибок нет.

  9. 15
    @

    Помогите убрать родительскую категорию и оставить только подкатегории

  10. 16
    иван

    здравствуйте, подскажите как сделать чтобы отображался шоткод [year] в крошках, в title он нормально год отображает 2018 например, а в крошках так и остается [year]

  11. 17
    Pervect
    @

    Здравствуйте!
    Великолепно реализованная логика. Работает «из коробки» и результат получается вполне ожидаемый.

    Но встал вопрос: вывод крошек по вашему способу осуществляется по схеме: div-span-span

    Есть ли возможность выводить в логике Бутстрапа (UL-LI c cообразными стилями?)
    То есть:
    1) Обертка
    2) Элементы вида Чего-то там
    3) Активная крошка вида Rome

  12. 21
    Ильяс
    @

    А с произвольной таксономией эта версия не работает? Старая 2017 года работала, как быть?

  13. 25

    Поддерживаются ли крошки в woocommerce?

  14. 27
    Павел
    @

    Столкнулся с такой проблемой: если название рубрики начинается с русской буквы С, то данная рубрика в цепочке навигации не выводится. Если заменить её на любую другую или латинскую C, то всё работает. С чем это может быть связно, куда копать?

    • 28
      Павел
      @

      Хотя нет… Как-то рандомно рубрики выводятся. Часть слов в цепочке появляется, а часть нет. Например, если рубрика называется «архитектор», то выводится, если «строитель», то нет её. Логики не вижу, только сижу синонимы подбираю, какие-то выводятся, какие-то нет…

  15. 30
    Владимир
    @
    <a class="breadcrumbs__link" href="%1$s" itemprop="item"><span itemprop="name">%2$s</span></a>

    Как здесь в выводе %2$s убрать теги (а то у меня встречаются в названии страниц)

  1. 1
    DH

    Для таксономий добавьте:

    } elseif ( is_tax() ) {
     if ($show_home_link &amp;&amp; $show_current) echo $sep;
     $term = get_queried_object();
     if ( $term ) {
     $tax = get_taxonomy( $term->taxonomy );
     $title = single_term_title( $tax->labels->name. ': ', false );
     echo $title;
     }
    

    где-нибудь перед

    } elseif ( is_day() ) {
  2. 2

    Замените эти строки:

    if ( get_query_var('cpage') ) {
    	echo $sep . sprintf($link, get_permalink(), get_the_title()) . $sep . $before . sprintf($text['cpage'], get_query_var('cpage')) . $after;
    } else {
    	if ($show_current) echo $before . get_the_title() . $after;
    }
    

    на такие:

    $title = get_the_title();
    $title_custom = get_post_meta(get_the_ID(), 'title', true);
    if ($title_custom) $title = $title_custom;
    if ( get_query_var('cpage') ) {
    	echo $sep . sprintf($link, get_permalink(), $title) . $sep . $before . sprintf($text['cpage'], get_query_var('cpage')) . $after;
    } else {
    	if ($show_current) echo $before . $title . $after;
    }
    

    Заголовок указывается через произвольное поле с именем «title».

Ваш комментарий