Skip to Content

breadcrumbs()

This custom breadcrumb function allows for easy addition of a site breadcrumb to any page.

PHP March 26, 2019

Usage

PHP
nebula()->breadcrumbs($options)

Parameters

$options
(Optional) (Array) An array of options
Default: None

Parameter Notes

Options

delimiter
The string between nodes.
Default: /

home
Text for the home link
Default: get_bloginfo('title')

home_link
The URL for the home link
Default: home_url('/')

prefix
What type of prefix to use before certain post types (categories, tags, etc.). Ex: text, icons, off (or false)
Default: text

current
Show or hide the current title in the breadcrumb
Default: true

before
Tag before the current node
Default: <span class="current">

after
Tag after the current crumb
Default: </span>

force
Override the breadcrumbs with an array of specific links (See example below)
Default: false

Request or provide clarification »

Examples

Simple Implementation

PHP
<?php nebula()->breadcrumbs(); ?>

Replace the delimiter and home link with text

PHP
<?php nebula()->breadcrumbs(array('delimiter' => '&raquo;', 'home' => '<i class="fas fa-home"></i>')); ?>

Replace the delimiter and home text, and also manually control the nodes. Notice the current page is not passed in the 'force' option; it is controlled by the 'current' option which defaults to true (so it is not passed here).

PHP
<?php
    nebula()->breadcrumbs(array(
        'home' => '<i class="fas fa-home"></i>',
        'delimiter' => '&raquo;',
        'force' => array(
            array('Home', home_url()),
            array(get_the_title(127), get_permalink(127)),
            array(get_field('product_type'), get_permalink(get_page_by_title(get_field('product_type')))),
        ),
    ));
?>

Disable prefixes for categories and tags

PHP
<?php nebula()->breadcrumbs(array('prefix' => false)); ?>

Change settings globally by changing default parameters across the entire site

PHP
<?php
    //Add this to a functions file
    add_filter('nebula_breadcrumb_defaults', function($defaults){
        $defaults['delimiter'] = '&rsaquo;';
        $defaults['home'] = '<i class="fas fa-home"></i>';
        return $defaults;
    });

    //Now it can be called in template files without parameters:
    nebula()->breadcrumbs();
?>

Source File

Located in /libs/Functions.php on line 1311.

7 Hooks

Find these filters and actions in the source code below to hook into them. Use do_action() and add_filter() in your functions file or plugin.

Filters
"pre_nebula_breadcrumbs"
"nebula_breadcrumb_defaults"
"nebula_breadcrumbs_category"
"nebula_breadcrumbs_tag"
"nebula_breadcrumbs_author"
"nebula_breadcrumbs_error"
"nebula_breadcrumbs_paged"
Need a new filter hook? Request one here.

Actions
This function has no action hooks available. Request one?

Note: This function contains 4 to-do comments.

