Xml child/parent help

Hi,

I have been trying to build a child/parent navigation for a little while now using PHP (I’m from a .net background) and I can’t get close to the desired results. I am loading my data from an xml file using simplexml successfully but I am trying to workout how I can map that into arrays or variables so I can write it out.

The xml is


  <Categories>
  <Category>
    <ID>1</ID>
    <Title>Days</Title>
    <Description />
    <ParentID />
    <Meta />
  </Category>
  <Category>
    <ID>2</ID>
    <Title>Monday</Title>
    <Description />
    <ParentID>1</ParentID>
    <Meta />
  </Category>
  <Category>
    <ID>3</ID>
    <Title>Tuesday</Title>
    <Description />
    <ParentID>1</ParentID>
    <Meta />
  </Category>
  <Category>
    <ID>4</ID>
    <Title>Wednesday</Title>
    <Description />
    <ParentID>1</ParentID>
    <Meta />
  </Category>
  <Category>
    <ID>5</ID>
    <Title>Thursday</Title>
    <Description />
    <ParentID>1</ParentID>
    <Meta />
  </Category>
  <Category>
    <ID>6</ID>
    <Title>Friday</Title>
    <Description />
    <ParentID>1</ParentID>
    <Meta />
  </Category>
  <Category>
    <ID>7</ID>
    <Title>Saturday</Title>
    <Description />
    <ParentID/>
    <Meta />
  </Category>
  <Category>
    <ID>8</ID>
    <Title>Sunday</Title>
    <Description />
    <ParentID/>
    <Meta />
  </Category>
</Categories>

Now my PHP


foreach($categories as $category) {

				if ($category->ParentID != "")
				{
					echo "<li><a href=index.php?Cat=$category->ID>$category->Title</a></li>";
					echo "<ul>";
					foreach($categories as $subcategory) {
					if ($subcategory->ParentID == $category->ID)
					{
						echo "<li><a href=index.php?Cat=$subcategory->ID>$subcategory->Title</a></li>";
					}
					}
					echo "</ul>";
				}
				else
				{
					echo "<li><a href=index.php?Cat=$category->ID>$category->Title</a></li>";
				} 
			} 
			echo "</ul>";

So my desired output would be something like this:


<ul id="p7menubar">
<li><a class="trigger" href="#">Days</a>
<ul>
<li><a href="#">Monday</a></li>
<li><a href="#">Tuesday</a></li>
<li><a href="#">Wednesday</a></li>
<li><a href="#">Thursday</a></li>
<li><a href="#">Friday</a></li>
</ul>
</li>
<li><a href="index.htm">Saturday</a></li>
<li><a href="index.htm">Sunday</a></li>
</ul>

If anyone could help me out it would be very much appreciated :slight_smile:

Thanks

Al

Thank you bcafs7!

I can’t thank you enough that works perfectly :smiley:

If you run print_r on the SimpleXML object, you’d get something like this:

SimpleXMLElement Object
(
    [Category] => Array
        (
            [0] => SimpleXMLElement Object
                (
                    [ID] => 1
                    [Title] => Days
                    [Description] => SimpleXMLElement Object
                        (
                        )

                    [ParentID] => SimpleXMLElement Object
                        (
                        )

                    [Meta] => SimpleXMLElement Object
                        (
                        )

                )

            [1] => SimpleXMLElement Object
                (
                    [ID] => 2
                    [Title] => Monday
                    [Description] => SimpleXMLElement Object
                        (
                        )

                    [ParentID] => 1
                    [Meta] => SimpleXMLElement Object
                        (
                        )

                )

            [2] => SimpleXMLElement Object
                (
                    [ID] => 3
                    [Title] => Tuesday
                    [Description] => SimpleXMLElement Object
                        (
                        )

                    [ParentID] => 1
                    [Meta] => SimpleXMLElement Object
                        (
                        )

                )
...

What you’re doing is trying to find the parent categories by testing to see if $category->ParentID does not equal “” (a blank string). But the problem is in the parent categories, $category->ParentID does equal “”. So if you change it to if ($category->ParentID == “”), then it would work.

Another problem with your code is that you don’t test to see if a parent category has children before you echo out the <ul> tags. Also, looping through the same array with foreach so many times is inefficient. A much cleaner solution would be to first organize the the XML into parents and children:

$parents = array();
$children = array();

foreach ($categories as $category) {
    // this is a parent category
    if ($category->ParentID == "") {
        // without the (int) and (string) typecasts, $category->ID and $category->Title would be
        // SimpleXMLElement Objects instead of ints and strings, like we want them to be
        $parents[(int) $category->ID] = (string) $category->Title;
    // and this is a child category
    } else {
        $children[(int) $category->ParentID][(int) $category->ID] = (string) $category->Title;
    }
}

/* Based on the sample XML you provided, $parents would look like this:
Array
(
    [1] => Days
    [7] => Saturday
    [8] => Sunday
)

and $children would look like this:
Array
(
    [1] => Array
        (
            [2] => Monday
            [3] => Tuesday
            [4] => Wednesday
            [5] => Thursday
            [6] => Friday
        )

)
*/

and then once you have the XML neatly sorted like that, it’d be very easy to create the list that you wanted:

// now we loop through all the parents
foreach ($parents as $id => $title) {
    // output them
    echo "<li><a href=index.php?Cat=$id>$title</a></li>";

    // and if they have any children, then create a list for all the children
    if (isset($children[$id])) {
        echo "<ul>";

        foreach($children[$id] as $subID => $subTitle) {
            echo "<li><a href=index.php?Cat=$subID>$subTitle</a></li>";
        }

        echo "</ul>";
    }
}