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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <ul id="menu">
<li class="first"><a href="/news/q/date/2011/">2011</a></li>
<li class="current last">
<a href="/news/q/date/2010/">2010</a>
<ul>
<li class=""><a href="/news/q/date/2010/07/">July</a></li>
<li class=""><a href="/news/q/date/2010/06/">June</a></li>
<li class=""><a href="/news/q/date/2010/11/">November</a></li>
<li class=" last"><a href="/news/q/date/2010/10/">October</a></li>
<li class=""><a href="/news/q/date/2010/09/">September</a></li>
<li class=""><a href="/news/q/date/2010/08/">August</a></li>
<li class="first"><a href="/news/q/date/2010/12/">December</a></li>
<li class=""><a href="/news/q/date/2010/05/">May</a></li>
</ul>
</li>
</ul> |
My (woeful) solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | $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 <ul>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 <a> 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> $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 <ul> 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 <ul> 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; } |