PHP
    public function breadcrumbs($options=array()){
        $override = apply_filters('pre_nebula_breadcrumbs', null);
        if ( isset($override) ){return;}

        global $post;
        $defaults = apply_filters('nebula_breadcrumb_defaults', array(
            'delimiter' => '/', //Delimiter between crumbs
            'home' => get_bloginfo('title'), //Text for the 'Home' link
            'home_link' => home_url('/'),
            'prefix' => 'off', //Prefix categories and tags with "text", "icon", or none with "off" (default)
            'current' => true, //Show/Hide the current title in the breadcrumb
            'before' => '<li class="current" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">', //Tag before the current crumb
            'after' => '</li>', //Tag after the current crumb
            'force' => false //Override the breadcrumbs with an array of specific links
        ));

        $data = array_merge($defaults, $options);
        $data['delimiter_html'] = '<li class="delimiter">' . $data['delimiter'] . '</li>';
        $data['current_node'] = $data['before'] . '<a class="current-breadcrumb-link" href="' . get_the_permalink() . '" itemprop="item"><span itemprop="name">' . strip_tags(get_the_title()) . '</span></a>';
        $position = 1; //Incrementer for each node (for schema tags)

        if ( !empty($data['force']) ){ //If using forced override
            echo '<ol class="nebula-breadcrumbs" itemscope itemtype="http://schema.org/BreadcrumbList">';

            foreach ( $data['force'] as $node ){
                $node_text = ( !empty($node['text']) )? $node['text'] : $node[0];
                $node_url = false;
                if ( !empty($node['url']) ){
                    $node_url = $node['url'];
                } else {
                    if ( !empty($node[1]) ){
                        $node_url = $node[1];
                    }
                }

                if ( !empty($node_text) ){
                    if ( !empty($node_url) ){
                        echo '<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . $node_url . '" itemprop="item">';
                    }

                    echo '<span itemprop="name">' . $node_text . '</span>';

                    if ( !empty($node_url) ){
                        echo '</a><meta itemprop="position" content="' . $position . '" /></li>';
                    }

                    echo ' ' . $data['delimiter_html'] . ' ';
                }

                $position++;
            }

            if ( !empty($data['current']) ){
                echo $data['current_node'] . '<meta itemprop="position" content="' . $position . '" />' . $data['after'];
            }

            echo '</ol>';
        } elseif ( is_home() || is_front_page() ){
            echo '<ol class="nebula-breadcrumbs" itemscope itemtype="http://schema.org/BreadcrumbList"><li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . $data['home_link'] . '" itemprop="item"><span itemprop="name">' . $data['home'] . ' <span class="sr-only">' . get_bloginfo('title') . '</span></span></a><meta itemprop="position" content="' . $position . '" /></li></ol>';
            $position++;
            return false;
        } else {
            echo '<ol class="nebula-breadcrumbs" itemscope itemtype="http://schema.org/BreadcrumbList"><li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . $data['home_link'] . '" itemprop="item"><span itemprop="name">' . $data['home'] . ' <span class="sr-only">' . get_bloginfo('title') . '</span></span></a><meta itemprop="position" content="' . $position . '" /></li> ' . $data['delimiter_html'] . ' ';
            $position++;

            if ( is_category() ){
                $thisCat = get_category(get_query_var('cat'), false);
                if ( $thisCat->parent !== 0 ){
                    $parents = get_ancestors($thisCat->parent, 'category', 'taxonomy');
                    array_unshift($parents, $thisCat->parent);
                    foreach ( array_reverse($parents) as $term_id ){
                        $parent = get_term($term_id, 'category');
                        echo '<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . esc_url(get_term_link($parent->term_id, 'category')) . '" itemprop="item"><span itemprop="name">' . $parent->name . '</span></a><meta itemprop="position" content="' . $position . '" /></li> ' . $data['delimiter_html'] . ' ';
                        $position++;
                    }
                }

                $prefix = '';
                if ( $data['prefix'] === 'icon' ){
                    $prefix = '<i class="fas fa-bookmark"></i>';
                } elseif ( $data['prefix'] === 'text' ){
                    $prefix = 'Category: ';
                }

                echo apply_filters('nebula_breadcrumbs_category', $data['before'] . '<a class="current-breadcrumb-link" href="' . get_category_link($thisCat->term_id) . '" itemprop="item"><span itemprop="name">' . $prefix . single_cat_title('', false) . '</span></a><meta itemprop="position" content="' . $position . '" />' . $data['after'], $data);
                $position++;
            } elseif ( is_search() ){
                echo $data['before'] . 'Search results' . $data['after'];
            } elseif ( is_day() ){
                echo '<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . get_year_link(get_the_time('Y')) . '" itemprop="item"><span itemprop="name">' . get_the_time('Y') . '</span></a><meta itemprop="position" content="' . $position . '" /></li> ' . $data['delimiter_html'] . ' ';
                $position++;

                echo '<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . get_month_link(get_the_time('Y'), get_the_time('m')) . '" itemprop="item"><span itemprop="name">' . get_the_time('F') . '</span></a><meta itemprop="position" content="' . $position . '" /></li> ' . $data['delimiter_html'] . ' ';
                $position++;

                echo $data['before'] . get_the_time('d') . $data['after'];
            } elseif ( is_month() ){
                echo '<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . get_year_link(get_the_time('Y')) . '" itemprop="item"><span itemprop="name">' . get_the_time('Y') . '</span></a><meta itemprop="position" content="' . $position . '" /></li> ' . $data['delimiter_html'] . ' ';
                $position++;

                echo $data['before'] . get_the_time('F') . $data['after'];
            } elseif ( is_year() ){
                echo $data['before'] . get_the_time('Y') . $data['after'];
            } elseif ( is_single() && !is_attachment() ){
                if ( get_post_type() !== 'post' ){ //Custom Post Type
                    $post_type = get_post_type_object(get_post_type());

                    $slug = $post_type->rewrite;
                    if ( is_string($post_type->has_archive) ){ //If the post type has a custom archive slug
                        $slug['slug'] = $post_type->has_archive; //Replace slug with the custom archive slug string
                    }

                    echo '<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . $data['home_link'] . $slug['slug'] . '/" itemprop="item"><span itemprop="name">' . $post_type->labels->name . '</span></a><meta itemprop="position" content="' . $position . '" /></li>'; //Changed from singular_name so plurals would appear in breadcrumb nodes
                    $position++;

                    //Check for parent "pages" on the custom post type and output them if they exist
                    $parent_id = $post->post_parent;
                    if ( !empty($parent_id) ){
                        echo $data['delimiter_html'];
                        $breadcrumbs = array();

                        while ( $parent_id ){
                            $page = get_page($parent_id);
                            $breadcrumbs[] = '<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . get_permalink($page->ID) . '" itemprop="item"><span itemprop="name">' . strip_tags(get_the_title($page->ID)) . '</span></a><meta itemprop="position" content="' . $position . '" /></li>';
                            $position++;
                            $parent_id = $page->post_parent;
                        }

                        $breadcrumbs = array_reverse($breadcrumbs);
                        $breadcrumbs_nodes = count($breadcrumbs);
                        for ( $i = 0; $i < $breadcrumbs_nodes; $i++ ){
                            echo $breadcrumbs[$i];
                            if ( $i !== $breadcrumbs_nodes-1 ){
                                echo ' ' . $data['delimiter_html'] . ' ';
                            }
                        }
                    }

                    if ( !empty($data['current']) ){
                        echo ' ' . $data['delimiter_html'] . ' ' . $data['current_node'] . '<meta itemprop="position" content="' . $position . '" />' . $data['after'];
                    }
                } else { //Post Category
                    $cat = get_the_category();
                    if ( !empty($cat) ){
                        $cat = $cat[0];

                        echo '<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . esc_url(get_term_link($cat->term_id, 'category')) . '" itemprop="item"><span itemprop="name">' . $cat->name . '</span></a><meta itemprop="position" content="' . $position . '" /></li> ' . $data['delimiter_html'] . ' ';
                        $position++;

                        if ( !empty($data['current']) ){
                            echo $data['current_node'] . '<meta itemprop="position" content="' . $position . '" />' . $data['after'];
                        }
                    }
                }
            } elseif ( !is_single() && !is_page() && get_post_type() !== 'post' && !is_404() ){
                $post_type = get_post_type_object(get_post_type());
                echo $data['before'] . $post_type->labels->singular_name . $data['after'];
            } elseif ( is_attachment() ){ //@TODO "Nebula" 0: Check for gallery pages? If so, it should be Home > Parent(s) > Gallery > Attachment
                if ( !empty($post->post_parent) ){ //@TODO "Nebula" 0: What happens if the page parent is a child of another page?
                    echo '<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . get_permalink($post->post_parent) . '" itemprop="item"><span itemprop="name">' . strip_tags(get_the_title($post->post_parent)) . '</span></a><meta itemprop="position" content="' . $position . '" /></li> ' . $data['delimiter_html'] . ' ' . strip_tags(get_the_title());
                    $position++;
                } else {
                    echo strip_tags(get_the_title());
                }
            } elseif ( is_page() && !$post->post_parent ){ //Page without ancestors/parents
                if ( !empty($data['current']) ){
                    echo $data['current_node'] . '<meta itemprop="position" content="' . $position . '" />' . $data['after'];
                }
            } elseif ( is_page() && $post->post_parent ){ //Page with ancestors/parents
                $parent_id = $post->post_parent;
                $breadcrumbs = array();

                while ( $parent_id ){
                    $page = get_page($parent_id);
                    $breadcrumbs[] = '<li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a href="' . get_permalink($page->ID) . '" itemprop="item"><span itemprop="name">' . strip_tags(get_the_title($page->ID)) . '</span></a><meta itemprop="position" content="' . $position . '" /></li>';
                    $position++;
                    $parent_id = $page->post_parent;
                }

                $breadcrumbs = array_reverse($breadcrumbs);
                $breadcrumbs_nodes = count($breadcrumbs);
                for ( $i = 0; $i < $breadcrumbs_nodes; $i++ ){
                    echo $breadcrumbs[$i];
                    if ( $i !== $breadcrumbs_nodes-1 ){
                        echo ' ' . $data['delimiter_html'] . ' ';
                    }
                }

                if ( !empty($data['current']) ){
                    echo ' ' . $data['delimiter_html'] . ' ' . $data['current_node'] . '<meta itemprop="position" content="' . $position . '" />' . $data['after'];
                }
            } elseif ( is_tag() ){
                $prefix = '';
                if ( $data['prefix'] === 'icon' ){
                    $prefix = '<i class="fas fa-tag"></i>';
                } elseif ( $data['prefix'] === 'text' ){
                    $prefix = 'Tag: ';
                }

                echo apply_filters('nebula_breadcrumbs_tag', $data['before'] . $prefix . single_tag_title('', false) . $data['after'], $data);
                //echo $data['before'] . '<a class="current-breadcrumb-link" href="' . get_tag_link($thisTag->term_id) . '">'. $prefix . single_tag_title('', false) . '</a>' . $data['after']; //@todo "Nebula": Need to get $thisTag like $thisCat above
            } elseif ( is_author() ){
                //@TODO "Nebula" 0: Support for multi author? is_multi_author()

                global $author;
                $userdata = get_userdata($author);
                echo apply_filters('nebula_breadcrumbs_author', $data['before'] . $userdata->display_name . $data['after'], $data);
            } elseif ( is_404() ){
                echo apply_filters('nebula_breadcrumbs_error', $data['before'] . 'Error 404' . $data['after'], $data);
            }

            if ( get_query_var('paged') ){
                echo apply_filters('nebula_breadcrumbs_paged', '&nbsp;(Page ' . get_query_var('paged') . ')', $data); //nbsp is needed here because something is stripping out the first space
            }
            echo '</ol>';
        }
    }

Override

To override this PHP function, use this hook in your child theme or plugin ("my_custom" can be changed):

PHP
add_filter('pre_nebula_breadcrumbs', 'my_custom_breadcrumbs', 10, 2); //The last integer must be 1 more than the actual parameters
function my_custom_breadcrumbs($null, $options){ //$null is required, but can be ignored
    //Write your own code here

    return true; //Return true to prevent the original function from running afterwords
}

You can completely disable this PHP function with a single line actions:

PHP
 add_filter('pre_nebula_breadcrumbs', '__return_false');