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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/*
 * "Хлебные крошки" для WordPress
 * автор: Dimox
 * версия: 2015.09.14
 * лицензия: 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">'; // открывающий тег обертки
	$wrap_after = '</div><!-- .breadcrumbs -->'; // закрывающий тег обертки
	$sep = '›'; // разделитель между "крошками"
	$sep_before = '<span class="sep">'; // тег перед разделителем
	$sep_after = '</span>'; // тег после разделителя
	$show_home_link = 1; // 1 - показывать ссылку "Главная", 0 - не показывать
	$show_on_home = 0; // 1 - показывать "хлебные крошки" на главной странице, 0 - не показывать
	$show_current = 1; // 1 - показывать название текущей страницы, 0 - не показывать
	$before = '<span class="current">'; // тег перед текущей "крошкой"
	$after = '</span>'; // тег после текущей "крошки"
	/* === КОНЕЦ ОПЦИЙ === */

	global $post;
	$home_link = home_url('/');
	$link_before = '<span itemscope itemtype="http://data-vocabulary.org/Breadcrumb">';
	$link_after = '</span>';
	$link_attr = ' itemprop="url"';
	$link_in_before = '<span itemprop="title">';
	$link_in_after = '</span>';
	$link = $link_before . '<a href="%1$s"' . $link_attr . '>' . $link_in_before . '%2$s' . $link_in_after . '</a>' . $link_after;
	$frontpage_id = get_option('page_on_front');
	$parent_id = $post->post_parent;
	$sep = ' ' . $sep_before . $sep . $sep_after . ' ';

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

		if ($show_on_home) echo $wrap_before . '<a href="' . $home_link . '">' . $text['home'] . '</a>' . $wrap_after;

	} else {

		echo $wrap_before;
		if ($show_home_link) echo sprintf($link, $home_link, $text['home']);

		if ( is_category() ) {
			$cat = get_category(get_query_var('cat'), false);
			if ($cat->parent != 0) {
				$cats = get_category_parents($cat->parent, TRUE, $sep);
				$cats = preg_replace("#^(.+)$sep$#", "$1", $cats);
				$cats = preg_replace('#<a([^>]+)>([^<]+)<\/a>#', $link_before . '<a$1' . $link_attr .'>' . $link_in_before . '$2' . $link_in_after .'</a>' . $link_after, $cats);
				if ($show_home_link) echo $sep;
				echo $cats;
			}
			if ( get_query_var('paged') ) {
				$cat = $cat->cat_ID;
				echo $sep . sprintf($link, get_category_link($cat), get_cat_name($cat)) . $sep . $before . sprintf($text['page'], get_query_var('paged')) . $after;
			} else {
				if ($show_current) echo $sep . $before . sprintf($text['category'], single_cat_title('', false)) . $after;
			}

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

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

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

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

		} elseif ( is_single() && !is_attachment() ) {
			if ($show_home_link) echo $sep;
			if ( get_post_type() != 'post' ) {
				$post_type = get_post_type_object(get_post_type());
				$slug = $post_type->rewrite;
				printf($link, $home_link . '/' . $slug['slug'] . '/', $post_type->labels->singular_name);
				if ($show_current) echo $sep . $before . get_the_title() . $after;
			} else {
				$cat = get_the_category(); $cat = $cat[0];
				$cats = get_category_parents($cat, TRUE, $sep);
				if (!$show_current || get_query_var('cpage')) $cats = preg_replace("#^(.+)$sep$#", "$1", $cats);
				$cats = preg_replace('#<a([^>]+)>([^<]+)<\/a>#', $link_before . '<a$1' . $link_attr .'>' . $link_in_before . '$2' . $link_in_after .'</a>' . $link_after, $cats);
				echo $cats;
				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;
				}
			}

		// custom post type
		} elseif ( !is_single() && !is_page() && get_post_type() != 'post' && !is_404() ) {
			$post_type = get_post_type_object(get_post_type());
			if ( get_query_var('paged') ) {
				echo $sep . sprintf($link, get_post_type_archive_link($post_type->name), $post_type->label) . $sep . $before . sprintf($text['page'], get_query_var('paged')) . $after;
			} else {
				if ($show_current) echo $sep . $before . $post_type->label . $after;
			}

		} elseif ( is_attachment() ) {
			if ($show_home_link) echo $sep;
			$parent = get_post($parent_id);
			$cat = get_the_category($parent->ID); $cat = $cat[0];
			if ($cat) {
				$cats = get_category_parents($cat, TRUE, $sep);
				$cats = preg_replace('#<a([^>]+)>([^<]+)<\/a>#', $link_before . '<a$1' . $link_attr .'>' . $link_in_before . '$2' . $link_in_after .'</a>' . $link_after, $cats);
				echo $cats;
			}
			printf($link, get_permalink($parent), $parent->post_title);
			if ($show_current) echo $sep . $before . get_the_title() . $after;

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

		} elseif ( is_page() && $parent_id ) {
			if ($show_home_link) echo $sep;
			if ($parent_id != $frontpage_id) {
				$breadcrumbs = array();
				while ($parent_id) {
					$page = get_page($parent_id);
					if ($parent_id != $frontpage_id) {
						$breadcrumbs[] = sprintf($link, get_permalink($page->ID), get_the_title($page->ID));
					}
					$parent_id = $page->post_parent;
				}
				$breadcrumbs = array_reverse($breadcrumbs);
				for ($i = 0; $i < count($breadcrumbs); $i++) {
					echo $breadcrumbs[$i];
					if ($i != count($breadcrumbs)-1) echo $sep;
				}
			}
			if ($show_current) echo $sep . $before . get_the_title() . $after;

		} elseif ( is_tag() ) {
			if ( get_query_var('paged') ) {
				$tag_id = get_queried_object_id();
				$tag = get_tag($tag_id);
				echo $sep . sprintf($link, get_tag_link($tag_id), $tag->name) . $sep . $before . sprintf($text['page'], get_query_var('paged')) . $after;
			} else {
				if ($show_current) echo $sep . $before . sprintf($text['tag'], single_tag_title('', false)) . $after;
			}

		} elseif ( is_author() ) {
			global $author;
			$author = get_userdata($author);
			if ( get_query_var('paged') ) {
				if ($show_home_link) echo $sep;
				echo sprintf($link, get_author_posts_url($author->ID), $author->display_name) . $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 ( is_404() ) {
			if ($show_home_link && $show_current) echo $sep;
			if ($show_current) echo $before . $text['404'] . $after;

		} elseif ( has_post_format() && !is_singular() ) {
			if ($show_home_link) 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, для разделителя — .sep а для текущей «крошки» — .current.

P.S. Функция работает на WordPress, начиная с версии 3.0 и выше.

Комментарии (451)
  1. 422
    Михаил

    Спасибо, все работает.
    Планируется ли интеграция с polylang? Крошки начинают сыпаться от главной страницы на русском при любом выбранном языке.

  2. 423
    Михаил

    Разобрался в polylang и переводом главной страницы.
    В самом годе можно бесполезно поменять вызов $text[‘home’] на pll_e(‘some_txt’) (но не в опциях).
    Главное объявить pll_register_string раньше в functions.

  3. 424
    Максим

    Добрый день! Спасибо за код)
    Вам не встречались проблемы с названиями категорий на русском?
    Мой случай:
    Добавляю новую запись (название TEST). Проставляю категории (3 чекбокса, категория «Земля» дочерняя 3 ур).
    Структура: Каталог->Горшечные цветы->Земля
    На странице записи отображается только Главная->Каталог->Горшечные цветы->TEST.

    Меняю название категории «Горшечные цветы» на «ЙГоршечные цветы». Т.е. просто добавил букву в начало названия категории и стало отображаться
    Главная->Каталог->Горшечные цветы->Земля->TEST.

    Проверил все буквы =) если добавить букву от А до З то не работает, если добавить от И до Я то работает. Как, вообще, вывод иерархии может зависеть от названия категории?

    При этом у меня очень много категорий и все работают, например,
    Каталог->Цветы->Ирис->TEST (для записи также проставлены 3 чекбокса до 3 ур. катериии «Ирис»).
    Кроме этих чертовых «Горшечных цветов» =)

    Абсолютно такая же проблема была с плагином «breadcrumb-navxt».
    Использовал англ. ВП. Сейчас специально поставил рус. ВП воссоздал структуру, загрузил ваш код — и опять проблема только с категорией «Горшечные цветы». Помогите, пожалуйста =)

  4. 426

    SOS
    прошу помощи
    схема такая главная-вторая страница-категория-подкатегория
    вторая страница выпадает везде и с плагинами и без. Думал вы нашли грааль. Поставил ваш код тоже самое. Поможите если можите

  5. 428
    Den

    Как можно решить эту проблему? У меня каталог и много категорий. Может совет дадите?

  6. 431

    Спасибо) Очень не хватало)

  7. 432
    Андрей

    Здравствуйте.
    Спасибо за код.
    Нет ли возможности его использовать для пользовательских таксономий?

  8. 435

    Здравствуйте!
    Подскажите код вывода крошек для своей таксономии (плагин Simple Taxonomy)

    Например для category:
    http://mysite.ru/category/avto
    http://mysite.ru/category/art

    вывод такой:
    Главная › Архив рубрики «Авто»
    Главная › Архив рубрики «Art»

    А для таксономии «razreshenie«:
    http://mysite.ru/razreshenie/1920×1080
    http://mysite.ru/razreshenie/2560×1600

    Нужно вывести
    Главная › Разрешение «1920×1080»
    Главная › Разрешение «2560×1600»

    Я думаю это просто и похоже на код для Категории и Тэги.
    Спасибо!

  9. 436
    Пух

    Спасибо, все идеально работает. Низкий поклон

  10. 437
    алекс

    как это работает ? скопировал в функшинс создал в хтмл код, ничего не отображается на страничке

  11. 438
    Art

    Здравствуйте,

    с помощью приведенного кода, можно присвоить крошкам вид,

    Главная / 2 cat

    Речь о Woocommerce.

    На данный момент, Главная / товары / 2 cat / 3 cat и так далее.

    ?

    Спасибо за помощь!

  12. 440
    Рита

    Добрый день!
    А как сделать так, чтобы рубрика не отображалась в хлебных крошках, а отображалось название страницы?
    У меня есть страница блога, на которой выводятся все записи, хочется иметь такой вид «хлебных крошек» на странице записи (single):
    Главная / Блог / Название статьи
    Также есть страница «Новости и акции», и на ней тогда будет иметь вид:
    Главная / Новости и акции / Название статьи

  13. 441
    Никита

    Здравствуйте!
    Спасибо за труды, все работает.
    Вопрос: можно ли отображать не заголовок поста а Атрибут заголовка или заголовок, используемый в меню.

    Спасибо!

  14. 445

    Большое спасибо, отлично работает, +++ тебе в карму, ну и like само собой

  15. 446
    Евгений

    Спасибо. Отлично работает.

  16. 448

    Спасибо!) Помогли. Только почему-то они у меня внизу страницы отображаются(

  17. 449

    Если у меня кастомная таксономия (вместо category есть area, price и другие), я могу названия вставить в опциях?
    по типу

    $text['category'] = '"%s"'; 
    $text['area'] = '"%s"';
    
  18. 451
    Alex

    А что за кусок в коде?
    «$link_before = »;
    $link_after = »;»
    Зачем он там нужен?