Web lists-archives.com

Re: [PHP] Nested Menu


Something like this looks more elegant to me


$menuItems = array(
    array('id'=>1, 'title'=>'One', 'parent_id'=>0),
    array('id'=>2, 'title'=>'two', 'parent_id'=>0),
    array('id'=>3, 'title'=>'three', 'parent_id'=>0),
    array('id'=>4, 'title'=>'two.1', 'parent_id'=>2),
    array('id'=>5, 'title'=>'two.2', 'parent_id'=>2),
    array('id'=>6, 'title'=>'two.1.1', 'parent_id'=>4),
    array('id'=>7, 'title'=>'two.1.2', 'parent_id'=>4),
    array('id'=>8, 'title'=>'two.1.3', 'parent_id'=>4),
    array('id'=>9, 'title'=>'two.1.3.1', 'parent_id'=>8),
    array('id'=>10, 'title'=>'two.1.3.2', 'parent_id'=>8),
    array('id'=>11, 'title'=>'two.', 'parent_id'=>10),
    array('id'=>12, 'title'=>'two.', 'parent_id'=>10),
    array('id'=>13, 'title'=>'two.', 'parent_id'=>10),

class DomUnorderedListBuilder {

    private $items;
    private $processed;
    private $document;

    public function __construct(array $items)
        $this->items = $items;

    public function build()
        $items = $this->items;
        $this->processed = array();
        $this->document = new DOMDocument('1.0', 'utf-8');

        $rootNode = $this->doBuild($items);

        return $this->document;

    private function getChildrenItemsOf($parent)
        return array_filter($this->items, function(array $item) use
($parent) { return $item['parent_id'] === $parent; });

    private function doBuild(array $unprocessedItems)
        $rootNode = $this->document->createElement('ul');
        $this->processItems($unprocessedItems, $rootNode);

        return $rootNode;

    private function processItems(array $unprocessedItems, DOMNode
        foreach ($unprocessedItems as $item) {
            if ($this->isProcessed($item['id'])) continue;

            $liNode = $this->document->createElement('li', $item['title']);

            $children = $this->getChildrenItemsOf($item['id']);

            if (!empty($children)) {
                $ulNode = $this->document->createElement('ul');
                $this->processItems($children, $ulNode);

    private function isProcessed($id)
        return isset($this->processed[$id]);

    private function markProcessed($id)
        $this->processed[$id] = true;

$b = new DomUnorderedListBuilder($menuItems);
$document = $b->build();
$document->formatOutput = true;

echo $document->saveHTML(), PHP_EOL;

On Wed, Jan 6, 2016 at 8:47 AM, Kevin Waterson <kevin.waterson@xxxxxxxxx>

> Trying to create a menu based on results from an adjacency list and DOM.
> I can get it work with some nasty hacks with loadHTML in the constructor
>  and str_replace in the toString functions.
> I was looking for something more elegant.
> http://pastie.org/10672688
> Thanks
> Kev