如何在PHP中使用连接字符串构建树?

I try to make a tree list in PHP from a hierarchy stored in a concatenated string in my mysql database

This my table :

Screenshot of table

and I'd like to reproduce something like this :

<ul>
  <li>
    <ul>
      <li></li>
      <li>
          <ul>
              <li></li>
              <li></li>
           </ul>
      </li>
      <li></li>
    </ul>
  </li>
  <li></li>
  <li></li>
</ul>

I know I have to use a recursive function I don't reach to do...

Maybe someone could help me

code without comments

see the usage and dataset section below to see what you need to pass and how to use these functions:

function items_to_tree( $items ){
  $array = array();
  foreach( $items as $item ) {
    $parts = explode('.', $item['hierarchy']);
    $last = array_pop( $parts );
    $cursor = &$array;
    foreach ( $parts as $part ) {
      if ( !is_array($cursor[$part]) ) {
        $cursor[$part] = array();
      }
      $cursor = &$cursor[$part];
    }
    $cursor[$last]['#item'] = $item;
  }
  return $array;
}

function tree_to_ul( $tree ){
  $html = $children = '';
  foreach( $tree as $key => $item ){
    if ( substr($key,0,1) == '#' ) continue;
    $children .= tree_to_ul( $item );
  }
  if ( isset($tree['#item']) ) {
    $html .= '<li>' . PHP_EOL;
    $html .= '<em>' . $tree['#item']['menu_text'] . '</em>' . PHP_EOL;
    $html .= ( $children ? '<ul>' . $children . '</ul>' . PHP_EOL : '' );
    $html .= '</li>' . PHP_EOL;
    return $html;
  }
  else {
    return $children;
  }
}


code with comments and explanation

The code to convert your items to a tree structure:

function items_to_tree( $items ){
  $array = array();
  foreach( $items as $item ) {
    /// split each hierarchy string into it's dot separated parts
    $parts = explode('.', $item['hierarchy']);
    /// pop off the last item of the array, we'll use this for assignment later
    $last = array_pop( $parts );
    /// create a reference to our position in the array we wish to fill out
    $cursor = &$array;
    /// step each hierarchy part and travel down the array structure, 
    /// just like you would if you typed an array path manually. 
    /// i.e. $array[$part][$part][...] and so on
    foreach ( $parts as $part ) {
      /// if at this point in the array, we don't have an array, make one.
      if ( !is_array($cursor[$part]) ) {
        $cursor[$part] = array();
      }
      /// ready for the next step, shift our reference to point to the next
      /// $part in the array chain. e.g. if $cursor pointed to `$array[$part]`
      /// before, after the next line of code the $cursor will point 
      /// to `$array[$oldpart][$part]`
      $cursor = &$cursor[$part];
    }
    /// we popped the last item off the $parts array so we could easily
    /// assign our final value to where the $cursor ends up pointing to.
    /// starting with a hierarchy of '00001.00002.00003' would mean at this
    /// point $cursor points to $array['00001']['00002'] and $last = '00003';
    /// so finally we get $array['00001']['00002']['00003']['#item'] = $item;
    $cursor[$last]['#item'] = $item;
    /// use '#item' to keep our item's information separate from it's children.
  }
  /// return our built up array.
  return $array;
}

The code to convert the tree structure to a UL:

function tree_to_ul( $tree ){
  /// start with nothing
  $html = $children = '';
  /// step each item found in the current level of $tree
  foreach( $tree as $key => $item ){
    /// if the item's key starts with a # skip, these contain
    /// our item's information and should not be treated as children
    if ( substr($key,0,1) == '#' ) continue;
    /// recurse this function so that we do the same for any child @ any level.
    $children .= tree_to_ul( $item );
  }
  /// if at this level a #item has been set, use this item information to
  /// add a title to our level. You could change this to add whatever info
  /// from your original database item that you'd like.
  if ( isset($tree['#item']) ) {
    $html .= '<li>' . PHP_EOL;
    $html .= '<em>' . $tree['#item']['menu_text'] . '</em>' . PHP_EOL;
    $html .= ( $children ? '<ul>' . $children . '</ul>' . PHP_EOL : '' );
    $html .= '</li>' . PHP_EOL;
    return $html;
  }
  /// if there wasn't an item, just return the traversed children.
  else {
    return $children;
  }
}

dataset:

/// I simplified your dataset to an array, this could easily be generated
/// from a database query. You could also convert my code so that you
/// don't have to pre-generate an array, and instead could process after
/// each fetch from the database.

$items = array(
  array('hierarchy' => '00001',             'menu_text' => 'One'),
  array('hierarchy' => '00002',             'menu_text' => 'Two'),
  array('hierarchy' => '00002.00001',       'menu_text' => 'Three'),
  array('hierarchy' => '00002.00002',       'menu_text' => 'Four'),
  array('hierarchy' => '00002.00003',       'menu_text' => 'Five'),
  array('hierarchy' => '00002.00004',       'menu_text' => 'Six'),
  array('hierarchy' => '00003',             'menu_text' => 'Seven'),
  array('hierarchy' => '00003.00001',       'menu_text' => 'Eight'),
  array('hierarchy' => '00003.00001.00001', 'menu_text' => 'Nine'),
  array('hierarchy' => '00003.00001.00002', 'menu_text' => 'Ten'),
  array('hierarchy' => '00003.00001.00003', 'menu_text' => 'Eleven'),
  array('hierarchy' => '00003.00002',       'menu_text' => 'Twelve'),
);

usage:

/// Simple usage :) if a little complex explanation

$tree = items_to_tree( $items );
$html = tree_to_ul( $tree );

echo $html;


in the interests of codegolf ;)

The following could replace my items_to_tree function -- however it isn't advised.

$a = array();
foreach($items as $i){
 eval('$a["'.str_replace('.','"]["',$i['hierarchy']).'"]=array("#item"=>$i);');
}
    $refs = new stdClass();
    //Assuming $data is the result array of your query to fetch the data.
    foreach($data as $result)
    {
        $name = $result['hierarchy'];
        $parent = substr($result['hierarchy'],0,strrpos($result['hierarchy'],'.'));
        $thisref = &$refs->{$name};
        foreach($result as $k => $v)
        {
            $thisref->{$k} = $v;
        }
        if ($parent == '') {
            $tree->{$name} = &$thisref;
        } else {
            $refs->{$parent}->children->{$name} = &$thisref;
        }
    }

This will give you a nice object with every node's child in the property children.

function drawUL($level){
    echo '<ul>';
    foreach($level as $li){
        echo '<li>'.$li->label;
        if(isset($li->children))drawUl($li->children);
        echo '</li>';
    }
    echo '</ul>';
}
drawUl($tree);