如何创建可排序的导航项

时间:2012-12-10 18:36:03

标签: php class recursion navigation

我正在创建一个网站,用户应该可以更改导航项的顺序。但是,导航项是从不同的源动态加载的,我不知道有多少导航项,也不知道我可以期待什么项。我所知道的是,我有多个这样的文件:

return array(
    array('Google', 'http://www.google.com'),
    array('StackOverflow', 'http://www.stackoverflow.com',
        array(
            array('Contact', 'contact.html'),
            array('Test', 'test.html')
        )
    ),
    array('Home', 'index.html')
);

正如您所看到的,这是一个菜单项树,其子项可以是任何深层。所有这些阵列必须组合成一个导航菜单,文件可以经常更改。显然,如果项目不必是用户可排序的,这并不难。但是有了这个功能,我就无法想出一个有效的解决方案。特别是因为我不想将业务逻辑和表示混合在一起。

目前,我有以下代码(稍微简化):

控制器:

$view = new \View('navigation.html');
$navigation = new mappers\Navigation();
$view->items = $navigation->fetchAll();

映射器\导航:

class Navigation {
    private $type;

    // Create a tree from:
    // $items: array(title, url, children); title = string; url = array / null / string; children = array / null
    // $order: url => array(position, children); position = integer; children = array / null
    // Pass $order by reference so that found items can be added
    private function buildTree($items, &$order) {
        $tree = array();

        foreach ($items as $item) {
            $uri = $item[1];

            // Get order
            if (isset($order[$uri])) {
                $position = $order[$uri][0];
                $childrenOrder = $order[$uri][1];
            } else {
                $order[$uri] = array(count($order), array()); // Create the item for this uri

                $position = $order[$uri][0];
                $childrenOrder = $order[$uri][1];
            }

            // Get children tree if specified
            if (isset($item[2])) {
                $children = $this->buildTree($item[2], $childrenOrder);
            } else {
                $children = null;
            }

            $tree[$position] = array($item[0], $uri, $children);
        }
        ksort($tree);   

        return $tree;
    }

    public function fetchAll() {
        $items = array();

        foreach (new \FilesystemIterator('navigation', \FilesystemIterator::SKIP_DOTS) as $file) {
            if ($file->isDir()) { // Ignore everything which isn't a directory
                $array = new \PersistentArray($file->getPathname() . '/items.php');
                foreach ($array as $item) {
                    $items[] = $item;
                }
            }
        }

        // Load the order settings
        $order = new \PersistentArray('order.php');

        // Build a tree of all navigation items in the right order
        $tree = $this->buildTree($items, $order);

        Save the order settings (have been changed, since $order is passed by reference)
        $order->save();

        return $tree;
    }
}

navigation.html:

$echo = '';
$echo = function($item) use($echo) { // Use a closure, in order not to pollute the global namespace
    printf('<li><a href="%s">%s</a>', $item[1], $item[0]);
    if (!empty($item[2])) {
        printf('<ul>%s</ul>', $echo($item[2]));
    }
    echo '</li>';
}

?>

<ul>
    <?php foreach ($items as $item) {
        $echo($item);
    } ?>
</ul>

在此代码中,PersistentArray是一个从文件加载数组的简单类。可以像任何数组一样访问此类的对象。它们还提供了一个方法save(),它将数组写入文件。稍后可以使用此类再次打开此文件。

上面的代码可以正常工作,但是这样做对我来说效率很低。我在这段代码中有四个循环!更不用说引用和闭包非常“hacky”。但我无法想出更好的方法来解决这个问题。

所以我的问题是:你能想出一个更好的方法吗?有经常使用的方法吗?感谢正手!


保存的订单数组的示例是:

return array(
    'index.html' => array(
        0 => 0,
        1 => array(
            'page1.html' => array(
                0 => 0,
                1 => array()
            ),
            'page2.html' => array(
                0 => 1,
                1 => array()
            )
        )
    ),
    'test.html' => array(
        0 => 1,
        1 => array()
    )
);

0 个答案:

没有答案