嵌套数组,用于在代码点火器中创建下拉列表


nested array to create a dropdown in codeigniter

  1. 我得到了存储在数据库中的菜单,如下所示

    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);
    
  2. 我使用一个函数将其转换为嵌套数组(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助手转换这个嵌套数组以用于form_dropdown,该助手需要类似的数组但具有不同的结构,例如:

    $options = array(
        'menu_id'  => 'menu title',
        '##' => 'text',
        'optgroup text' => array(
            'menu_id'  => 'menu title',
            '##' => 'text',
            ),
        ),
    );
    echo form_dropdown('name',$options,null)

这会将嵌套数组转换为包含嵌套选项组的下拉列表。

我无法成功地将我的表菜单数据转换为可以与下拉助手一起使用的嵌套下拉列表,因此我希望有人可以给我一个提示或线索来实现这一目标。 提前感谢我知道我可能会重写整个过程,所以欢迎任何想法

object[0]
    =>[0]
    =>[1]

。是您当前的结构

从这棵树(递归(中提取很多工作......我可以建议另一种方法吗?!

还有一个算法浮现在脑海中,现在想不起来,它使用 lft 和 rt 指针(啊,我的脑子是空白的(......或者你可以尝试SQL中的树和层次结构

我终于成功了,什么也没改变。我受到链接和xpath等线性存储的启发。

这是我结束的类(有些代码不是我写的(。这只是一个基础,我没有在其他项目中测试过它,所以它可能包含错误或不适合您完成工作,但您可以尝试一下。

它将平面数组转换为不同类型的多迪姆/嵌套数组。包括转换为 html 下拉列表和 html 列表:

抱歉,代码很多。

编辑:经过更多的测试,嵌套的optgroups不会像预期的那样被浏览器呈现,所以我不建议使用嵌套的opgroups。我将切换到结合一些 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>';

  ?>