我得到了像这样存储在数据库中的菜单
INSERT INTO `mycms_menus` (`menu_id`, `title`, `pos`, `parent_menu_id`) VALUES
(1, 'Menu principal', 0, 0),
(2, 'Menu secondaire', 0, 0),
(3, 'SUBMENU 1-1', 0, 1),
(4, 'SUBMENU 2-1', 0, 2),
(5, 'SUBMENU 1-2', 0, 1),
(6, 'SUBMENU 2-2', 0, 2),
(7, 'submenu 2-3', 0, 2),
(10, 'submenu 1-2-1', 0, 5);
我使用函数将其转换为嵌套数组(multidim)
function _flat_to_nested($source)
{
$nodes = array();
$tree = array();
foreach ($source as &$node) {
$node->children = array();
$id = $node->menu_id;
$parent_id = $node->parent_menu_id;
$nodes[$id] =& $node;
if (array_key_exists($parent_id, $nodes)) {
$nodes[$parent_id]->children[] =& $node;
} else {
$tree[] =& $node;
}
}
return $tree;
}
它返回一个结构化数组,如:
Array
(
[0] => stdClass Object
(
[menu_id] => 1
[title] => Menu principal
[pos] => 0
[parent_menu_id] => 0
[children] => Array
(
[0] => stdClass Object
(
[menu_id] => 3
[title] => SUBMENU 1-1
[pos] => 0
[parent_menu_id] => 1
[children] => Array
(
)
)
[1] => stdClass Object
(
[menu_id] => 5
[title] => SUBMENU 1-2
[pos] => 0
[parent_menu_id] => 1
[children] => Array
(
[0] => stdClass Object
(
[menu_id] => 10
[title] => submenu 1-2-1
[pos] => 0
[parent_menu_id] => 5
[children] => Array
(
)
)
)
)
)
)
[1] => stdClass Object
(
[menu_id] => 2
[title] => Menu secondaire
[pos] => 0
[parent_menu_id] => 0
[children] => Array
(
[0] => stdClass Object
(
[menu_id] => 4
[title] => SUBMENU 2-1
[pos] => 0
[parent_menu_id] => 2
[children] => Array
(
)
)
[1] => stdClass Object
(
[menu_id] => 6
[title] => SUBMENU 2-2
[pos] => 0
[parent_menu_id] => 2
[children] => Array
(
)
)
[2] => stdClass Object
(
[menu_id] => 7
[title] => submenu 2-3
[pos] => 0
[parent_menu_id] => 2
[children] => Array
(
)
)
)
)
)
3.但在这里我被卡住了因为我需要转换这个嵌套数组,以便在codeigniter html helper的form_dropdown中使用,这需要一个类似的数组但具有不同的结构,如:
$options = array(
'menu_id' => 'menu title',
'##' => 'text',
'optgroup text' => array(
'menu_id' => 'menu title',
'##' => 'text',
),
),
);
echo form_dropdown('name',$options,null)
这会将嵌套数组转换为带有嵌套optgroup的下拉列表。
我无法成功将我的表菜单数据转换为嵌套下拉列表,我可以使用下拉帮助器,所以我希望有人可以给我一个提示或线索来实现这一点。提前 我知道我可能会重写整个过程,所以欢迎任何想法
答案 0 :(得分:1)
object[0]
=>[0]
=>[1]
...是您当前的结构
从这棵树中抽出很多工作(recursivley)......我可以建议另一种方法吗?!
还有另一个算法浮现在脑海中,现在无法想到它,它使用了lft和rt指针(啊,我的头脑是空白的)......替代你可以尝试Trees & Hierarchies in SQL
答案 1 :(得分:0)
我终于成功了,没有改变任何东西。我从链接和xpath这样的线性存储中获得了一点灵感。
这是我结束的课程(有些代码不是来自我)。它只是一个基础,我没有在其他项目中测试它,所以它可能包含bug或者没有为你做这个工作,但你可以尝试一下。
它会将平面数组转换为不同类型的multidim /嵌套数组。包括转换为html下拉列表和html列表:
很抱歉这是很多代码。
编辑:经过一些测试后,浏览器不会按预期呈现嵌套的optgroup,因此我不建议使用嵌套的opgroup。我将切换到与某些CSS结合的单选按钮。
<?php
/**
*
* CLASS
* *********************************************************************
* several functions to convert a menu, from a FLAT array into a MULTIDIM/NESTED array
* or into an HTML dropdown OR HTML list
* *********************************************************************
** ::_flat_to_nested ** convert FLAT into MULTIDIM/NESTED
*
** ::_nested_to_ul ** convert MULTIDIM/NESTED into HTML LIST
*
** ::_nested_to_dropdown ** convert MULTIDIM/NESTED into HTML SELECT/OPTGROUP/OPTION
* no nested optgroup allowed
* <optgroup> tag can only contains <options> tags = w3c standards
*
** ::_nested_to_dropdown_html ** convert MULTIDIM/NESTED into HTML SELECT/OPTGROUP/OPTION
* multiple nested optgroups allowed
*
* USAGE:
* $list = {array of objects}
* $t = new myNested;
* $result = $t->index( $list );
*
*/
class myNested {
//results
var $parserStrFin="";
//iterations
var $it=0;
//level
var $profondeur=0;
/**
* convert flat array into ...
*
* @return several formatted arrays
*/
function index($list=array())
{
$nested = $this->_flat_to_nested($list);
$dropdown = $this->_nested_to_dropdown_html($nested);
$dropdown2 = $this->_nested_to_dropdown($nested);
$htmlist = $this->_nested_to_ul($nested);
//trick to convert recursivelly all objects into arrays
// $dropdown = json_decode(json_encode($dropdown), true);
// $dropdown2 = json_decode(json_encode($dropdown2), true);
return array(
'list' => $list,
'nested' => $nested,
'dropdown' => $dropdown,
'dropdown2' => $dropdown2,
'htmlist' => $htmlist,
);
}
/**
*
* convert MULTIDIM/NESTED into HTML SELECT/OPTGROUP/OPTION
* no nested optgroup allowed
* <optgroup> tag can only contains <options> tags = w3c standards
*
* @param array $array
* @param bool $_autoCall
* @param int $profondeur
* @param int $it
* @return str html string to use into <select> tag
*/
private function _nested_to_dropdown( $array=array(), $_autoCall=false, $profondeur="", $it=0 )
{
//first call, reset
if (!$_autoCall)
{
$this->fin=array();
$this->root=null;
$this->it=0;
$this->profondeur=0;
$this->xpath="";
$this->parserStrFin="";
}
//items count
$this->it++;
//dim after dim
foreach( $array as $value )
{
if ( count($value->children)>0 )
{
//if item contains children
$this->profondeur++;
if (!$this->root) {
$this->root =& $this->fin;//change current node
}
$this->root[$value->title] = array();//prep optgroup
$this->xpath .= $value->title."/"; //linear path
$this->root = & $this->root[$value->title]; //define this node as root
$this->root[$value->menu_id] = $value->title;//the optgroup is added as a normal option to be selectable
$this->_nested_to_dropdown( $value->children, true, $this->profondeur, $this->it );//process with children
}
elseif ( count($value->children)==0 && isset($value->title) ) {
//items without children
//title is a string, without chukd: extremity reached
$this->root[$value->menu_id] = $value->title; //each child as new option
}
}//foreach
//after an item without children is added
// returns one dim up
if ($this->profondeur) {
$this->profondeur--;
$regs=array();
$regs = explode('/',$this->xpath);
$regs = array_filter($regs);//clear empty values
//one dim up in the path
$regs = array_slice($regs, 0, $this->profondeur);
if ($regs) {
//goes from dim 0 to dim ##
$n =& $this->fin;
foreach ($regs as $v) {
$n =& $n[$v];//fin[lv1], puis fin[lv1][lv2]
}
//set the new root for next items
$this->root = & $n;
//xpath
$this->xpath=implode('/',$regs);//reset
}
else{
//reset
$this->root =& $this->fin;
$this->xpath='';
}
}
return $this->fin;
}
/**
*
* convert MULTIDIM/NESTED into HTML SELECT/OPTGROUP/OPTION
* multiple nested optgroups allowed !
*
* @param <array> $array
* @param <bool> $_autoCall
* @param <str/int> $profondeur
* @param <int> $it
* @return <str> html string
*
**/
function _nested_to_dropdown_html( $array=array(), $_autoCall=false, $profondeur="", $it=0 )
{
if (!$_autoCall)
{
$this->parserStrFin="";
$this->it=0;
$this->profondeur=0;
}
$this->it++;
foreach( $array as $value )
{
if ( count($value->children)>0 )
{
$p = 15 * $this->profondeur;//or in your .css: optgroup>optgroup {padding-left: 15px};
$this->profondeur++;
$this->parserStrFin .= '<optgroup class="submenu-title" style="padding-left:'.$p.'px" label="'.$value->title.'">'."\n";
$this->parserStrFin .= ' <option class="submenu-item" value="'.$value->menu_id.'">'.$value->title.'</option>'."\n";
$this->_nested_to_dropdown_html( $value->children, true, $this->profondeur, $this->it );
}
elseif ( count($value->children)==0 && isset($value->title) ) {
$this->parserStrFin .= ' <option class="submenu-item" value="'.$value->menu_id.'">'.$value->title.'</option>'."\n";
}
}
if ($this->profondeur) { $this->parserStrFin .= '</optgroup>'."\n"; $this->profondeur--; } //prof = 0, on ferme le UL FirstLevelListTypeTag
return $this->parserStrFin;
}
/**
* convett FLAT en MULTIDIM/NESTED
*
* @param array flat $list
* @return array multi
* @source: php.net
*
* NOTE: array must be well sorted: parents items must be parsed before any of their child
*/
private function _flat_to_nested($source)
{
$nodes = array();
$tree = array();
foreach ($source as &$node) {
$node->children = array();
$id = $node->menu_id;
$parent_id = $node->parent_menu_id;
$nodes[$id] =& $node;
if (array_key_exists($parent_id, $nodes)) {
$nodes[$parent_id]->children[] =& $node;
} else {
$tree[] =& $node;
}
}
return $tree;
}
/**
* convert MULTIDIM/NESTED into HTML LIST
*
* @param array $array
* @param bool $_autoCall
* @param int $profondeur
* @param int $it
* @return <str> html list
*
**/
function _nested_to_ul( $array=array(), $_autoCall=false, $profondeur="", $it=0 )
{
if (!$_autoCall)
{
$this->parserStrFin="";
$this->it=0;
$this->profondeur=0;
}
$firstListType = 'ul';//first level list type
$itemsListType = 'ul';//sub level list type
$tag = (empty($this->profondeur) && $this->it==0) ? $firstListType : $itemsListType;
$this->parserStrFin .= "\n".'<'.$tag.' class="level-'.$this->profondeur.'">'."\n";
$this->it++;
foreach( $array as $value )
{
if ( count($value->children)>0)
{
$this->profondeur++;
$this->parserStrFin .= ' <li class="submenu-title"><h3>'. $value->title .'</h3>'."\n";
$this->_nested_to_ul( $value->children, true, $this->profondeur, $this->it );
}
elseif ( count($value->children)==0 && isset($value->title) ) {
$this->parserStrFin .= ' <li class="submenu-item">'. '<a href="#'.$value->menu_id.'">'.$value->title.'</a>' .'</li>'."\n";
}
}
if ($this->profondeur == 0) { $this->parserStrFin .= "</".$firstListType.">\n"; $this->profondeur--; }
else if (($this->profondeur > 0)) { $this->parserStrFin .= "</".$itemsListType.">\n</li>\n"; $this->profondeur--; }
return $this->parserStrFin;
}
}
//SAMPLE USAGE:
//DATAS:
//the class uses FLAT array containing "objects" so here's the datas
$list='a:8:{i:0;O:8:"stdClass":4:{s:7:"menu_id";s:1:"1";s:5:"title";s:14:"Menu principal";s:3:"pos";s:1:"0";s:14:"parent_menu_id";s:1:"0";}i:1;O:8:"stdClass":4:{s:7:"menu_id";s:1:"2";s:5:"title";s:15:"Menu secondaire";s:3:"pos";s:1:"0";s:14:"parent_menu_id";s:1:"0";}i:2;O:8:"stdClass":4:{s:7:"menu_id";s:1:"3";s:5:"title";s:11:"SUBMENU 1-1";s:3:"pos";s:1:"0";s:14:"parent_menu_id";s:1:"1";}i:3;O:8:"stdClass":4:{s:7:"menu_id";s:1:"4";s:5:"title";s:11:"SUBMENU 2-1";s:3:"pos";s:1:"0";s:14:"parent_menu_id";s:1:"2";}i:4;O:8:"stdClass":4:{s:7:"menu_id";s:1:"5";s:5:"title";s:11:"SUBMENU 1-2";s:3:"pos";s:1:"0";s:14:"parent_menu_id";s:1:"1";}i:5;O:8:"stdClass":4:{s:7:"menu_id";s:1:"6";s:5:"title";s:11:"SUBMENU 2-2";s:3:"pos";s:1:"0";s:14:"parent_menu_id";s:1:"2";}i:6;O:8:"stdClass":4:{s:7:"menu_id";s:1:"7";s:5:"title";s:11:"submenu 2-3";s:3:"pos";s:1:"0";s:14:"parent_menu_id";s:1:"2";}i:7;O:8:"stdClass":4:{s:7:"menu_id";s:2:"10";s:5:"title";s:13:"submenu 1-2-1";s:3:"pos";s:1:"0";s:14:"parent_menu_id";s:1:"5";}}';
$list=unserialize($list);
//EXEC!
$t = new myNested;
$result = $t->index( $list );
//RETURNS:
$source = $result['list'];
$nested = $result['nested'];
$dropdown = $result['dropdown'];
$dropdown2 = $result['dropdown2'];
$htmlist = $result['htmlist'];
echo '<pre style="border:1px solid #C00; color: #C00; background:#FFFFFE;">';
echo "<B>SOURCE=</B>:<br/>"; print_r($source); echo "<hr/>";
echo "<B>NESTED=</B>:<br/>"; print_r($nested); echo "<hr/>";
echo "<B>DROPDOWN=</B>:<br/>"; highlight_string($dropdown); echo "<hr/>";
echo "<B>DROPDOWN2=</B>:<br/>"; print_r($dropdown2); echo "<hr/>";
echo "<B>HTMLIST=</B>:<br/>"; print_r($htmlist); echo "<hr/>";
echo '</pre>';
?>