Recently I was working on website where I had to re-order a nested list (part of a navigation menu) – unfortunately I only had access to fragment of HTML so I couldn’t just manipulate the arrays from which it was built. The menu was compiled from various arrays and months within years sometimes came out all wrong.
So I thought, I’d just treat it as a bit of XML (which obviously it is) and re-order it using PHPs native XML handling classes. XML is one of those things that I use frequently, but never really do anything with, and finding a solution took me rather longer than I had expected. It is a mixture of simpleXML and XMLDom.
One of the main problems was the lack any real examples.
If anyone can suggest a more elegant solution, I would love to hear it.
Here is my code:
The UL to re-order
As you can see the month names are in the wrong order
My (woeful) solution
$xml = simplexml_load_string($string);
// pull a node tree as an Array out using simpleXML xpath
$trees = $xml->xpath('/ul/li/ul');
$array = array();
$order = array();
$i = 0;
// we only need to delve into XML if there are any nested
- s
if(isset($trees[0])){
foreach($trees[0] as $var){
// store each node in an indexed array
$array[$i] = $var;
// store the month number in an index array
// based on the text node value of the tag
$order[$i] = date('m', strtotime((string) $var->a));
$i++;
}
// sort the month number array descending, but maintaining the keys
arsort($order);
// create a new XML Dom object to manipulate stuff
$dom = new DomDocument();
// create a holder node
-
$ul = $dom->createElement('ul');
// iterate through the array of simpleXML objects
// based on the order in which their keys appear in the re-ordered array
foreach($order as $key => $value){
// get the simpleXML objects into a string
$node = dom_import_simplexml($array[$key]);
// get the string into an actual DOM node
$node = $dom->importNode($node, true);
// append
$ul->appendChild($node);
}
//$dom->appendChild($ul);
// unset the contents of the original
- node that we have resorted
$parent = $trees[0]->xpath( 'parent::*' );
$parent[0]->ul = NULL;
// turn our simpleXML object into a DOM object
$ixml = dom_import_simplexml($xml);
$new = new DOMDocument('1.0');
$ixml = $new->importNode($ixml, true);
$ixml = $new->appendChild($ixml);
// fire up a DOM xpath object
$xpath = new DomXpath($new);
// pull a node tree out using simpleXML xpath
$tree = $xpath->query('/ul/li/ul');
// add our newly created DOM node conatining the re-ordered
- after the existing node
$tree->item(0)->parentNode->appendChild($new->importNode($ul, true));
// delete the original empty node
$tree->item(0)->parentNode->removeChild($tree->item(0));
echo $new->saveHTML();
} else {
echo $string;
}