Home > other >  PHP XML Trying to add stock_quantity by item id into main feed
PHP XML Trying to add stock_quantity by item id into main feed

Time:09-16

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.&nbsp;</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.&nbsp;</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.&nbsp;</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.&nbsp;</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();
  • Related