我正在创建一个用户应该能够更改导航项顺序的站点。但是,导航项是从不同来源动态加载的,我不知道有多少导航项,也不知道我可以期待哪些项。我所知道的是我有多个这样的文件:
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;
}
}
导航.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()
)
);