Michael
Michael

Reputation: 159

Problems with data alignment in XSLT with nested tables

I'm generating a Word document via FoundationPHP using XSLT and nested tables.

My desired results would be:

Table 1        Table 2
Image 1        Image 2
Label 1        Label 2
Data 1         Data 2

Table 3        Table 4
Image 3        Image 4
Label 3        Label 4
Data 3         Data 4

Table 5
Image 5
Label 5
Data 5

However, what I am getting is:

Table 1        Table 2
Image 1        Image 2
Label 1        Label 2
Label 1        Label 4

Table 3        Table 4
Image 3        Image 4
Label 3        Label 4
Data 2        Label 5

Table 5
Image 5
Label 5
Data 3

My XSLT code (simplified for posting purposes)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:w="http://some.bla">      <!-- Namespace of 'w' added by edit -->
    <xsl:template match="/">
        <w:document>             <!-- Added by edit !!! -->
            blah
            <w:body>
                <w:tbl>
                    <xsl:variable name="size" select="count(root/row)"/>

                    <xsl:for-each name="count" select="root/row[ceiling($size div 2) &gt;= position()]">
                        <xsl:variable name="level0Count" select="position()"/>
                        <xsl:variable name="imageid" select="position()+1"/>
                        <xsl:variable name="level1Count" select="position() - 1"/>
                        <xsl:variable name="level2Count" select="$level1Count * 2 + position()"/>
                        <w:tr w:rsidR="003346C6" w:rsidTr="003346C6">
                            <w:tc>
                                <w:tcPr>
                                    <w:tcW w:w="4648" w:type="dxa"/>
                                </w:tcPr>
                                <w:tbl>
                                    <w:tr w:rsidR="003346C6" w:rsidTr="00BD1383">
                                        <w:tc>
                                            <w:p w:rsidR="003346C6" w:rsidRDefault="003346C6" w:rsidP="003346C6">
                                                <w:r>
                                                    Image 1
                                                </w:r>
                                            </w:p>
                                        </w:tc>
                                    </w:tr>
                                    <w:tr w:rsidR="003346C6" w:rsidTr="00BD1383">
                                        <w:tc>
                                            <w:p w:rsidR="003346C6" w:rsidRPr="0004412A" w:rsidRDefault="003346C6" w:rsidP="003346C6">
                                                <w:r w:rsidRPr="0004412A">
                                                    <xsl:choose>
                                                        <xsl:when test="position()=1">
                                                            <w:t>Label <xsl:value-of select="position()"/>
                                                                <w:b />
                                                            </w:t>
                                                        </xsl:when>
                                                        <xsl:otherwise>
                                                            <w:t>Label <xsl:value-of select="2*position()-1"/>
                                                                <w:b />
                                                            </w:t>
                                                        </xsl:otherwise>
                                                    </xsl:choose>
                                                </w:r>
                                            </w:p>
                                            <w:p w:rsidR="003346C6" w:rsidRPr="005F3377" w:rsidRDefault="003346C6" w:rsidP="003346C6">
                                                <w:r>
                                                    <w:t>
                                                        <xsl:value-of select="Data"/>
                                                    </w:t>
                                                </w:r>
                                                <w:bookmarkStart w:id="0" w:name="_GoBack"/>
                                                <w:bookmarkEnd w:id="0"/>
                                            </w:p>
                                        </w:tc>
                                    </w:tr>
                                </w:tbl>
                                <w:p w:rsidR="003346C6" w:rsidRDefault="003346C6" w:rsidP="003346C6"/>
                            </w:tc>
                            <xsl:if test="following::row[ceiling($size div 2)]/data!=''">
                                <w:tc>
                                    <w:tcPr>
                                        <w:tcW w:w="4648" w:type="dxa"/>
                                    </w:tcPr>
                                    <w:tbl>
                                        <w:tr w:rsidR="003346C6" w:rsidTr="00BD1383">
                                            <w:tc>
                                                <w:p w:rsidR="003346C6" w:rsidRDefault="003346C6" w:rsidP="003346C6">
                                                    <w:r>
                                                        Image 2
                                                    </w:r>
                                                </w:p>
                                            </w:tc>
                                        </w:tr>
                                        <w:tr w:rsidR="003346C6" w:rsidTr="00BD1383">
                                            <w:tc>
                                                <w:p w:rsidR="003346C6" w:rsidRDefault="003346C6" w:rsidP="003346C6">
                                                    <w:r>
                                                        <xsl:choose>
                                                            <xsl:when test="position()=1">
                                                                <w:t>
                                                                    Label <xsl:value-of select="position()+1"/>
                                                                </w:t>
                                                            </xsl:when>
                                                            <xsl:otherwise>
                                                                <xsl:if test="following::row[ceiling($size div 2)]/data!=''">
                                                                    <w:t>
                                                                        Label <xsl:value-of select="2*position()"/>
                                                                    </w:t>
                                                                </xsl:if>
                                                            </xsl:otherwise>
                                                        </xsl:choose>
                                                    </w:r>
                                                </w:p>
                                                <w:p w:rsidR="003346C6" w:rsidRPr="005F3377" w:rsidRDefault="003346C6" w:rsidP="003346C6">
                                                    <w:r>
                                                        <w:t>
                                                            <xsl:value-of select="following::row[ceiling($size div 2)]/data"/>
                                                        </w:t>
                                                    </w:r>
                                                </w:p>
                                            </w:tc>
                                        </w:tr>
                                    </w:tbl>
                                    <w:p w:rsidR="003346C6" w:rsidRDefault="003346C6" w:rsidP="003346C6"/>
                                </w:tc>

                            </xsl:if>
                        </w:tr>
                    </xsl:for-each>
                </w:tbl>
                <w:p w:rsidR="00A00BC9" w:rsidRDefault="00A00BC9"/>
            </w:body>
        </w:document>
    </xsl:template>
