0

I'm using a PHP recursive loop to parse through an XML document to create a nested list, however for some reason the loop is broken and creating duplicates of elements within the list, as well as blank elements.

The XML (a list of family tree data) is structured as follows:

        <?xml version="1.0" encoding="UTF-8"?>
<family>
<indi>
    <id>id1</id>
    <fn>Thomas</fn>
    <bday></bday>
    <dday></dday>
    <spouse></spouse>
    <family>
            <indi>
                   <id>id1</id>
                   <fn>Alexander</fn>
                   <bday></bday>
                   <dday></dday>
                   <spouse></spouse>
                  <family>
                  </family>
           </indi>
           <indi>
                   <id>id1</id>
                   <fn>John</fn>
                   <bday></bday>
                   <dday></dday>
                   <spouse></spouse>
                   <family>
                            <indi>
                                 <id>id1</id>
                                 <fn>George</fn>
                                 <bday></bday>
                                 <dday></dday>
                                 <spouse></spouse>
                                 <family>
                                 </family>
            </indi>
                   </family>
            </indi>
    </family>
</indi>
</family>

And here's my PHP loop, which loads the XML file then loops through it to create a nested ul:

<?php 
    function outputIndi($indi) {
        echo '<li>';
        $id = $indi->getElementsByTagName('id')->item(0)->nodeValue;
        echo '<span class="vcard person" id="' . $id . '">';

        $fn = $indi->getElementsByTagName('fn')->item(0)->nodeValue;
        $bday = $indi->getElementsByTagName('bday')->item(0)->nodeValue;

        echo '<span class="edit fn">' . $fn . '</span>';
        echo '<span class="edit bday">' . $bday . '</span>';
        // ...
        echo '</span>';
        echo '<ul>';
        $family = $indi->getElementsByTagName('family');
        foreach ($family as $subIndi) {
                outputIndi($subIndi);
            }
        echo '</ul></li>';
    }

    $doc = new DOMDocument();
    $doc->load('armstrong.xml');

    outputIndi($doc);

    ?>

EDIT here's the desired outcome (nested lists, with ul's signifying families and li's signifying individuals)

<ul>
  <li>
    <span class="vcard">
      <span class="fn">Thomas</span>
      <span class="bday"></span>
      <span class="dday"></span>
      <ul>
             ... repeat for all ancestors ...
      </ul>
   <li>
<ul>

You can see the output at http://chris-armstrong.com/gortin . Any ideas where I'm going wrong? I think it's something to do with the $subIndi value, but anytime I try and change it I get an error. Would really appreciate any help!

Chris Armstrong
  • 3,585
  • 12
  • 42
  • 47

3 Answers3

3

Sounds perfect! Could you give me an example? Does this mean I can save the data as XML, then load it in as nested ul's?

Yes, you can do exactly that. Here's an XSL which renders nested UL's:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
      <html>
      <body>
        <h2>Family tree</h2>
        <ul>
            <li><xsl:value-of select="indi/fn" /></li>

            <!-- apply-templates will select all the indi/family nodes -->
            <xsl:apply-templates select="indi/family" />
        </ul>
      </body>
      </html>
    </xsl:template>

    <xsl:template match="family">       
        <ul>
            <li>
                <div>
                    <xsl:value-of select="id" />: <xsl:value-of select="fn" />
                    (<xsl:variable name="bday" select="bday" />
                    to
                    <xsl:variable name="dday" select="dday" />)
                </div>
            </li>
            <!-- This node matches the 'family' nodes, and we're going to apply-templates on the inner 'family' node,
            so this is the same thing as recursion. -->
            <xsl:apply-templates select="family" />
        </ul>
    </xsl:template>
</xsl:stylesheet>

I don't know php, but this article will show you how to transform XML using the style sheet above.

You can also link your style sheet by adding a stylesheet directive at the top of your XML file (see for an example).

Juliet
  • 80,494
  • 45
  • 196
  • 228
  • 1
    +1 kudos for recognizing this as an application for XSLT, and that the OP had not considered it. Note that to get results like the OP seems to want, you need to put the xsl:apply-templates (both times) inside the `
  • ` element. (@Chris too.)
  • – LarsH Dec 13 '10 at 11:42
  • by golly, that is beautiful! One thing though, if I was wanting to take the 'id' value, and use it to create the id attribute of each li... how would I do that? ) seems to break it :/ – Chris Armstrong Dec 15 '10 at 00:51
  • @Chris Armstrong: ``. Horridly ugly language, amirite ;) – Juliet Dec 15 '10 at 04:33
  • Hah kinda, but it's still a lot more elegant than parsing! :D Thanks a bunch, just trying to work out how to enable XSLT functions in php now... – Chris Armstrong Dec 15 '10 at 14:32