从平面数据在 PHP 中构建分层 html 标签

Posted

技术标签:

【中文标题】从平面数据在 PHP 中构建分层 html 标签【英文标题】:Build hierarchical html tags in PHP from flat data 【发布时间】:2010-09-24 00:38:17 【问题描述】:

如何在 php 中构建带有数据的分层标签集?

例如嵌套列表:

<div>
    <ul>
        <li>foo
        </li>
        <li>bar
            <ul>
                <li>sub-bar
                </li>
            </ul>
        </li>
    </ul>
</div>

这将是从这样的平面数据构建的:

nested_array = array();
nested_array[0] = array('name' => 'foo', 'depth' => 0)
nested_array[1] = array('name' => 'bar', 'depth' => 0)
nested_array[2] = array('name' => 'sub-bar', 'depth' => 1)

如果它的格式也像示例一样很好。

【问题讨论】:

您的数组中确实没有足够的数据来构建层次结构。脚本如何知道将“子栏”项目附加到“栏”?如果你再做一次嵌套,那它会是“子子吧”吗?深度值也有点没用。为什么不让你的数组也分层呢? 数组的顺序很重要,诀窍是从具有深度信息的平面数据构建层次结构。我将编辑标题以使其更加明显。 好的,那么确保子项正确附加到其各自父项的约定是什么?索引顺序?命名约定? 【参考方案1】:

你的意思是这样的

function array_to_list(array $array, $width = 3, $type = 'ul', $separator = ' ', $depth = 0)

    $ulSpace = str_repeat($separator, $width * $depth++);
    $liSpace = str_repeat($separator, $width * $depth++);
    $subSpace = str_repeat($separator, $width * $depth);
    foreach ($array as $key=>$value) 
        if (is_array($value)) 
        $output[(isset($prev) ? $prev : $key)] .= "\n" . array_to_list($value, $width, $type, $separator, $depth);
         else 
            $output[$key] = $value;
            $prev = $key;
        
    
    return "$ulSpace<$type>\n$liSpace<li>\n$subSpace" . implode("\n$liSpace</li>\n$liSpace<li>\n$subSpace", $output) . "\n$liSpace</li>\n$ulSpace</$type>";


echo array_to_list(array('gg', 'dsf', array(array('uhu'), 'df', array('sdf')), 'sdfsd', 'sdfd')) . "\n";

生产

<ul>
   <li>
      gg
   </li>
   <li>
      dsf
      <ul>
         <li>

            <ul>
               <li>
                  uhu
               </li>
            </ul>
         </li>
         <li>
            df
            <ul>
               <li>
                  sdf
               </li>
            </ul>
         </li>
      </ul>
   </li>
   <li>
      sdfsd
   </li>
   <li>
      sdfd
   </li>
</ul>

如果子列表没有以解释开头,我知道会有一点差距。

就个人而言,我通常并不关心 html 的外观,只要它易于在 PHP 中使用即可。

编辑:好的,如果你先运行它,它就可以工作......:P

function flat_array_to_hierarchical_array(array &$array, $depth = 0, $name = null, $toDepth = 0)

    if ($depth == 0) 
        $temp = $array;
        $array = array_values($array);
    
    if (($name !== null) && ($depth == $toDepth)) 
        $output[] = $name;
     else if ($depth < $toDepth) 
        $output[] = flat_array_to_hierarchical_array(&$array, $depth + 1, $name, $toDepth);
    
    while ($item = array_shift($array)) 
        $newDepth = $item['depth'];
        $name = $item['name'];
        if ($depth == $newDepth) 
            $output[] = $name;
         else if ($depth < $newDepth) 
            $output[] = flat_array_to_hierarchical_array(&$array, $depth + 1, $name, $newDepth);
         else 
            array_unshift($array, $item);
            return $output;
        
    
    $array = $temp;
    return $output;


$arr = flat_array_to_hierarchical_array($nested_array);
echo array_to_list($arr);

【讨论】:

这看起来像是分层嵌套数组的解决方案。一个很好的解决方案,但不适用于我的问题。【参考方案2】:

编辑:添加格式

正如 cmets 中已经说过的,您的数据结构有些奇怪。我更喜欢 DOM,而不是使用文本操作(如 OIS):

<?php

$nested_array = array();
$nested_array[] = array('name' => 'foo', 'depth' => 0);
$nested_array[] = array('name' => 'bar', 'depth' => 0);
$nested_array[] = array('name' => 'sub-bar', 'depth' => 1);
$nested_array[] = array('name' => 'sub-sub-bar', 'depth' => 2);
$nested_array[] = array('name' => 'sub-bar2', 'depth' => 1);
$nested_array[] = array('name' => 'sub-sub-bar3', 'depth' => 3);
$nested_array[] = array('name' => 'sub-sub3', 'depth' => 2);
$nested_array[] = array('name' => 'baz', 'depth' => 0);

