test.xml:
<?xml version="1.0"?>
<props>
<prop>
<state statename="Mississippi">
<info>
<code>a1</code>
<location>Jackson</location>
</info>
<info>
<code>d2</code>
<location>Gulfport</location>
</info>
<info>
<code>g6</code>
<location>Hattiesburg</location>
</info>
</state>
<state statename="Texas">
<info>
<code>i9</code>
<location>Dallas</location>
</info>
<info>
<code>a7</code>
<location>Austin</location>
</info>
</state>
<state statename="Maryland">
<info>
<code>s5</code>
<location>Mount Laurel</location>
</info>
<info>
<code>f0</code>
<location>Baltimore</location>
</info>
<info>
<code>h4</code>
<location>Annapolis</location>
</info>
</state>
</prop>
</props>
test.php
// start the sortCities
function sortCities($a, $b){
return strcmp($a->location, $b->location);
}
// start the sortStates
function sortStates($t1, $t2) {
return strcmp($t1['statename'], $t2['statename']);
}
$props = simplexml_load_file('test.xml');
foreach ($props->prop as $prop) {
$sortedStates = array();
foreach($prop->state as $states) {
$sortedStates[] = $states;
}
usort($sortedStates, "sortStates"); // finish the sortStates
/* --- */
echo '<pre>'."\n";
print_r($sortedStates);
echo '</pre>'."\n";
/* --- */
foreach ($prop->children() as $stateattr) { // this doesn't do it
//foreach($sortedStates as $hotel => @attributes){ // blargh!
if(isset($stateattr->info)) {
$statearr = $stateattr->attributes();
echo '<optgroup label="'.$statearr['statename'].'">'."\n";
$options = array();
foreach($stateattr->info as $info) {
$options[] = $info;
}
usort($options, "sortCities"); // finish the sortCities
foreach($options as $stateattr => $info){
echo '<option value="'.$info->code.'">'.$info->location.'</option>'."\n";
}
echo '</optgroup>'."\n";
} else {
//empty nodes don't do squat
}
}
}
?>
This is the array that:
print_r($sortedStates);
prints out:
Array
(
[0] => SimpleXMLElement Object
(
[@attributes] => Array
(
[statename] => Maryland
)
[info] => Array
(
[0] => SimpleXMLElement Object
(
[code] => s5
[location] => Mount Laurel
)
[1] => SimpleXMLElement Object
(
[code] => f0
[location] => Baltimore
)
[2] => SimpleXMLElement Object
(
[code] => h4
[location] => Annapolis
)
)
)
[1] => SimpleXMLElement Object
(
[@attributes] => Array
(
[statename] => Mississippi
)
[info] => Array
(
[0] => SimpleXMLElement Object
(
[code] => a1
[location] => Jackson
)
[1] => SimpleXMLElement Object
(
[code] => d2
[location] => Gulfport
)
[2] => SimpleXMLElement Object
(
[code] => g6
[location] => Hattiesburg
)
)
)
[2] => SimpleXMLElement Object
(
[@attributes] => Array
(
[statename] => Texas
)
[info] => Array
(
[0] => SimpleXMLElement Object
(
[code] => i9
[location] => Dallas
)
[1] => SimpleXMLElement Object
(
[code] => a7
[location] => Austin
)
)
)
)
this:
// start the sortCities
function sortCities($a, $b){
return strcmp($a->location, $b->location);
}
plus this part of code:
$options = array();
foreach($stateattr->info as $info) {
$options[] = $info;
}
usort($options, "sortCities"); // finish the sortCities
foreach($options as $stateattr => $info){
echo '<option value="'.$info->code.'">'.$info->location.'</option>'."\n";
}
is doing a fine job of sorting by the 'location' node within each optgroup.
You can see that in the array I can make it sort by the attribute 'statename'. What I am having trouble with is echoing out and combining the two functions in order to have it auto sort both the states and the cities within and forming the needed optgroups.
I tried copying the lines for the cities and changing the names called several ways to no avail.
So using the XML structure above, I am trying to get it to look like:
<optgroup label="Maryland">
<option value="h4">Annapolis</option>
<option value="f0">Baltimore</option>
<option value="s5">Mount Laurel</option>
</optgroup>
<optgroup label="Mississippi">
<option value="d2">Gulfport</option>
<option value="g6">Hattiesburg</option>
<option value="a1">Jackson</option>
</optgroup>
<optgroup label="Texas">
<option value="a7">Austin</option>
<option value="i9">Dallas</option>
</optgroup>
So as to no matter what order the states are ordered in the XML and no matter how the Locations are ordered in the node within the states, they always order alphabetically upon the creating of the optgroup.
Artefacto-
the last 2 code blocks show the function for sorting the nodes by name (the location nodes).
// start the sortCities
function sortCities($a, $b){
return strcmp($a->location, $b->location);
}
// start the sortStates
function sortStates($t1, $t2) {
return strcmp($t1['statename'], $t2['statename']);
}
the second function does sort by attribute (statename) in the array but, combing the two function or rather nesting them so that the states and the cities get sorted alphabetically has got me stumped.
@Artefacto,
Thanks for the reply. Seems to make sense the way it's nested. Issue is, none of my servers run PHP 5.3. So the generic functions are tossing errors. I should have mentioned this but didn't think about it. They are running 5.2. I have been trying to revert the script back and have gotten stuck with a section.
<?php
$doc = simplexml_load_file('test.xml');
$states = get_object_vars($doc->prop->children());
$states = $states["state"];
function sortStates($t1, $t2) {
return strcmp($t1['statename'], $t2['statename']);
};
usort($states, "sortStates");
/* this is just here for testing */
echo '<pre>';
print_r($states);
echo '</pre>';
/* end testing */
/*
array_walk($states,
function (&$state) {
$state = get_object_vars($state);
array_walk($state["info"],
function (&$el) {
$el = get_object_vars($el);
}
);
usort($state["info"],
function($a, $b) { return strcmp($a["location"], $b["location"]); }
);
}
);
*/
?>
The commented out section starting with the array_walk. I can't figure out how to rewrite the 'function (&$state)' with out the next line dying.
My approach was to convert the SimpleXMLElement objects into arrays:
changing this
to this
did it.
could have sworn I tried that already.
You want to sort a list of
SimpleXMLElement
s based on some attribute or element value. You normally do this by turning the list into an array and then apply the sorting on that array.A function helpful for that in PHP is
iterator_to_array
. It does the job to turn something that is so magic in simplexml into a more "fixed" array.Let's say your tour elements in
$result->tour
. By default simplexml returns an iterator here, something you can not sort out of the box. Let's convert it into an array:Now the
$tours
variable is an array containing each<tour>
element as aSimplXMLElement
. You can now sort that array with an array function. Those array sorting functions are outlined in the PHP manual and I normally suggest How do I sort a multidimensional array in php as a good starting point:And that's already it. The whole code at a glance: