There is a lot of logic here but it will do exactly what you want for the test data sets provided. The below merges two (though it could be any number) separate data sets, sorts them and even throws out repeats.
So first I’m going to explain the data sets I have created.
I have created two data sets for music albums. Each one differs in structure:
/datasets/albums.xml
<?xml version="1.0"?>
<albums>
<album>
<name>7th Symphony</name>
<artist>Apocalyptica</artist>
</album>
<album>
<name>Blindside</name>
<artist>The Black Rose EP</artist>
</album>
</albums>
/datasets/music.xml
<?xml version="1.0"?>
<music>
<cd>
<title>7th Symphony</title>
<band>Apocalyptica</band>
</cd>
<cd>
<title>Avenged Sevenfold</title>
<band>Avenged Sevenfold</band>
</cd>
<cd>
<title>Disturbed</title>
<band>Asylum</band>
</cd>
</music>
Each data set has a xsl file for normalizing its format. This is so that when the two are merged into a single document they have the same structure.
/transforms/albums.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<rows>
<xsl:for-each select="/albums/album">
<row>
<album_title><xsl:value-of select="name"/></album_title>
<album_artist><xsl:value-of select="artist"/></album_artist>
</row>
</xsl:for-each>
</rows>
</xsl:template>
</xsl:stylesheet>
/transforms/music.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<rows>
<xsl:for-each select="/music/cd">
<row>
<album_title><xsl:value-of select="title"/></album_title>
<album_artist><xsl:value-of select="band"/></album_artist>
</row>
</xsl:for-each>
</rows>
</xsl:template>
</xsl:stylesheet>
The last xsl file is the one to be applied to the merged data that sorts it once it has been combined. I also added a a filter for repeats. This is so that given both data sets have the same album it only appears once in the result set ( you’ll see my comments for that).
/final.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:key name="parsed-rows" match="row" use="concat(album_title,'-',album_artist)"/>
<xsl:param name="global_sort_col" select="'artist'"/>
<xsl:param name="global_sort" select="desc"/>
<xsl:template match="/">
<rows>
<xsl:choose>
<xsl:when test="$global_sort = 'asc' and $global_sort_col = 'title'">
<xsl:apply-templates select="/root/rows/row">
<xsl:sort select="album_title" order="ascending"/>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$global_sort = 'desc' and $global_sort_col = 'title'">
<xsl:apply-templates select="/root/rows/row">
<xsl:sort select="album_title" order="descending"/>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$global_sort = 'asc' and $global_sort_col = 'artist'">
<xsl:apply-templates select="/root/rows/row">
<xsl:sort select="album_artist" order="ascending"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="/root/rows/row">
<xsl:sort select="album_artist" order="descending"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</rows>
</xsl:template>
<xsl:template match="row">
<xsl:comment>
Throw out repeats based on artist name and album title combinations
</xsl:comment>
<xsl:if test="generate-id() = generate-id(key('parsed-rows',concat(album_title,'-',album_artist)))">
<row>
<album_title><xsl:value-of select="album_title"/></album_title>
<album_artist><xsl:value-of select="album_artist"/></album_artist>
</row>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Lastly, there is the PHP file that handles all the logic. You can go ahead and read the comments to get a better idea of what is being done. However, in a nutshell both data sets are normalized, added to a new xml document than the final sorting transformation is applied. This results in the final output. The script may also feed the sort and sort column to the final transformation, as this is dynamic using parameters.
/merge.php
<?php
/*
* Load XML data sets
*/
$albums = new DOMDocument();
$music = new DOMDocument();
$albums->load('datasets/albums.xml');
$music->load('datasets/music.xml');
/*
* Load XSL normalzation transformations
*/
$albums_xsl = new DOMDocument();
$music_xsl = new DOMDocument();
$albums_xsl->load('transforms/albums.xsl');
$music_xsl->load('transforms/music.xsl');
/*
* Normalize data sets
*/
$albums_processor = new XSLTProcessor();
$music_processor = new XSLTProcessor();
$albums_processor->importStyleSheet($albums_xsl);
$music_processor->importStyleSheet($music_xsl);
$albums_normalized = $albums_processor->transformToDoc($albums);
$music_normalized = $music_processor->transformToDoc($music);
/*
* Merge into new document
*/
$merged = new DOMDocument();
$wrap = $merged->createElement('root');
$merged->appendChild($wrap);
/*
* Add normalized documents as children of wrapper
*/
$wrap->appendChild($merged->importNode($albums_normalized->documentElement,true));
$wrap->appendChild($merged->importNode($music_normalized->documentElement,true));
/*
* Load final stylesheet transform
*/
$final = new DOMDocument();
$final->load('final.xsl');
/*
* Apply XXL sorting translation for final output
*/
$final_processor = new XSLTProcessor();
$final_processor->importStyleSheet($final);
// pass sort column and order
$final_processor->setParameter('','global_sort_col','title');
$final_processor->setParameter('','global_sort','desc');
// apply transform and return dom document
$sorted = $final_processor->transformToDoc($merged);
$sorted->formatOutput = true;
$sorted->preserveWhiteSpace = true;
/*
* Show final XML although you could do anything at this point
*/
header('content-type: text/xml');
echo $sorted->saveXML();
?>
If you create the same file structure as shown you will see the merge in action. However, the result with the supplied files and sorting parameters is shown below.
- Take note of even though Apocalyptica:7th symphony exists in both data sets there is only one in the result set due to the filter conditional within /final.xsl.
<?xml version="1.0"?>
<rows xmlns:fn="http://www.w3.org/2005/xpath-functions">
<row>
<album_title>Disturbed</album_title>
<album_artist>Asylum</album_artist>
</row>
<row>
<album_title>Blindside</album_title>
<album_artist>The Black Rose EP</album_artist>
</row>
<row>
<album_title>Avenged Sevenfold</album_title>
<album_artist>Avenged Sevenfold</album_artist>
</row>
<row>
<album_title>7th Symphony</album_title>
<album_artist>Apocalyptica</album_artist>
</row>
</rows>
Its probably a lot to take in, but this seems like the best approach. At least in my eyes.
The below is the file structure I was working with for these files. So if you mimic this you will be able to see it in action on your own environment and play around with it.
File Structure (root relative)
- merge.php
- final.xsl
- datasets
[list=*]
- albums.xml
- music.xml
[/list]
- transforms
[list=*]
- albums.xsl
- music.xsl
[/list]
Hope that helps or at least gives you an idea of how to implement it for your situation.