Pregunta

¿Cómo se crea un conjunto jerárquico de etiquetas con datos en PHP?

Por ejemplo, una lista anidada:

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

Esto se construiría a partir de datos planos como este:

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)

Sería bueno si también estuviera bien formateado como en el ejemplo.

¿Fue útil?

Solución

Editar: Formato agregado

Como ya se dijo en los comentarios, su estructura de datos es algo extraña. En lugar de utilizar la manipulación de texto (como OIS), prefiero DOM:

<?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();

Su convención de formato es un poco extraña. Reemplace la última línea con lo siguiente para lograrlo:

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";
    }

}

Otros consejos

Tengo una pregunta, con respecto a su pregunta, que es demasiado elaborada para un campo de comentarios.

¿Cómo desea ajustar los datos de atributos en eso? Necesitarías una mesa Whory & # 8482; me gusta

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' )
          ))
        ))
      ))
    ))
  ))
));

porque esa es la estructura mínima requerida para representar con precisión un conjunto de datos HTML mediante programación.

He hecho trampa un poco al eliminar la necesidad de "text-node" tanto elementos, suponiendo que si

matriz (nombre, atributo, hijos)

tiene una cadena en lugar de una matriz para 'hijos', entonces es un nodo de texto implícito, y que los nodos con nombre == nulo no tienen etiquetas y, por lo tanto, también son nodos de texto.

Lo que creo que quiere es una herramienta de generación de DOM programática adecuada, que analizará algunos html existentes en un árbol para facilitarle la vida

FWIW, la estructura anterior se puede serializar en html con bastante facilidad.

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; 
}

Te refieres a algo como

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";

produce

<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>

Sé que hay una pequeña brecha si una sublista no comienza con una explicación.

Personalmente, generalmente no me importa cómo se ve el HTML, siempre y cuando sea fácil trabajar con él en PHP.

Editar: OK, funciona si lo ejecutas primero ...: 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);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top