我正在开发一个内容管理系统,我遇到了CMS中项目的父子关系问题。
基本上我有一个系统,可以创建页面,当一个页面被创建,你可以选择父页面的子导航。这一切都很好,直到我尝试从DB生成导航。
我不确定某种连接是否会更好,但我更喜欢在数组中获取所有数据并使用php操作数组。
我的数组结果从DB是这样的:
Array
(
[0] => Array
(
[id] => 27
[name] => home
[link] => home.html
[parent] => 0
)
[1] => Array
(
[id] => 30
[name] => about
[link] => about.html
[parent] => 27
)
)
我需要循环遍历这样一个数组,它可以有任意数量的导航,并将其智能地排序到它的父子关系中。我能做到,但只有一层深。它需要管理无限层的子层的子层等,并将其输出到HTML无序嵌套列表。
<ul>
<li>
<a>Nav</a>
<ul>
<li>
<a>Subnav</a>
<ul>
<li>
<a>Sub Sub nav</a>
</li>
</ul>
</li>
</ul>
<li>
<ul>
等等…
我认为你不应该研究对象。另外,我认为生成对象等只是额外的工作。在我看来,您应该循环遍历数组并生成一个表示导航层次结构的多维数组,然后递归地循环生成的数组以生成HTML。我已经为你做了一个示例代码,它按你想要的方式工作,但你可能想做一些改变。
// Generate your multidimensional array from the linear array
function GenerateNavArray($arr, $parent = 0)
{
$pages = Array();
foreach($arr as $page)
{
if($page['parent'] == $parent)
{
$page['sub'] = isset($page['sub']) ? $page['sub'] : GenerateNavArray($arr, $page['id']);
$pages[] = $page;
}
}
return $pages;
}
// loop the multidimensional array recursively to generate the HTML
function GenerateNavHTML($nav)
{
$html = '';
foreach($nav as $page)
{
$html .= '<ul><li>';
$html .= '<a href="' . $page['link'] . '">' . $page['name'] . '</a>';
$html .= GenerateNavHTML($page['sub']);
$html .= '</li></ul>';
}
return $html;
}
**示例用法**
$nav = Array
(
Array
(
'id' => 27,
'name' => 'home',
'link' => 'home.html',
'parent' => 0
),
Array
(
'id' => 30,
'name' => 'about',
'link' => 'about.html',
'parent' => 27
)
);
$navarray = GenerateNavArray($nav);
echo GenerateNavHTML($navarray);
你可以一步完成这两件事,但我认为先生成多维数组更简洁。古德勒克!
Saad Imran的解决方案工作正常,除非你想在同一列表中多个导航项。我必须修改几行代码才能生成经过验证的项目列表。我还添加了缩进,使生成的代码更具可读性。我使用空格,但这可以很容易地更改为制表符,如果你喜欢,只需替换4个空格与"'t"
。(注意必须使用双引号,而不是单引号,否则PHP不会用TAB字符替换,而是用't)
我在codeigniter 2.1.0中使用superfish (PHP5)
function GenerateNavHTML($nav, $tabs = "")
{
$tab=" ";
$html = "'n$tabs<ul class='"sf-menu'">'n";
foreach($nav as $page)
{
//check if page is currently being viewed
if($page['link'] == uri_string()) {
$html .= "$tabs$tab<li class='"current'">";
} else {
$html .= "$tabs$tab<li>";
}
$html .= "<a href='"$page[link]'">$page[name]</a>";
//Don't generate empty lists
if(isset($page['sub'][0])) {
$html .= $this->GenerateNavHTML($page['sub'], $tabs.$tab);
}
$html .= "</li>'n";
}
$html .= $tabs."</ul>'n";
return $html;
}
我正在得到(为了澄清,切换到ol)
1. Home
1. About Us
1. Products
1. sub-product 1
1. sub-product 2
1. Contact
现在得到
1. Home
2. About Us
3. Products
1. sub-product 1
2. sub-product 2
4. Contact
生成精美的HTML
<ul class="sf-menu">
<li class="current"><a href="index.html">Home</a></li>
<li><a href="about.html">About Us</a></li>
<li><a href="products.html">Products</a>
<ul class="sf-menu">
<li><a href="products-sub1.html">sub-product 1</a></li>
<li><a href="products-sub2.html">sub-product 2</a></li>
</ul>
</li>
<li><a href="contact.html">Contact</a></li>
</ul>
最简单的方法可能是使用对象。这些可以很容易地操作,也可以很容易地从数据库中获得的数组中创建。
例如,每个对象都有:
- ID
- 对父对象的对象引用
- 子对象列表
您将需要一个静态函数,该函数能够找出具有特定数据库ID的对象。这是在创建新对象期间通过在静态列表中放置引用来完成的。然后可以在运行时调用并检查此列表。
其余部分非常简单:
$arr = get_array_from_database();
foreach($arr as $node){
$parent_object = get_parent_object($node_id);
$parent_object.subnodes[] = new NodeObject($node);
}
至于返回列表中的对象,最好是递归地完成;
function return_in_list($objects)
{
foreach($objects as $node)
{
echo '<li>';
echo '<a>' + node.link + '</a>';
if(node.subnodes.length > 0)
{
echo '<ul>';
return_in_list($node.subnodes);
echo '</ul>';
}
puts '</li>'
}
}
我在同一个项目上工作,我还没有发现需要使用对象。数据库基本上负责结构,php函数可以完成其余的工作。我的解决方案是为指向节名的页面添加父字段,就像您所做的那样,但也为sections表添加父字段,以便section可以作为父部分指向其他section。它仍然非常易于管理,因为只有两个父字段要跟踪,每个表一个,并且我可以在将来根据需要嵌套我的结构。
对于动态树创建,我将递归地检查父元素,直到遇到null,这表明当前元素位于doc根目录。这样我们就不需要知道函数代码中当前页面结构的任何细节,我们可以专注于在mysql中添加和排列页面。由于另一张海报展示了一些菜单html,我想我应该在这里添加一个动态面包屑路径的例子,因为主题是如此相似。
数据// Sample 3 x 2 page array (php / html pages)
// D1 key = page name
// D2 element 1 = page name for menu display
// D2 element 2 = parent name (section)
$pages = Array
(
'sample-page-1.php' => Array ( 'M' => 'page 1', 'P' => null ),
'sample-page-2.php' => Array ( 'M' => 'page 2', 'P' => 'hello' ),
'sample-page-3.php' => Array ( 'M' => 'page 3', 'P' => 'world' )
);
// Sample 2 x 1 section array (parent directories)
// D1 key = section name
// D2 element = parent name (if null, assume root)
$sections = Array
(
'hello' => null,
'world' => 'hello'
);
$sep = ' > '; // Path seperator
$site = 'test.com'; // Home string
功能// Echo paragraph to browser
function html_pp ( $text )
{
echo PHP_EOL . '<p>' . sprintf ( $text ) . '</p>' . PHP_EOL;
}
// Get breadcrumb for given page
function breadcrumb ( $page )
{
// Reference variables in parent scope
global $pages;
global $sections;
global $sep;
global $site;
// Get page data from array
$menu = $pages [ $page ] [ 'M' ];
$parent = $pages [ $page ] [ 'P' ];
if ( $parent == null )
{
$path = $site . $sep . $menu;
}
else
{
$path = $site . $sep . get_path ( $parent ) . $sep . $menu;
}
return $path;
}
// Trace ancestry back to root
function get_path ( $parent )
{
// Reference variables in parent scope
global $sections;
global $sep;
if ( $sections [ $parent ] == null )
{
// No more parents
return $parent;
}
else
{
// Get next parent through recursive call
return get_path ( $sections [ $parent ] ) . $sep . $parent;
}
}
使用// Get breadcrumbs by page name
$p1 = 'sample-page-1.php';
$p2 = 'sample-page-2.php';
$p3 = 'sample-page-3.php';
html_pp ( $p1 . ' || ' . breadcrumb ( $p1 ) );
html_pp ( $p2 . ' || ' . breadcrumb ( $p2 ) );
html_pp ( $p3 . ' || ' . breadcrumb ( $p3 ) );
// or use foreach to list all pages
foreach ( $pages as $page => $data)
{
html_pp ( $page . ' || ' . breadcrumb ( $page ) );
}
输出sample-page-1.php || test.com> page 1
sample-page-2.php || test.com> hello> page 2
sample-page-3.php || test.com> hello> world> page 3