嵌套数组,用于在codeigniter中创建下拉列表

时间:2011-12-27 01:08:18

标签: php codeigniter drop-down-menu

  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;
    
    }
    
  3. 它返回一个结构化数组,如:

        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的下拉列表。

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

2 个答案:

答案 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>';



  ?>