Каков наилучший способ представления структуры навигации дерева с помощью PHP? Мне нужно иметь возможность отслеживать текущее расположение страницы в дереве с помощью тропы для хлебных крошек. Части дерева генерируются из базы данных, например make -> models. Каждая модель имеет такую же эквивалентную ветвь дерева, как:
Сделайте> Модель> (Площадь 1, Площадь 2, Площадь 3).
Части дерева могут меняться со временем. Лучше ли иметь иерархическую структуру статического класса или динамическое решение, в котором классы повторно используются?
Надеюсь, это объяснено просто.
Я бы пошел с:
$nodes
для каждого элемента [если пустым является, конечно, листовой узел]; $parent
для родительского элемента [если null это корневой узел]. Таким образом, вы можете восстановить getTrail()
для каждого узла, предоставив им метод getTrail()
:
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); }
Если ваши узлы отличаются по типу, будьте чисты, предоставляя интерфейс getTrail()
с помощью getTrail()
/ getParent()
.
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; }
С точки зрения OO я бы рекомендовал определить интерфейс, такой как:
interface BreadcrumbInterface { public function getLabel(); public function getParent(); // returns an instance of BreadcrumbInterface, or null }
Затем вы создадите класс Page, который реализует этот интерфейс и может необязательно содержать «родительский», который также должен реализовать этот интерфейс. Это создаст вам иерархию.
Отличный способ получить полные панировочные сухари (при чистке ваших шаблонов проектирования OO в процессе) – использовать шаблон посетителя . В этом случае вы, вероятно, хотите определить общий абстрактный класс, а также интерфейс, чтобы «абстрагировать» логику обработки посетителя …
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; } }
,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; } }
Это не будет работать, как есть, но, надеюсь, вы получите эту идею. Вероятно, вы также захотите, чтобы ваши узлы определяли URL-адрес на своей странице, а также метку, но ее можно добавить достаточно легко. Я просто хотел показать общую структуру OO для решения этой проблемы.
Редактировать:
Добавление примерного примера использования:
$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);