问题
What is the best way to represent a tree navigation structure using PHP? I need to be able to track the current page location in the tree using a bread crumb trail. Parts of the tree are generated from a database such as make -> models. Each model then has the same equivalent tree branch, such as:
Make > Model > (Area 1, Area 2, Area 3).
Parts of the tree may change over time. Is it best to have a static class hierarchy structure or a dynamic solution where classes are re-used?
Hope this has been explained simply.
回答1:
I'd go with:
- a
$nodes
list for each element [if empty it's a leaf node of course]; - a
$parent
field for parent element [if null it's the root node].
This way you can rebuild the breadcrumb trail for every node, providing them a getTrail()
method:
public function getTrail()
{
$parent = $this -> parent();
$trail = array();
while($parent !== NULL)
{
// push parent element to trail
$trail[] = $parent;
// go backwards one node
$parent = $parent -> parent();
}
// you want trail in reverse order [from root to latest]
return array_reverse($trail);
}
If your nodes differ in type, be clean providing a TrailTrackable
interface with getTrail()
/ getParent()
methods at least.
回答2:
class TreeNode {
/**
* the parent node
*
* @var TreeNode
*/
private $parentNode=null;
/**
* the children of this node
*
* @var TreeNode[]
*/
private $children=array();
/**
* The user element this tree node holds.
*
* @var Object
*/
private $element;
}
回答3:
From an OO point of view, I'd recommend defining an interface such as the following:
interface BreadcrumbInterface
{
public function getLabel();
public function getParent(); // returns an instance of BreadcrumbInterface, or null
}
Then you'd create a Page class, which implements this interface and can optionally contain a 'parent', which must also implement this interface. This will build the heirarchy that you need.
A great way to retrieve the full breadcrumbs (while brushing up on your OO design patterns in the process) is to use the visitor pattern. In this case, you probably want to define a common abstract class as well as the interface in order to 'abstract away' the logic of handling the visitor...
abstract class BaseNode implements BreadcrumbInterface
{
protected $parent = null;
public function accept(BreadcrumbVisitor $visitor)
{
$visitor->visit($this);
}
public function setParent(BreadcrumbInterface $parent)
{
$this->parent = $parent;
}
public function getParent()
{
return $this->parent;
}
}
class BreadcrumbVisitor
{
protected $breadcrumbs = array();
public function visit(BreadcrumbInterface $node)
{
$parent = $node->getParent();
if ($parent instanceof BaseNode) {
$parent->accept($this);
}
$this->breadcrumbs[] = $node->getLabel();
}
public function getBreadcrumbs()
{
return $this->breadcrumbs;
}
}
This won't run as is, but hopefully you get the idea. You probably also want your nodes to determine the URL to their page as well as the label, but that can be added easily enough. I just wanted to show the general OO structure to solving this problem.
Edit:
Adding rough usage example:
$rootPage = new Page(/*...*/);
$parentPage = new Page(/*...*/);
$parentPage->setParent($rootPage); // In reality you most likely wouldn't be building this structure so explicitly. Each object only needs to know about it's direct parent
$currentPage = new Page(/*...*/);
$currentPage->setParent($parentPage);
$visitor = new BreadcrumbVisitor();
$currentPage->accept($visitor);
$breadcrumbs = $visitor->getBreadcrumbs(); // returns an array, where the first element is the root
// then you can implode with ' > ' if you want
$breadcumbString = implode(' > ', $breadcrumbs);
来源:https://stackoverflow.com/questions/12394934/php-hierarchical-navigational-oop-structure