Skip to content

Instantly share code, notes, and snippets.

@Dimox
Last active March 27, 2023 09:27
Show Gist options
  • Star 85 You must be signed in to star a gist
  • Fork 73 You must be signed in to fork a gist
  • Save Dimox/5654092 to your computer and use it in GitHub Desktop.
Save Dimox/5654092 to your computer and use it in GitHub Desktop.
WordPress Breadcrumbs Without a Plugin - http://dimox.net/wordpress-breadcrumbs-without-a-plugin/
<?php
/*
* WordPress Breadcrumbs
* author: Dimox
* version: 2019.03.03
* license: MIT
*/
function dimox_breadcrumbs() {
/* === OPTIONS === */
$text['home'] = 'Home'; // text for the 'Home' link
$text['category'] = 'Archive by Category "%s"'; // text for a category page
$text['search'] = 'Search Results for "%s" Query'; // text for a search results page
$text['tag'] = 'Posts Tagged "%s"'; // text for a tag page
$text['author'] = 'Articles Posted by %s'; // text for an author page
$text['404'] = 'Error 404'; // text for the 404 page
$text['page'] = 'Page %s'; // text 'Page N'
$text['cpage'] = 'Comment Page %s'; // text 'Comment Page N'
$wrap_before = '<div class="breadcrumbs" itemscope itemtype="http://schema.org/BreadcrumbList">'; // the opening wrapper tag
$wrap_after = '</div><!-- .breadcrumbs -->'; // the closing wrapper tag
$sep = '<span class="breadcrumbs__separator"> › </span>'; // separator between crumbs
$before = '<span class="breadcrumbs__current">'; // tag before the current crumb
$after = '</span>'; // tag after the current crumb
$show_on_home = 0; // 1 - show breadcrumbs on the homepage, 0 - don't show
$show_home_link = 1; // 1 - show the 'Home' link, 0 - don't show
$show_current = 1; // 1 - show current page title, 0 - don't show
$show_last_sep = 1; // 1 - show last separator, when current page title is not displayed, 0 - don't show
/* === END OF OPTIONS === */
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 ( get_query_var( 'paged' ) ) {
$position += 1;
if ( $show_home_link ) echo $sep;
echo sprintf( $link, $home_url . '?s=' . get_search_query(), sprintf( $text['search'], get_search_query() ), $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['search'], get_search_query() ) . $after;
} elseif ( $show_last_sep ) echo $sep;
}
} 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()
@con322
Copy link

con322 commented Sep 3, 2014

Hey nice work!

Can I use this in a commercial product?

Thanks

Copy link

ghost commented Oct 17, 2014

Hi, I have this error :

Trying to get property of non-object

for this line :

$parent_id = $parent_id_2 = $post->post_parent;

in search results page when the query not found

@cassandre
Copy link

You may fix the empty search results error (Notice: Tryin to get property of non-object...) by replacing line 40 ($parent_id = $post->post_parent) by the following code:

if (have_posts()) {
    $parent_id = $post->post_parent;
}

@krishna19
Copy link

Please add support for Woo Commerce & BBPress.

Thanks

@ohsik
Copy link

ohsik commented Dec 4, 2015

When displaying a post has no category set up will show the error.

Notice: undefined offset:0 on $cat = get_the_category(); $cat = $cat[0];

Catchable fatal error: Object of class WP_Error could not be converted to string in $cats = preg_replace('#<a([^>]+)>([^<]+)<\/a>#', $link_before . '<a$1' . $link_attr .'>' . $link_in_before . '$2' . $link_in_after .'</a>' . $link_after, $cats);

@krishna19
Copy link

It happened to me also. Please help to fix the error when the post has no category.

@jnadd
Copy link

jnadd commented Dec 12, 2015

I had a problem trying to condition this:
-- $thisCat = get_category(get_query_var ('cat'), false); --
-- $thisCat->parent != 0 --

