I would like merge two feeds the one has all product data and has an product identifier ITEM_ID in every , the second XML feed has same value as ITEM_ID in <item id="">
and inside this <item>
has stock_quantity tag but I can't figure it out how to merge these values.. The three dots in XML content means that there are more item tags
The first feed (items.xml) looks like:
<SHOP>
<SHOPITEM>
<DESCRIPTION>
<![CDATA[ <p><span>Just an description. </span></p> ]]>
</DESCRIPTION>
<URL>https://www.korkmaz.cz/tombik-cajova-konvice-2l/</URL>
<IMGURL>https://cdn.myshoptet.com/usr/www.korkmaz.cz/user/shop/orig/52_konvice-tombik-1l.jpg?5f4fcd7d</IMGURL>
<IMGURL_ALTERNATIVE>https://cdn.myshoptet.com/usr/www.korkmaz.cz/user/shop/orig/52-1_bez-trouby.jpg?5f4fcd7d</IMGURL_ALTERNATIVE>
<PURCHASE_PRICE>487,99</PURCHASE_PRICE>
<PRICE_VAT>797,00</PRICE_VAT>
<VAT>21%</VAT>
<CATEGORYTEXT>KUCHYŇSKÉ DOPLŇKY | Příprava čaje a kávy</CATEGORYTEXT>
<DELIVERY_DATE>0</DELIVERY_DATE>
<ITEM_ID>A093</ITEM_ID>
...
</SHOPITEM>
</SHOP>
The second feed (stock.xml) loks like:
<item_list>
<item id="A093">
<delivery_time orderDeadline="2021-09-14 12:00">2021-09-16 12:00</delivery_time>
<stock_quantity>32</stock_quantity>
...
</item>
</item_list>
So I trying something like this (similar method like the $item->ITEM_ID was in separate tag in stock.xml) but doesn't work for me..
<?php
$catalog_name = 'items.xml';
$catalog_url = 'https://admin.srovnej-ceny.cz/export/ca1b20bb6415b2d93ff36c9e3df3f96c.xml';
file_put_contents($catalog_name, fopen($catalog_url, 'r'));
$stock_name = 'stock.xml';
$stock_url = 'https://www.korkmaz.cz/heureka/export/availability.xml';
file_put_contents($stock_name, fopen($stock_url, 'r'));
$stocks=simplexml_load_file("stock.xml") or die("Error: Cannot create object");
foreach($stocks->children() as $item) {
$_stocks["" . $item['id'] . ""] = $item->stock_quantity;
}
$xml=simplexml_load_file("items.xml") or die("Error: Cannot create object");
$dom = new DOMDocument();
$dom->encoding = 'utf-8';
$dom->xmlVersion = '1.0';
$dom->formatOutput = true;
$xml_file_name = 'products.xml';
$root = $dom->createElement('SHOP');
$i=0;
foreach($xml->children() as $item) {
$item_node = $dom->createElement('SHOPITEM');
//$track = $xml->addChild('item');
$item_node->appendChild($dom->createElement('ITEM_ID', $item->ITEM_ID ));
$item_node->appendChild($dom->createElement('PRODUCTNAME', htmlspecialchars($item->PRODUCTNAME) ));
$item_node->appendChild($dom->createElement('DESCRIPTION', htmlspecialchars($item->DESCRIPTION)));
$item_node->appendChild($dom->createElement('MANUFACTURER', $item->MANUFACTURER));
$item_node->appendChild($dom->createElement('EAN', strval($item->EAN) ));
$item_node->appendChild($dom->createElement('IMGURL', strval($item->IMGURL)));
$item_node->appendChild($dom->createElement('PRICE_VAT', strval($item->PRICE_VAT)));
$item_node->appendChild( $dom->createElement('STOCK', $_stocks["" . $item['id'] . ""] ) );
$root->appendChild($item_node);
$i ;
}
$dom->appendChild($root);
$dom->save($xml_file_name);
echo "$i items to $xml_file_name has been successfully created";
?>
CodePudding user response:
Without simplexml
you can quite easily "merge" the two documents using the standard DOMDocument
and DOMXPath
functions.
Given input files as follows:
items.xml
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<SHOP>
<SHOPITEM>
<DESCRIPTION>
<![CDATA[ <p><span>Just an description. </span></p> ]]>
</DESCRIPTION>
<URL>https://www.korkmaz.cz/tombik-cajova-konvice-2l/</URL>
<IMGURL>https://cdn.myshoptet.com/usr/www.korkmaz.cz/user/shop/orig/52_konvice-tombik-1l.jpg?5f4fcd7d</IMGURL>
<IMGURL_ALTERNATIVE>https://cdn.myshoptet.com/usr/www.korkmaz.cz/user/shop/orig/52-1_bez-trouby.jpg?5f4fcd7d</IMGURL_ALTERNATIVE>
<PURCHASE_PRICE>487,99</PURCHASE_PRICE>
<PRICE_VAT>797,00</PRICE_VAT>
<VAT>21%</VAT>
<CATEGORYTEXT>KUCHYŇSKÉ DOPLŇKY | Příprava čaje a kávy</CATEGORYTEXT>
<DELIVERY_DATE>0</DELIVERY_DATE>
<ITEM_ID>A093</ITEM_ID>
</SHOPITEM>
<SHOPITEM>
<DESCRIPTION>
<![CDATA[ <p><span>Just an description. </span></p> ]]>
</DESCRIPTION>
<URL>https://www.korkmaz.cz/tombik-cajova-konvice-2l/</URL>
<IMGURL>https://cdn.myshoptet.com/usr/www.korkmaz.cz/user/shop/orig/52_konvice-tombik-1l.jpg?5f4fcd7d</IMGURL>
<IMGURL_ALTERNATIVE>https://cdn.myshoptet.com/usr/www.korkmaz.cz/user/shop/orig/52-1_bez-trouby.jpg?5f4fcd7d</IMGURL_ALTERNATIVE>
<PURCHASE_PRICE>1850,99</PURCHASE_PRICE>
<PRICE_VAT>2598,00</PRICE_VAT>
<VAT>21%</VAT>
<CATEGORYTEXT>KUCHYŇSKÉ DOPLŇKY | Příprava čaje a kávy</CATEGORYTEXT>
<DELIVERY_DATE>0</DELIVERY_DATE>
<ITEM_ID>A094</ITEM_ID>
</SHOPITEM>
<SHOPITEM>
<DESCRIPTION>
<![CDATA[ <p><span>Just an description. </span></p> ]]>
</DESCRIPTION>
<URL>https://www.korkmaz.cz/tombik-cajova-konvice-2l/</URL>
<IMGURL>https://cdn.myshoptet.com/usr/www.korkmaz.cz/user/shop/orig/52_konvice-tombik-1l.jpg?5f4fcd7d</IMGURL>
<IMGURL_ALTERNATIVE>https://cdn.myshoptet.com/usr/www.korkmaz.cz/user/shop/orig/52-1_bez-trouby.jpg?5f4fcd7d</IMGURL_ALTERNATIVE>
<PURCHASE_PRICE>200,99</PURCHASE_PRICE>
<PRICE_VAT>300,00</PRICE_VAT>
<VAT>21%</VAT>
<CATEGORYTEXT>KUCHYŇSKÉ DOPLŇKY | Příprava čaje a kávy</CATEGORYTEXT>
<DELIVERY_DATE>0</DELIVERY_DATE>
<ITEM_ID>A095</ITEM_ID>
</SHOPITEM>
stock.xml
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<item_list>
<item id="A093">
<delivery_time orderDeadline="2021-09-14 12:00">2021-09-16 12:00</delivery_time>
<stock_quantity>32</stock_quantity>
</item>
<item id="A094">
<delivery_time orderDeadline="2021-09-14 12:00">2021-09-16 12:00</delivery_time>
<stock_quantity>8366</stock_quantity>
</item>
<item id="A095">
<delivery_time orderDeadline="2021-09-14 12:00">2021-09-16 12:00</delivery_time>
<stock_quantity>6732</stock_quantity>
</item>
</item_list>
To generate the "combined xml" type output based upon matching item IDS:
<?php
/*
#
# XML file merge
#
Read "stock.xml" and find matching elements in "items.xml"
- update "items.xml" with nodes cloned from "stock.xml"
*/
function getdom($file){
libxml_use_internal_errors( true );
$dom=new DOMDocument;
$dom->validateOnParse=true;
$dom->recover=true;
$dom->strictErrorChecking=true;
$dom->preserveWhiteSpace=true;
$dom->formatOutput=true;
$dom->load($file);
libxml_clear_errors();
return $dom;
}
$items=getdom('items.xml');
$xpi=new DOMXPath($items);
$stock=getdom('stock.xml');
$xps=new DOMXPath($stock);
/*
If ALL nodes from "stock.xml" are to be merged per ID then set `$merge_only_selected=false`
`$merge_nodes` is an array of nodes from "stock.xml" that will be merged if `$merge_only_selected` is true
*/
$merge_only_selected=true;
$merge_nodes=array('stock_quantity','stock_supplier');
#Find all items in the "stock.xml" file to get the item ID
$col=$xps->query( '//item[@id]' );
foreach( $col as $node ){
#The ID from the "item"
$id=$node->getAttribute('id');
# nodelist of items within "items.xml" that have the same ID.
$item=$xpi->query( sprintf( '//SHOPITEM/ITEM_ID[ text()="%s" ]', $id ) );
# only proceed if we have found a matching node in "items.xml"
if( $item && $item->length > 0 ){
# Find the matched element
$obj=$item->item(0);
# Find the children from the "item"
$children=$node->childNodes;
# for each child found, clone it and import to the "items.xml" file
foreach( $children as $child ){
if( $child->nodeType==XML_ELEMENT_NODE && $id==$obj->nodeValue ){
if( $merge_only_selected==true && !in_array( $child->tagName, $merge_nodes ) ){
continue;
}
$clone=$child->cloneNode(true);
$obj->parentNode->appendChild( $items->importNode( $clone, true ) );
}
}
}
}
#To actually save the modified "items.xml" file:
#$items->save('items.xml');
#To simply view the changes:
printf('<textarea cols=150 rows=50>%s</textarea>',$items->saveXML() );
?>
CodePudding user response:
This is a lot easier with DOM Xpath. You can use DOMXpath::evaluate()
to fetch node lists and scalar values from the XML.
An Xpath expression like /SHOP/SHOP_ITEM
returns a DOMNodeList
which implements Traversable
to support foreach()
.
But Xpath expression can return scalar values as well. A boolean if they are a condition or a string/number if they contain a type cast or function call. string(/item_list/item[@id="A093"]/stock_quantity)
will return the text content of the first matching node or an empty string.
DOMDocument::importNode()
allows you to copy a node from another document. But in this case I would suggest creating a new node with a name matching the existing elements.
// bootstrap the XML
$shopDocument = new DOMDocument();
// ignoring pure whitespace nodes (indentation)
$shopDocument->preserveWhiteSpace = FALSE;
$shopDocument->loadXML(getShopXML());
$shopXpath = new DOMXpath($shopDocument);
$stocksDocument = new DOMDocument();
$stocksDocument->loadXML(getStocksXML());
$stocksXpath = new DOMXpath($stocksDocument);
// iterate the shop items
foreach ($shopXpath->evaluate('/SHOP/SHOPITEM') as $shopItem) {
// get the item ID
$itemID = $shopXpath->evaluate('string(ITEM_ID)', $shopItem);
$stockQuantity = 0;
if ($itemID !== '') {
// fetch the stock quantity using the item id
$stockQuantity = (int)$stocksXpath->evaluate(
"string(/item_list/item[@id = '$itemID']/stock_quantity)"
);
// check if here is a "STOCK_QUANTITY" element in the item
if ($shopXpath->evaluate('count(STOCK_QUANTITY) > 0', $shopItem)) {
// update it
foreach ($shopXpath->evaluate('STOCK_QUANTITY', $shopItem) as $quantity) {
$quantity->textContent = (string)$stockQuantity;
}
} else {
// add one
$shopItem
->appendChild($shopDocument->createElement('STOCK_QUANTITY'))
->textContent = (string)$stockQuantity;
}
}
}
$shopDocument->formatOutput = TRUE;
echo $shopDocument->saveXML();