</xsl:stylesheet>

As you can see, the image and the label are correct, however the data is coming in vertically. I've been staring at this code to the point where my double vision is making me second guess everything.

EDIT: This is the XML output from my script. (I only included 3 rows)

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <row>
        <id>10755</id>
        <image>10755/11786.jpg</image>
        <data> 8220 Southwest Warm Springs Street</data>
    </row>
    <row>
        <id>10493</id>
        <image>10493/11786.jpg</image>
        <data> 21101-21149 SW 115th Avenue</data>
    </row>
    <row>
        <id>11008</id>
        <image>11008/11786.jpg</image>
        <data> 7144 NW Progress Court</data>
    </row>
</root>

To make the question more complete, here is the code to get the data:

if ( isset( $_GET[ 'id' ] ) ) {
    $sql = "SELECT ID, data, image FROM property WHERE ID in (" . $_GET['id'] .") ORDER BY field(ID," . $_GET['id'] .")";
    $result = $db->query($sql);
    if ($db->error) {
        $error = $db->error;
    }
}
}

function getRow($result) {
    return $result->fetch_assoc();
}

Upvotes: 2

Views: 329

Answers (2)

MAM
MAM

Reputation: 56

In your first call to data, change to

<xsl:choose>
  <xsl:when test="position()=1">
    <w:t><xsl:value-of select="data"/></w:t>
  </xsl:when>
  <xsl:otherwise>
    <w:t><xsl:value-of select="following::row[$imageid - 2]/data"/></w:t>
  </xsl:otherwise>
</xsl:choose>

Then, on your second call, use

    <w:t><xsl:value-of select="following::row[$imageid - 1]/data"/></w:t>

This should line up the data the way you want.

Upvotes: 1

Martin Honnen
Martin Honnen

Reputation: 167716

If the aim is to map the sequence of row elements (i.e. row 1, 2, 3, 4, 5) to a two column table where each table cell contains the data of a a row and the layout is

<table>
  <row><cell>1</cell><cell>2</cell></row>
  <row><cell>3</cell><cell>4</cell></row>
  <row><cell>5</cell></row>
</table>

then I think that is easy by processing the odd rows to map them to a table row and then build the two cells in each row by processing that odd row plus its immediately following sibling and then if needed map each row to a nested table presenting the data of an input row element.

Here is an example using HTML tables as the target format:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exsl="http://exslt.org/common"
    xmlns:msxml="urn:schemas-microsoft-com:xslt"
    exclude-result-prefixes="exsl msxml"
    version="1.0">

  <xsl:output method="html" indent="yes" version="5" doctype-system="about:legacy-doctype"/>


  <xsl:template match="/">
    <html>
      <head>
        <title>.NET XSLT Fiddle Example</title>
      </head>
      <body>
          <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="root">
      <table>
          <thead>
              <tr>
                  <th>col 1</th>
                  <th>col 2</th>
              </tr>
          </thead>
          <tbody>
              <xsl:apply-templates select="row[position() mod 2 = 1]" mode="row"/>
          </tbody>
      </table>
  </xsl:template>

  <xsl:template match="row" mode="row">
      <tr>
          <xsl:apply-templates select=". | following-sibling::row[1]" mode="cell"/>
      </tr>
  </xsl:template>

  <xsl:template match="row" mode="cell">
      <td>
          <xsl:apply-templates select="." mode="table"/>
      </td>
  </xsl:template>

  <xsl:template match="row" mode="table">
      <table>
          <thead>
              <tr>
                  <th>Table <xsl:number/></th>
                  <xsl:apply-templates select="*" mode="th"/>
              </tr>
          </thead>
          <tbody>
              <tr>
                  <td><xsl:number/></td>
                  <xsl:apply-templates select="*"/>
              </tr>
          </tbody>
      </table>
  </xsl:template>

  <xsl:template match="row/*" mode="th">
      <th>
          <xsl:value-of select="local-name()"/>
      </th>
  </xsl:template>

  <xsl:template match="row/*">
      <td>
          <xsl:value-of select="."/>
      </td>
  </xsl:template>

  <xsl:template match="row/image">
      <td>
          Image <xsl:number count="row"/> <img src="{.}" alt="image"/>
      </td>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/6qVRKwi

Of course the result elements need to be adjusted to your target markup language but the algorithm should be the same, the only problem left is to make sure an empty cell is added to the last table row in case there is an odd number of input row elements and the target layout does not work or looks awful with missing cell. This is easily done by changing one template to

  <xsl:template match="row" mode="row">
      <tr>
          <xsl:apply-templates select=". | following-sibling::row[1]" mode="cell"/>
          <xsl:if test="not(following-sibling::row)">
              <td></td>
          </xsl:if>
      </tr>
  </xsl:template>

as done in https://xsltfiddle.liberty-development.net/6qVRKwi/1

Upvotes: 1

Related Questions