2

I do have following XML-data:

 <schedule>
  <event>
    <date>2017-08-25</date>
    <time>16:00</time>
  </event>
  <event>
    <date>2017-08-25</date>
    <time>20:00</time>
  </event>
  <event>
    <date>2017-08-26</date>
    <time>16:00</time>
  </event>
    <event>
    <date>2017-08-26</date>
    <time>20:00</time>
  </event>
    </event>
    <event>
    <date>2017-08-27</date>
    <time>20:00</time>
  </event>
</schedule>

To output the data I do using following loop:

<?php $xml = simplexml_load_file('events.xml'); ?>
<?php foreach ( $xml->event as $event ) { ?>
<dl>
    <dt>
        <?php echo $event->date; ?>
    </dt>
    <dd>
        <?php echo $event->time; ?>
    </dd>
</dl>
<?php } ?>

The result is:

<dl>
    <dt>2017-08-25</dt>
    <dd>16:00</dd>
</dl>
<dl>
    <dt>2017-08-25</dt>
    <dd>20:00</dd>
</dl>
<dl>
    <dt>2017-08-26</dt>
    <dd>16:00</dd>
</dl>

etc.

But know I want to order the time by date, to get this output:

<dl>
    <dt>2017-08-25</dt>
    <dd>16:00</dd>
    <dd>20:00</dd>
</dl>

<dl>
    <dt>2017-08-26</dt>
    <dd>16:00</dd>
    <dd>20:00</dd>
</dl>

etc.

Is there a way to modify the loop, to get this result? I would be very thankfull for any help!

Filip
  • 77
  • 2
  • 11

1 Answers1

2

Consider XSLT, the special purpose language designed to transform XML files, because what you actually need is an XSLT 1.0 method called the Muenchian Grouping where you group by nodes using a document key and retrieve or manipulate all corresponding values.

PHP can run XSLT 1.0 scripts using its php-xsl extension (be sure to enable in .ini file). With this approach, no foreach loop or if logic needed.

XSLT Script (save as .xsl file, a special .xml file that can be handled like any XML)

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*"/>

<xsl:key name="eventid" match="event" use="date" />

  <xsl:template match="/schedule">
    <root>
       <xsl:apply-templates select="event"/>
    </root>
  </xsl:template>

  <xsl:template match="event">
    <dl>      
      <xsl:for-each select=".[generate-id() = generate-id(key('eventid', date)[1])]">
            <dt><xsl:value-of select="date"/></dt>
            <xsl:for-each select="key('eventid', date)">
                <dd><xsl:value-of select="time"/></dd>
            </xsl:for-each>
      </xsl:for-each>
    </dl>    
  </xsl:template>

</xsl:transform>

PHP (referencing above .xsl file)

// LOAD XML AND XSL
$xml = new DOMDocument();
$xml->load('Input.xml');       // OR $xml->loadXML($xmlstring);

$xsl = new DOMDocument;
$xsl->load('XSLTScript.xsl');  // OR $xsl->loadXML($xslstring);

// INITIALIZE TRANSFORMER
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl); 

// RUN TRANSFORMATION
$newXML = $proc->transformToXML($xml);

// ECHO STRING OUTPUT
echo $newXML;

// SAVE OUTPUT TO FILE
file_put_contents('Output.xml', $newXML);

Output (online transform)

<root>
   <dl>
      <dt>2017-08-25</dt>
      <dd>16:00</dd>
      <dd>20:00</dd>
   </dl>
   <dl/>
   <dl>
      <dt>2017-08-26</dt>
      <dd>16:00</dd>
      <dd>20:00</dd>
   </dl>
   <dl/>
   <dl>
      <dt>2017-08-27</dt>
      <dd>20:00</dd>
   </dl>
</root>
Parfait
  • 104,375
  • 17
  • 94
  • 125
  • Thank you for your detailed steps to realize! I do like the way to use XSLT. Sadly I forgot to mention, that I have no influence to modify the XML file. The file is based as API key on external servers. – Filip Aug 29 '17 at 17:40
  • You are not modifying the original XML but transforming original and saving to a new file. And if your output is just to screen, `echo $newXML;` should do. – Parfait Aug 29 '17 at 17:42