The solution for me:
global $wp_query;
$thisCat = $wp_query->queried_object;

@kbytin
Copy link

kbytin commented Dec 21, 2015

After getting err (Notice: Tryin to get property of non-object...)

changed
$parent_id = $post->post_parent;

to this

$thisPostID = get_the_ID();
$parent_id = wp_get_post_parent_id( $thisPostID );

@sanjeevthakur58
Copy link

Can any one tell me I don't want breadcrumb links, it will only text, how to remove the anchor tag from this code

@Anydog
Copy link

Anydog commented Jun 13, 2016

Yeah, on line 101 and 125 there's an error if post is not assigned to any category (there are some cases):
This
$cat = get_the_category(); $cat = $cat[0];
should be replaced with this (after line 101, for example):

$cat = get_the_category(); 
$cat = isset( $cat ) ? $cat[0] : '';
if( $cat ) {
    $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;
    }
}else{
    if ($showCurrent == 1) {
        $breadcrumb_html .= $before . esc_html( strip_tags( get_the_title() ) ) . $after; 
    }
}

Apply the same logic on line 125 (line number will change after line 101 fix)

@rhcarlosweb
Copy link

Maybe can improve this code for custom taxonomy?

I'm using this function to added custom taxonomy on breadcrumb in conditional is_tax(), but i'm don't understand much about php, so is only a suggest :)

function get_taxonomy_parents( $id, $taxonomy = 'category', $link = false, $separator = '/', $nicename = false, $visited = array() ) {

        $chain = '';
        $parent = get_term( $id, $taxonomy );

        if ( is_wp_error( $parent ) )
                return $parent;

        if ( $nicename )
                $name = $parent->slug;
        else
                $name = $parent->name;

        if ( $parent->parent && ( $parent->parent != $parent->term_id ) && !in_array( $parent->parent, $visited ) ) {
                $visited[] = $parent->parent;
                $chain .= get_taxonomy_parents( $parent->parent, $taxonomy, $link, $separator, $nicename, $visited );
        }

        if ( $link )
                $chain .= '<a href="' . esc_url( get_term_link( $parent,$taxonomy ) ) . '" title="' . esc_attr( sprintf( __( "View all posts in %s" ), $parent->name ) ) . '">'.$name.'</a>' . $separator;
        else
                $chain .= $name.$separator;

        return $chain;
}

@Ninetheme
Copy link

thank you!

@haipham
Copy link

haipham commented Oct 14, 2019

Please fix missing field "id" in Google Search Console. Thank you.

@webarter
Copy link

Hello!

Google today started issuing a warning data-vocabulary.org schema deprecated

From April 2020 the current markup won't be supported anymore and Google won't display rich results unless we migrate to a different markup in this code.

@Dimox can you please look into this and let us know if you feel like updating it? Here's detailed info on the issue and example of the new markup: https://www.spyloadedng.com/webmasters-board/breadcrumbs-issue-how-to-fix-data-vocabulary-schema-deprecated/

The alternative seems to be using Yoast SEO's breadcrumbs from their new version.

@MKH67
Copy link

MKH67 commented Jan 22, 2020

About Google data vocabulary problem, using last version of the code solved it for me :)

@Dimox
Copy link
Author

Dimox commented Jan 22, 2020

@webarter, I'm not using data-vocabulary.

@alex-wdmg
Copy link

A custom WordPress nav walker class to implement the Bootstrap 4 breadcrumbs style in a custom theme using the WordPress. Google schema validation is Ok.
wp_breadcrumbs.php

@tweenpix
Copy link

tweenpix commented Jul 12, 2021

чтобы работало в категориях woocommerce

        elseif (is_product_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;
            }
        } 

@DimonFreeman
Copy link

чтобы работало в категориях woocommerce

        elseif (is_product_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;
            }
        } 

не совсем корректно работает,
Главная->Категория, или Главная->Подкатегория.
Каталог с категориями пропускает + категорию пропускает

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment