Crea tag html gerarchici in PHP da dati semplici
Domanda
Come si crea un set gerarchico di tag con dati in PHP?
Ad esempio, un elenco nidificato:
<div>
<ul>
<li>foo
</li>
<li>bar
<ul>
<li>sub-bar
</li>
</ul>
</li>
</ul>
</div>
Questo sarebbe costruito da dati semplici come questo:
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)
Sarebbe bello se fosse ben formattato come nell'esempio.
Soluzione
Modifica: aggiunta formattazione
Come già detto nei commenti, la struttura dei dati è alquanto strana. Invece di usare la manipolazione del testo (come OIS), preferisco 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();
La tua convenzione di formattazione è piuttosto strana. Sostituisci l'ultima riga con la seguente per raggiungerla:
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";
}
}
Altri suggerimenti
Ho una domanda riguardo la tua domanda troppo elaborata per un campo di commento.
Come vuoi adattare i dati degli attributi in questo? Avresti bisogno di un tavolo Whory & # 8482; come
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' )
))
))
))
))
))
));
perché questa è la struttura minima richiesta per rappresentare accuratamente un set di dati HTML a livello di codice.
Ho tradito un po 'eliminando la necessità di " text-node " elementi allo stesso modo, assumendo se
array (nome, attributo, figli)
ha una stringa anziché un array per "figli", quindi è un nodo di testo implicito, e che i nodi con nome == null non hanno tag e sono quindi anche nodi di testo.
Quello che penso tu voglia è un adeguato strumento di generazione DOM programmatica, che analizzerà alcuni HTML esistenti in un albero per semplificarti la vita
FWIW, la struttura di cui sopra può essere serializzata in HTML piuttosto facilmente.
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;
}
Intendi qualcosa come
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>
So che c'è un piccolo divario se un sottoelenco non inizia con una spiegazione.
Personalmente di solito non mi interessa davvero l'aspetto dell'HTML purché sia ??facile da lavorare in PHP.
Modifica: OK, funziona se lo esegui per primo ...: 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);