$doc = new DOMDocument('1.0', 'iso-8859-1');
$doc->formatOutput = true;
$rootNode = $doc->createElement('div');
$doc->appendChild($rootNode);

$rootList = $doc->createElement('ul');
$rootNode->appendChild($rootList);

$listStack = array($rootList); // Stack of created XML list elements
$depth = 0; // Current depth

foreach ($nested_array as $nael) 
    while ($depth < $nael['depth']) 
        // New list element
        if ($listStack[$depth]->lastChild == null) 
            // More than one level at once
            $li = $doc->createElement('li');
            $listStack[$depth]->appendChild($li);
        
        $listEl = $doc->createElement('ul');
        $listStack[$depth]->lastChild->appendChild($listEl);
        array_push($listStack, $listEl);

        $depth++;
    

    while ($depth > $nael['depth']) 
        array_pop($listStack);
        $depth--;
    

    // Add the element itself
    $li = $doc->createElement('li');
    $li->appendChild($doc->createTextNode($nael['name']));
    $listStack[$depth]->appendChild($li);


echo $doc->saveXML();

您的格式约定有点奇怪。将最后一行替换为以下内容即可实现:

printEl($rootNode);

function printEl(DOMElement $el, $depth = 0) 
    $leftFiller = str_repeat("\t", $depth);
    $name = preg_replace('/[^a-zA-Z]/', '', $el->tagName);

    if ($el->childNodes->length == 0) 
        // Empty node
        echo $leftFiller . '<' . $name . "/>\n";
     else 
        echo $leftFiller . '<' . $name . ">";
        $printedNL = false;

        for ($i = 0;$i < $el->childNodes->length;$i++) 
            $c = $el->childNodes->item($i);

            if ($c instanceof DOMText) 
                echo htmlspecialchars($c->wholeText);
             elseif ($c instanceof DOMElement) 
                if (!$printedNL) 
                    $printedNL = true;
                    echo "\n";
                
                printEl($c, $depth+1);
            
        

        if (!$printedNL) 
            $printedNL = true;
            echo "\n";
        

        echo $leftFiller . '</' . $name . ">\n";
    


【讨论】:

【参考方案3】:

我有一个问题,您的问题对于评论字段来说过于详尽。

您希望如何将属性数据放入其中?你需要一个 Whory Table™ 像

array('html', null, array (
  array( 'div' , null , array( 
    array('ul', array('id'=>'foo'), array( 
      array('li', null, 'foo' ),
        array('li', null, array( 
          array(null,null, 'bar'), 
          array('ul', null, array( 
            array('li', null, 'sub-bar' )
          ))
        ))
      ))
    ))
  ))
));

因为这是以编程方式准确表示 HTML 数据集所需的最小结构。

通过假设如果

数组(名称、属性、子项)

有一个字符串而不是'children'的数组,然后它是一个隐式文本节点, 并且名称 == null 的节点没有标签,因此也是文本节点。

我认为你想要的是一个合适的程序化 DOM 生成工具,它将一些现有的 html 解析成一棵树,让你的生活更轻松

FWIW,上面的结构可以很容易的序列化成html。

function tohtml( $domtree ) 
   if( is_null($domtree[0]) ) 
     if( !is_array($domtree[2])) 
         return htmlentities($domtree[2]);
     
     die("text node cant have children!"); 
   
   $html = "<" . $domtree[0]; 
   if( !is_null( $domtree[1] ) )
   
     foreach( $domtree[1] as $name=>$value ) 
       $html .= " " . $name . '="' . htmlentities($value) . '"'; 
     
   
   $html .= ">" ; 
   if( !is_null($domtree[2]) )
     if( is_array($dometree[2]) ) 
        foreach( $domtree[2] as $id => $item ) 
          $html .= tohtml( $item ); # RECURSION
         
     
     else 
       $html .= htmlentities($domtree[2]);
     
  
  $html .= "</" . $domtree[1] . ">"; 
  return $html; 

【讨论】:

以上是关于从平面数据在 PHP 中构建分层 html 标签的主要内容,如果未能解决你的问题,请参考以下文章

从 PHP 中的平面路径数组构建目录树

通过数据库或平面文件进行本地化?

网页基础

将分层平面数据(带 ParentID)转换为带缩进级别的排序平面列表的算法

从分层数组Ruby生成平面数组的递归函数[关闭]

php 在PHP中使用平面数组构建树