Step 10 – Make your first library: a breadcrumb generator

(created at: December 10, last update: July 20, 2015)
What site doesn’t need a breadcrumb? Maybe those that are not user friendly…

So why not make a breadcrumb library to help us with the task of outputting a breadcrumb on our site?

We will name our library Make_bread (I tried to make the name as original as I could…). So let’s make some bread. If you are interested only in the final product, you can find it at https://github.com/avenirer/CI-breadcrumb-library

CodeIgniter libraries that don’t come with the framework are stored inside application/libraries directory. Libraries are classes that add functionalities to your framework. You could ask: Why make a library and not a helper. Make_bread will be a library because is not a simple collection of functions, but methods that work with each other.

Thinking before working

Before we get to work let’s think about what we want to accomplish. The end result of our library would have to allow us to create a breadcrumb with the “crumbs” that we provide. Also it will have to be well formed: have a container (<div> or <nav>), and have separators between the “crumbs”.

The prototype – doing the first draft of our library

To start with, let’s create a file named Make_bread.php inside application/libraries. In it we will have to write the basic format of a Codeigniter library class:

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

class Make_bread
{
  public function __construct()
  {
  }
}

Simple, wasn’t it? Now, what do we need? Considering that we will work with links, it would be nice to use the URL helper (http://www.codeigniter.com/userguide3/helpers/url_helper.html). So let’s load it in the constructor. To load it, we can’t just use $this->load->helper(‘url’); . We must first call an instance of CodeIgniter (http://www.codeigniter.com/userguide3/general/creating_libraries.html#utilizing-codeigniter-resources-within-your-library). So, the file will now look like below:

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

class Make_bread
{
  public function __construct()
  {
    $CI =& get_instance();
    $CI->load->helper('url');
  }
}

Now, let’s put in variables what we know we will need for our breadcrumb. In my opinion, we will need:

  • a name (title) for homepage, this being the first element (crumb) of the breadcrumb;
  • the container’s opening tag;
  • the container’s closing tag;
  • the crumb’s opening tag;
  • the crumb’s closing tag;
  • the divider of the crumbs;
  • the breadcrumb itself.

If we add these, the file would change as follows:

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

class Make_bread
{
  private $_include_home = 'First page';
  private $_breadcrumb = array();
  private $_divider = '&nbsp;&#8250;&nbsp;';
  private $_container_open = '<div id="breadcrumb">';
  private $_container_close = '</div>';
  private $_crumb_open = '';
  private $_crumb_close = '';

  public function __construct()
  {
    $CI =& get_instance();
    $CI->load->helper('url');
  }
}

Now, if we chose to have the first segment as the homepage, it would be a good idea to start building the breadcrumb array with that element inside the constructor:

public function __construct()
{
  $CI =& get_instance();
  $CI->load->helper('url');
  if(isset($this->_include_home) && (sizeof($this->_include_home) > 0))
  {
    // we will trim the last '/' just to make sure we are consistent with all servers
    $this->_breadcrumb[] = array('title'=>$this->_include_home, 'href'=>rtrim(base_url(),'/'));
  }
}

Cool. Now what do we need? We need to put our library to work. What methods do we need:

  1. A method that adds crumbs to the breadcrumb. It will receive the title, the address (optional), and TRUE or FALSE (also optional which defaults to FALSE) if the links are going to really be crumbs. I will explain more below. So the format would be: add($title, $href = NULL , 1)
  2. A method that outputs the breadcrumb. The format would simply be output();

1. The add() method

The add() method will allow us to add “crumbs” to the breadcrumb. The method must receive at least one parameter. The first parameter must be the title of the link. As a second parameter, which is optional, the method can receive the url. Let’s allow the user to put as URL a relative path to the site. Also, if we know the breadcrumb will have, for example, the format:

First crumb: yoursite.tld
Second crumb: yoursite.tld/testing
Third crumb: yoursite.tld/testing/the_test

… let’s allow the user to just add as address parameters the segments of the URL: ‘testing‘, ‘the_test‘.

public function add($title = NULL, $href = '', $segment = FALSE)
{
  // if the method won't receive the $title parameter, it won't do anything to the $_breadcrumb
  if (is_null($title)) return;
  // first let's find out if we have a $href
  if(isset($href) && strlen($href)>0) {
    // if $segment is not FALSE we will build the URL from the previous crumb
    if ($segment) {
      $previous = $this->_breadcrumb[sizeof($this->_breadcrumb) - 1]['href'];
      $href = $previous . '/' . $href;
    }
    // else if the $href is not an absolute path we compose the URL from our site's URL
    elseif (!filter_var($href, FILTER_VALIDATE_URL))
    {
      $href = site_url($href);
    }
  }
  // add crumb to the end of the breadcrumb
  $this->_breadcrumb[] = array('title' => $title, 'href' => $href);
}

2. The output() method

The output() method will simply output the breadcrumb as a string, taking into the consideration the container and the delimiter:

public function output()
{
  // we open the container's tag
  $output = $this->_container_open;
  if(sizeof($this->_breadcrumb) > 0)
  {
    foreach($this->_breadcrumb as $key=>$crumb)
    {
      $output .= $this->_crumb_open;
      if(strlen($crumb['href'])>0)
      {
        $output .= anchor($crumb['href'],$crumb['title']);
      }
      else
      {
        $output .= $crumb['title'];
      }
      $output .= $this->_crumb_close;
      // we end the crumb with the divider if is not the last crumb
      if($key < (sizeof($this->_breadcrumb)-1))
      {
        $output .= $this->_divider;
      }
    }
  }
  // we close the container's tag
  $output .= $this->_container_close;
  return $output;
}

Now let’s see the whole library:

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

class Make_bread
{
  private $_include_home = 'First page';
  private $_breadcrumb = array();
  private $_divider = '&nbsp;&#8250;&nbsp;';
  private $_container_open = '<div id="breadcrumb">';
  private $_container_close = '</div>';
  private $_crumb_open = '';
  private $_crumb_close = '';

  public function __construct()
  {
    $CI =& get_instance();
    $CI->load->helper('url');
    if(isset($this->_include_home) && (sizeof($this->_include_home) > 0))
    {
    $this->_breadcrumb[] = array('title'=>$this->_include_home, 'href'=>rtrim(base_url(),'/'));
    }
  }

  public function add($title = NULL, $href = '', $segment = FALSE)
  {
    // if the method won't receive the $title parameter, it won't do anything to the $_breadcrumb
    if (is_null($title)) return;
    // first let's find out if we have a $href
    if(isset($href) && strlen($href)>0)
    {
      // if $segment is not FALSE we will build the URL from the previous crumb
      if ($segment)
      {
        $previous = $this->_breadcrumb[sizeof($this->_breadcrumb) - 1]['href'];
        $href = $previous . '/' . $href;
      }
      // else if the $href is not an absolute path we compose the URL from our site's URL
      elseif (!filter_var($href, FILTER_VALIDATE_URL))
      {
        $href = site_url($href);
      }
    }
    // add crumb to the end of the breadcrumb
    $this->_breadcrumb[] = array('title' => $title, 'href' => $href);
  }
  public function output()
  {
    // we open the container's tag
    $output = $this->_container_open;
    if(sizeof($this->_breadcrumb) > 0)
    {
      foreach($this->_breadcrumb as $key=>$crumb)
      {
        // we put the crumb with open and closing tags
        $output .= $this->_crumb_open;
        if(strlen($crumb['href'])>0)
        {
          $output .= anchor($crumb['href'],$crumb['title']);
        }
        else
        {
          $output .= $crumb['title'];
        }
        $output .= $this->_crumb_close;
        // we end the crumb with the divider if is not the last crumb
        if($key < (sizeof($this->_breadcrumb)-1))
        {
          $output .= $this->_divider;
        }
      }
    }
    // we close the container's tag
    $output .= $this->_container_close;
    return $output;
  }
}

Some changes to the prototype – move the configuration of the library in a separate file

Looks nice, but… There is room for improvement. Why not keeping the configuration of the library inside a config file? This way we can make sure no changes are made to the library.

So let’s create a make_bread.php file inside application/config directory. You might say “Why not inside application/config/development if we are working inside a development environment?”. My answer would be that no matter what the environment, the breadcrumb will look the same in the end.

The configuration file would have to store all the variables that can be found just before the constructor of the library. So it will look like this:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

// $config['include_home'] will tell the library if the first element should be the homepage. You only put the title of the first crumb. If you leave it blank it will not put homepage as first crumb
$config['include_home'] = 'First page';
// $config['divider'] is the divider you want between the crumbs. Leave blank if you don't want a divider;
$config['divider'] = '&nbsp;&#8250;&nbsp;';
// $config['container_open'] is the opening tag of the breadcrumb container
$config['container_open'] = '<div id="breadcrumb">';
// $config['container_close'] is the closing tag of the breadcrumb container
$config['container_close'] = '</div>';
// $config['crumb_open'] is the opening tag of the crumb container
$config['crumb_open'] = '';
// $config['crumb_close'] is the closing tag of the crumb container
$config['crumb_close'] = '';

Now we will change the start of the Make_bread library so that it can fill the variables from the configuration file in the constructor:

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

class Make_bread
{
  private $_breadcrumb = array();
  private $_include_home;
  private $_container_open;
  private $_container_close;
  private $_divider;
  private $_crumb_open;
  private $_crumb_close;

  public function __construct()
  {
    $CI =& get_instance();
    $CI->load->helper('url');
    $CI->load->config('make_bread', TRUE);
    $this->_include_home = $CI->config->item('include_home', 'make_bread');
    $this->_container_open = $CI->config->item('container_open', 'make_bread');
    $this->_container_close = $CI->config->item('container_close', 'make_bread');
    $this->_divider = $CI->config->item('divider', 'make_bread');
    $this->_crumb_open = $CI->config->item('crumb_open', 'make_bread');
    $this->_crumb_close = $CI->config->item('crumb_close', 'make_bread');
    if(isset($this->_include_home) && (sizeof($this->_include_home) > 0))
    {
      $this->_breadcrumb[] = array('title'=>$this->_include_home, 'href'=>rtrim(base_url(),'/'));
    }
  }

Cool. Now, if we will need to make changes to the way the library works, we can make them inside the configuration file and leave the library alone.

The testing – Putting the library at work

Now we can test the library inside a controller. For starters, let’s do the test in the Welcome controller.

<?php defined('BASEPATH') OR exit('No direct script access allowed');

class Welcome extends CI_Controller {

  public function index()
  {
    // first load the library
    $this->load->library('make_bread');

    // add the first crumb, the segment being added to the previous crumb's URL
    $this->make_bread->add('first crumb', 'testing', TRUE);

    // add the second crumb, the segment not being added to the previous crumb's URL
    $this->make_bread->add('second crumb', 'the_test', FALSE);

    // add another crumb with a absolute URL
    $this->make_bread->add('test','http://google.com');

    // being the last crumb in the breadcrumb I want this to have no link, so I will only put the title
    $this->make_bread->add('Testing breadcrumbs');

    // now, let's store the output of the breadcrumb in a variable and show it (preferably inside a view)
    $breadcrumb = $this->make_bread->output();
    echo $breadcrumb;
  }
}

As you can see, I’ve put an echo inside the controller, killing the MVC pattern philosophy. But at least we can testify that the library worked as a charm. You can download the whole library from https://github.com/avenirer/CI-breadcrumb-library.

10 comments

  1. This is a very good tutorial and I really learn something. However I have a question about the breadcrumb library. How can I remove the anchor link to the last child of the breadcrumb? Is it possible also to create a breadcrumb that copies the current url and put links to every section?
    Thanks again for this wonderful tutorial.

    1. Thank you for your input. I actually did think about allowing the user not to add a link to the last crumb (for SEO and/or usability reasons), but when I wrote the tutorial I forgot about it. Now, thanks to you, I’ve changed the script so that, if you don’t put a link the crumb will show up without one.

  2. Thanks for your update. This library works fine for me. By the way, do you have a tutorial about image multi-upload with thumbnails using one input field?
    thanks!

    1. Just because you found a problem with my library I am going to make a tutorial about what you requested. But please give me a day or two. Thank you.

  3. Hi Avenir, I got an error trying to test the library…
    A PHP Error was encountered
    Severity: Parsing Error
    Message: syntax error, unexpected ‘$breadcrumb’ (T_VARIABLE)
    Filename: controllers/Welcome.php
    Line Number: 41
    Backtrace:

    Any hint? Thanks

  4. In many cases breadcrumbs are built using unordered list. How about adding an “active” class to the selected item list page? Thanks

    1. That’s what I thought. But then I thought that, according to SEO and user friendly design principles, the active page should never be a link inside the breadcrumb. So you simply don’t link it and style it accordingly (hence without needing to add another class).

Leave a Reply

Your email address will not be published. Required fields are marked *

No spam? * Time limit is exhausted. Please reload CAPTCHA.