Page 1 of 1

Designing A Breadcrumb Navigation w/ DOMXPath Rate Topic: -----

#1 codeprada  Icon User is offline

  • Changed Man With Different Priorities
  • member icon

Reputation: 946
  • View blog
  • Posts: 2,355
  • Joined: 15-February 11

Posted 12 January 2012 - 08:44 PM

Designing A Breadcrumb Navigation


As defined by Wikipedia:
"Breadcrumbs or breadcrumb trail is a navigation aid used in user interfaces. It allows users to keep track of their locations within programs or documents. The term comes from the trail of breadcrumbs left by Hansel and Gretel in the popular fairytale." - Wikipedia

Here's a lovely example of what a breadcrumb looks like if you're still not sure.
Breadcrumb Navigation

In this tutorial I'm going to show you how to design a breadcrumb navigation using DOMXPath, DOMDocument and a XML file. Basically we're going to take advantage of the way paths work in a URL and XML file. Let's say we visit a website and we're at the current location
http://www.fakesite.com/tutorials/codeprada/breadcrumb/
It's safe to say that we can visit the following URL without a problem (provided the site was designed properly)

http://www.fakesite.com/
http://www.fakesite.com/tutorials/
http://www.fakesite.com/tutorials/codeprada/


Let's now look at a simple XML file
<root>
	<tutorials>
		<codeprada>
			<breadcrumbs>
			</breadcrumbs>
		</codeprada>
	</tutorials>
</root>


From this we can determine that in order to get to breadcrumbs we must go through the tutorials then codeprada node. By now you should begin to see the resemblance between the two. To make it even clearer the XPath expression would be /tutorials/codeprada/breadcrumbs which is the same as the $_SERVER['REQUEST_URI'] when visiting
http://www.fakesite.com/tutorials/codeprada/breadcrumb/
.

The next order of business is to place the name you wish to be shown as the tag's attribute. Basically the updated version of our XML file will look like this.
<root>
	<tutorials name="Tutorials">
		<codeprada name="Codeprada">
			<breadcrumbs name="Breadcrumbs">
			</breadcrumbs>
		</codeprada>
	</tutorials>
</root>


To get the attribute of a node with XPath you must use the @ sign. e.g.
Getting the name attribute of the breadcrumbs node
/tutorials/codeprada/breadcrumbs/@name
The name attribute will be a more user friendly representation of the path. You don't want your site displaying something like tuts -> php5.4 -> 23432343 within your breadcrumb.

With all that said I've created a class to handle what was previously explained.
<?php
    /**
    * Navigation_BreadCrumbs
    * 
    * Generate Breadcrumbs according to the $_SERVER['REQUEST_URI']
    * 
    * @author Chavez Watkins
    * @created 12 Jan 2012
    */
    class Navigation_BreadCrumbs
    {
        /**
        * Properties
        * 
        * @var string $xml Store the XML string used to determine the user-friendly name
        * @var string $breadcrumb Store the breadcrumb string
        */
        private
            $xml = NULL,
            $breadcrumb = ''
        ;
        
        /**
        * Constructor
        * 
        * @param string $xml
        * @return Navigation_BreadCrumbs
        */
        public function __construct($xml)
        {
            $this->setXML($xml);
        }
        
        /**
        * Set the $xml property
        * 
        * @param string $xml
        * @return Navigation_BreadCrumbs
        */
        public function setXML($xml)
        {
            if(is_string($xml))
            {
                $this->xml = $xml;
                $this->render();
            }
            else
                throw new UnexpectedValueException('$xml parameter is suppose to be of type string');
            
            return $this;
        }
        
        /**
        * Create the breadcrumb string
        * 
        * @return Navigation_BreadCrumbs
        */
        private function render()
        {
            if(!isset($this->xml))
                throw new UnexpectedValueException('$xml property is suppose to be of type string');
            
            $query = '';
            $url = array_slice(explode('/', $_SERVER['REQUEST_URI']), 1, -1);
            
            $dom = new DOMDocument();
            $dom->loadXML($this->xml);
            
            $xpath = new DOMXPath($dom);
            foreach($url as $dir)
            {
                $query .= '/' . $dir;
                $node = $xpath->query('/' . $query . '/@name');
                if($node->length > 0)
                    $this->breadcrumb .= ' ==> ' . ($dir == $url[count($url) - 1] ?  $node->item(0)->nodeValue : '<a href="' . $query . '">' . $node->item(0)->nodeValue . '</a>');
                else
                    break;
            }
        }
        
        /**
        * Magic Method __toString
        * 
        * @return mixed
        */
        public function __toString()
        {
            return $this->breadcrumb;
        }
    }
?>



Using this class only requires two lines of code.
$navi = new Navigation_BreadCrumbs(file_get_contents('path/to/xml'));
echo $navi;



We've come to the end of this tutorial, feel free to ask any questions if something is still not clear to you.

This post has been edited by codeprada: 07 February 2012 - 07:36 AM


Is This A Good Question/Topic? 1
  • +

Replies To: Designing A Breadcrumb Navigation w/ DOMXPath

#2 Dormilich  Icon User is offline

  • 痛覚残留
  • member icon

Reputation: 3512
  • View blog
  • Posts: 10,136
  • Joined: 08-June 10

Posted 07 February 2012 - 06:21 AM

minor notes and nitpicking:
- if render() is private, there is no need to make setXML() public
- the string test in __toString() is always true, nowhere in the code breadcrumb is set to anything except a string (even the default value is)
- __toString() should always return a string (i.e. '' instead of NULL)
- in render() I'd reverse the condition:
if (!isset($this->xml))
{
    throw new UnexpectedValueException("No XML given");
}
// proceed with code

though that condition is only met if you explicitly pass NULL as value in the constructor.
- what if the path to XML is invalid?
- what if the output encoding is not Latin-1?
- the XML is missing the XML Processing Instruction (assuming that it is encoded in Latin-1, as is the encoding set in DOMDocument). only XML encoded in UTF/Unicode does not require a PI.
Was This Post Helpful? 1
  • +
  • -

#3 codeprada  Icon User is offline

  • Changed Man With Different Priorities
  • member icon

Reputation: 946
  • View blog
  • Posts: 2,355
  • Joined: 15-February 11

Posted 07 February 2012 - 07:31 AM

Nice catch. I'll use those exceptions instead of triggering errors.

*Changes made.*

This post has been edited by codeprada: 07 February 2012 - 07:37 AM

Was This Post Helpful? 0
  • +
  • -

#4 Dormilich  Icon User is offline

  • 痛覚残留
  • member icon

Reputation: 3512
  • View blog
  • Posts: 10,136
  • Joined: 08-June 10

Posted 07 February 2012 - 07:42 AM

it might be a good idea not to pass the XML string, but an already loaded XML document

ex. (using Dependency Injection)
// ...
public function setXML(DOMDocument $xml)
{
    $this->xml = $xml;
}
// ...

$dom = new DOMDocument("1.0", "ISO-8859-1");
$dom->load('path/to/xml');
$navi = new Navigation_BreadCrumbs($dom);

// or if you like to have it separately
$navi = new Navigation_BreadCrumbs;
$navi->setXML($dom);



since objects are passed by reference I wonder if loading another file into $dom would suffice to change the output (of course it must be done before render() is called) ...

This post has been edited by Dormilich: 07 February 2012 - 07:52 AM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1