views:

237

answers:

3

I'm trying to make a page using data from the discogs.com (XML)-API. i've been parsing it with simpleXML and it's working pretty well but there is some things i'm not sure how to do.

Here is part of the XML:

<releases>
  <release id="1468764" status="Accepted" type="Main">
    <title>Versions</title>
    <format>12", EP</format>
    <label>Not On Label</label>
    <year>1999</year>
  </release>
  <release id="72246" status="Accepted" type="Main">
    <title>The M.O.F Blend</title>
    <format>LP</format>
    <label>Blenda Records</label>
    <year>2002</year>
  </release>
  <release id="890064" status="Accepted" type="Main">
    <title>The M.O.F Blend</title>
    <format>CD</format>
    <label>Blenda Records</label>
    <year>2002</year>
  </release>
  <release id="1563561" status="Accepted" type="TrackAppearance">
    <title>Ännu En Gång Vol. 3</title>
    <trackinfo>Backtrack</trackinfo>
    <format>Cass, Comp, Mix</format>
    <label>Hemmalaget</label>
    <year>2001</year>
  </release>
</releases>

What i want to achieve is something similair to how discogs presents the releases: http://www.discogs.com/artist/Mics+Of+Fury where diferent versions of the same release are sorted together. (see. The M.O.F Blend in my link) This is done on discogs with having a master release that features the other releases. unfortunately this information isn't present in the API data, so i want to do the same thing by grouping the <release>-nodes with the same <title>-tags, or add a flag to the <releases> that don't have a unique <title>? any good ideas on the best way of doing this?

i also like to know if it's possible to count the <release>-nodes (child of releases) that have the same type-attribute? like in this example count the releases with the type "Main"?

maybe it's better to do this things with XMLReader or XPath?

A: 

I have recently built a class I use for creating and sorting HTML tables of objects, here are a some static methods from it you may find useful for sorting associative arrays. I have removed the self:: reference so you can just use the methods as functions.

Usage: $array = array_sort($array, 'sort_key_name');

function array_sort(&$array)
    {
        if(!$array) return $keys;
        $keys = func_get_args();
        array_shift($keys);
        array_sort_func($keys);
        usort($array,array("listview","array_sort_func"));
        return $array;
    }


function array_sort_func($a, $b = NULL)
    {
        static $keys;
        if($b === NULL) return $keys = $a;

        foreach($keys as $k)
        {
            $aval = hod($a, '$a->' . $k);
            $bval = hod($b, '$b->' . $k);

            // modify string to compate
            if(!is_numeric($aval)){$aval = strtolower($aval);}
            if(!is_numeric($bval)){$bval = strtolower($bval);}

            if($k[0]=='!')
            {
                $k=substr($k,1);

                if($aval!== $bval)
                {
                    if(is_numeric($aval) and is_numeric($bval))
                    {
                        return $aval - $bval;
                    }
                    else
                    {
                        return strcmp($bval, $aval);
                    }
                }
            }
            else if($aval !== $bval)
            {
                if(is_numeric($aval) and is_numeric($bval))
                {
                    $compare = $aval - $bval;

                    if($compare > 0)
                    {
                        return 1;
                    }
                    elseif($comare < 0)
                    {
                        return -1;
                    }
                }
                else
                {
                    return strcmp($aval, $bval);
                }
            }
        }

        return 0;
    }

function hod(&$base, $path)
    {
        $licz = '';
        $keys = explode("->", $path);
        $keys[0] = str_replace('$', '', $keys[0]);
        $expression = '$ret = ';
        $expression.= '$';

        foreach ($keys as $key)
        {
            if (++$licz == 1)
            {
                $expression.= 'base->';
            }
            else
            {
                $expression.= $key.'->';
            }
        }

        $expression = substr($expression, 0, -2);
        $expression.= ';';
        eval($expression);
        return $ret;
    }
Giles Smith
+1  A: 

You can use xsl(t) and php's XSLTProcessor.
In xslt 2.0 you could use something like

<xsl:for-each-group select="release" group-by="title">

Unfortunately libxslt doesn't support this (at least the version used in the php.net win32 build of php 5.3.2 doesn't).
But you can use the Muenchian grouping method.

test.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
<xsl:output method="html" version="4.0" encoding="utf-8" indent="yes"/>

<xsl:key name="release-by-title" match="release" use="title" />

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

<xsl:template match="releases">
  <table border="1">
    <xsl:for-each select="release[count(. | key('release-by-title', title)[1]) = 1]">
      <xsl:sort select="title" />
      <tr>
        <th colspan="3"><xsl:value-of select="title" /></th>
      </tr>
      <xsl:for-each select="key('release-by-title', title)">
        <xsl:sort select="year" />
        <tr>
          <td><xsl:value-of select="year" /></td>
          <td><xsl:value-of select="label" /></td>
          <td><xsl:value-of select="format" /></td>
        </tr>
      </xsl:for-each>
    </xsl:for-each>
  </table>
</xsl:template>

</xsl:stylesheet>

test.xml containing the xml document you provided.
And test.php:

<?php
$doc = new DOMDocument;
$doc->load('test.xsl');
$stylesheet = new XSLTProcessor;
$stylesheet->importStyleSheet($doc);
$doc->load('test.xml');

header('Content-type: text/html; charset=utf-8');
echo $stylesheet->transformToXML($doc);

And the output (onlye the <table>-part) is:

<table border="1">
  <tr><th colspan="3">The M.O.F Blend</th></tr>
  <tr>
    <td>2002</td>
    <td>Blenda Records</td>
    <td>LP</td>
  </tr>
  <tr>
    <td>2002</td>
    <td>Blenda Records</td>
    <td>CD</td>
  </tr>
  <tr><th colspan="3">Versions</th></tr>
  <tr>
    <td>1999</td>
    <td>Not On Label</td>
    <td>12", EP</td>
  </tr>
  <tr><th colspan="3">Ännu En Gång Vol. 3</th></tr>
  <tr>
    <td>2001</td>
    <td>Hemmalaget</td>
    <td>Cass, Comp, Mix</td>
  </tr>
</table>

Now, that's not much of an explaination. But maybe it gives you some hints to what to search for.

VolkerK
A: 

Though I myself am pretty new to php and xslt, even I have a similar problem, but I guess that xslt 2.0 may be able help you, here's the documentation for applying grouping and sorting on xml data using xsl 2.0:

P.S: I'm new to php means I've just started yesterday, and primarily I develop web apps using Asp.Net, so I'm still trying to implement this in the new php module I've been handed over.

Anchit