update/append data to xml file using php - php

this may sound pretty straight forward, but still I want to post this question in the forum. I have a xml file, which needs to be appended with data after the main element and save the xml file without overwriting the existing xml file but to append the data to already existing data and update the xml file.
For example my xml data looks something similar to this:
<maincontent>
<headercontent>
<product num="2102">
<name>MSG</name>
<category>Wellness</category>
<available content="YES"></available>
</product>
<product num="2101">
<name>YSD</name>
<category>Music</category>
<available content="NO"></available>
</product>
<product num="2100">
<name>RCS</name>
<category>Media</category>
<available content="YES"></available>
</product>
</headercontent>
</maincontent>
I want to add another product with all the info and append the newly added data at the top so that the newly added data should come after the headercontent.
Data to be added:
<product num="2103">
<name>AGB</name>
<category>Movies</category>
<available content="YES"></available>
</product>
The updated xml file should be looking like this as shown below:
<maincontent>
<headercontent>
<product num="2103">
<name>AGB</name>
<category>Movies</category>
<available content="YES"></available>
</product>
<product num="2102">
<name>MSG</name>
<category>Wellness</category>
<available content="YES"></available>
</product>
<product num="2101">
<name>YSD</name>
<category>Music</category>
<available content="NO"></available>
</product>
<product num="2100">
<name>RCS</name>
<category>Media</category>
<available content="YES"></available>
</product>
</headercontent>
</maincontent>
Any useful advice or a piece of example code would be really helpful.
Edit:
sorry guys I haven't posted any php code, my fault. Here is the code which I have been working on:
Thanks
<?php
$xmldoc = new DomDocument();
$xmldoc->formatOutput = true;
$productNum = "2103";
$name = "AGB";
$category = "Movies";
$content = "YES";
if($xml = file_get_contents('main.xml')){
$xmldoc->loadXML($xml);
$root = $xmldoc->firstChild;
$newElement = $xmldoc->createElement('product');
$root->appendChild($newElement);
$numAttribute = $xmldoc->createAttribute("num");
$numAttribute->value = $productNum;
$newElement->appendChild($numAttribute);
$nameElement = $xmldoc->createElement('name');
$root->appendChild($nameElement);
$nameText = $xmldoc->createTextNode($name);
$nameElement->appendChild($nameText);
$categoryElement = $xmldoc->createElement('category');
$root->appendChild($categoryElement);
$categoryText = $xmldoc->createTextNode($category);
$categoryElement->appendChild($categoryText);
$availableElement = $xmldoc->createElement('available');
$root->appendChild($availableElement);
$availableAttribute = $xmldoc->createAttribute("content");
$availableAttribute->value = $content;
$availableElement->appendChild($availableAttribute);
$xmldoc->save('main.xml');
}
?>
My xml file gets updated but the data is added to the firstchild and that too at the bottom, instead I want to add data after and in the beginning as shown above.
Here is my output:
<maincontent>
<headercontent>
<product num="2102">
<name>MSG</name>
<category>Wellness</category>
<available content="YES"/>
</product>
<product num="2101">
<name>YSD</name>
<category>Music</category>
<available content="NO"/>
</product>
<product num="2100">
<name>RCS</name>
<category>Media</category>
<available content="YES"/>
</product>
</headercontent>
<product num="2103"/><name>AGB</name><category>Movies</category><available content="YES"/></maincontent>
Any advice?

This will work.
<?php
$xmldoc = new DomDocument( '1.0' );
$xmldoc->preserveWhiteSpace = false;
$xmldoc->formatOutput = true;
$productNum = "2103";
$name = "AGB";
$category = "Movies";
$content = "YES";
if( $xml = file_get_contents( 'main.xml') ) {
$xmldoc->loadXML( $xml, LIBXML_NOBLANKS );
// find the headercontent tag
$root = $xmldoc->getElementsByTagName('headercontent')->item(0);
// create the <product> tag
$product = $xmldoc->createElement('product');
$numAttribute = $xmldoc->createAttribute("num");
$numAttribute->value = $productNum;
$product->appendChild($numAttribute);
// add the product tag before the first element in the <headercontent> tag
$root->insertBefore( $product, $root->firstChild );
// create other elements and add it to the <product> tag.
$nameElement = $xmldoc->createElement('name');
$product->appendChild($nameElement);
$nameText = $xmldoc->createTextNode($name);
$nameElement->appendChild($nameText);
$categoryElement = $xmldoc->createElement('category');
$product->appendChild($categoryElement);
$categoryText = $xmldoc->createTextNode($category);
$categoryElement->appendChild($categoryText);
$availableElement = $xmldoc->createElement('available');
$product->appendChild($availableElement);
$availableAttribute = $xmldoc->createAttribute("content");
$availableAttribute->value = $content;
$availableElement->appendChild($availableAttribute);
$xmldoc->save('main.xml');
}
?>
Hope this helps.

Related

How do I extract xml data for all products where a certain field has a certain value?

The XML file contains a 1000 product entries. Some of them for the country Portugal.
I would like to get only the products where the country is Portugal and write that information into a new XML file onto my server.
How would I do that in PHP?
The XML content structure:
<products>
<product ID="38450">
<name>Aparthotel Alfagar</name>
<price currency="EUR">239.00</price>
<URL>https://website.com/</URL>
<images>
<image>https://website.com/1.jpg</image>
<image>https://website.com/2.jpg</image>
<image>https://website.com/3.jpg</image>
</images>
<description>
<![CDATA[<p>some text</p>]]>
</description>
<categories/>
<properties>
<property name="country">
<value>Portugal</value>
</property>
<property name="lowestPrice">
<value>239.00</value>
</property>
<property name="lowestPriceDate">
<value>13-01-2020</value>
</property>
</properties>
<variations/>
</product>
<!-- more product entries -->
My approuch started out as this:
<?php
// Define source
$source_url = 'https://website.net/?encoding=utf-8&type=xml&id=';
// Define target
$file_url = '/home/website/public_html/media/';
$file_ext = '.xml';
// Load data
$array = simplexml_load_file($source_url.'654321');
// Filter data
$results_portugal = '';
foreach($array->product->properties->property->value['Portugal'] as $results) {
}
// Create datafiles
copy ($results_portugal,$file_url.'portugal'.$file_ext);
Obiously I got stuck pretty soon. Can anyone help me out please? Many thanks in advance!
You can fetch a part of an XML in SimpleXML or DOM using XPath expressions:
$products = new SimpleXMLElement($xml);
var_dump(
count(
$products->xpath('//product[properties/property[#name = "country"]/value = "Portugal"]')
)
);
var_dump(
count(
$products->xpath('//product[properties/property[#name = "country"]/value = "Spain"]')
)
);
However here is no "nice" way to copy nodes in SimpleXML. DOM allows that:
// create source document and load XML
$source = new DOMDocument();
$source->loadXML($xml);
$xpath = new DOMXpath($source);
// create target document and append root node
$target = new DOMDocument();
$target->appendChild($target->createElement('products'));
$expression = '//product[properties/property[#name = "country"]/value = "Portugal"]';
// iterate filtered nodes
foreach ($xpath->evaluate($expression) as $product) {
// import node into target document and append
$target->documentElement->appendChild(
$target->importNode($product, TRUE)
);
}
echo $target->saveXML();
For really large XMLs you need to use XMLReader/XMLWriter. They allow you to load only a part of the XML file into memory. Originally here is no easy way to copy nodes but I added this to FluentDOM.
// Create the target writer and add the root element
$writer = new \FluentDOM\XMLWriter();
$writer->openUri('php://stdout');
$writer->setIndent(2);
$writer->startDocument();
$writer->startElement('products');
// load the source into a reader
$reader = new \FluentDOM\XMLReader();
$reader->open('data://text/plain;base64,'.base64_encode($xml));
// iterate the product elements - the iterator expands them into a DOM node
foreach (new FluentDOM\XMLReader\SiblingIterator($reader, 'product') as $product) {
/** #var \FluentDOM\DOM\Element $product */
// validate country property
if ($product('properties/property[#name = "country"]/value = "Portugal"')) {
// write expanded node to the output
$writer->collapse($product);
}
}
$writer->endElement();
$writer->endDocument();

SimpleXMLElement output format

I'm using SimpleXMLElement for create datafeed page: Here the code:
$xml = new SimpleXMLElement("<Products />");
foreach ($products as $pro) {
$track = $xml -> addChild('Product');
$track -> addChild('simple_sku', "<![CDATA[ ABC ]]>");
}
Header('Content-type: text/xml');
print($xml -> asXML());
and the output:
<Products>
<Product>
<simple_sku><![CDATA[ ABC ]]></simple_sku>
</Product>
</Products>
I want to know how to turn output into format like this:
<Products>
<Product>
<simple_sku>
<![CDATA[ ABC ]]>
</simple_sku>
</Product>
<Products>
Thanks you a lot.
It's not possible with SimpleXML so I advice you to use DOMDocument.
Usage example:
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($simpleXml->asXML());

PHP XML: how to use createCDataSection() properly

I am trying to create an xml dynamically using domDocument.
What I would like to obtain is following:
<?xml version="1.0"?>
<books>
<book>
<content>
<name><![CDATA[dddd]]></name>
</content>
</book>
</books>
Unfortunatelly the script below does not show the output as excepted.
$xml = new DOMDocument('1.0');
$xml->formatOutput = true;
$books = $xml->createElement('books');
$xml->appendChild($books);
$book = $xml->createElement('book');
$books->appendChild($book);
$inside = $xml->createElement('content');
$book->appendChild($inside);
$xml->appendChild($inside)->appendChild($xml->createElement('name'))->appendChild($xml->createCDataSection('dddd'));
echo '<xmp>'.$xml->saveXML().'</xmp>';
This is the output
<?xml version="1.0"?>
<books>
<book>
<content>
<name><![CDATA[dddd]]></name>
<lastname><![CDATA[dddd]]></lastname>
....
</content>
I do not know how to use createCDataSection().
Like this?
$xml = new DOMDocument('1.0');
$xml->formatOutput = true;
$books = $xml->createElement('books');
$xml->appendChild($books);
$book = $xml->createElement('book');
$books->appendChild($book);
$content = $xml->createElement('content');
$book->appendChild($content);
$name = $xml->createElement('name');
$content->appendChild($name);
$name->appendChild($xml->createCDataSection('dddd'));
echo '<xmp>'.$xml->saveXML().'</xmp>';

Explode the attribute in XML File

I have an XML with attribute like this:
<products>
<product ProductName="One" ProductCategory="Software::Utilities::Email">
<product ProductName="Two" ProductCategory="Software::Video::Editing">
<product ProductName="Three" ProductCategory="Software::Audio::Converter">
</products>
And how can I explode the "ProductCategory" attribute and separated it like this:
<products>
<product ProductName="One" ProductCategory="Software">
<product ProductName="One" ProductCategory="Utilities">
<product ProductName="One" ProductCategory="Email">
<product ProductName="Two" ProductCategory="Software">
<product ProductName="Two" ProductCategory="Video">
<product ProductName="Two" ProductCategory="Editing">
<product ProductName="Three" ProductCategory="Software">
<product ProductName="Three" ProductCategory="Audio">
<product ProductName="Three" ProductCategory="Converter">
</products>
An Example for you
<?php
$string = <<<XML
<products>
<product ProductName="One" ProductCategory="Software::Utilities::Email"></product>
<product ProductName="Two" ProductCategory="Software::Video::Editing"></product>
<product ProductName="Three" ProductCategory="Software::Audio::Converter"></product>
</products>
XML;
$xml = simplexml_load_string($string);
$obj = json_decode(json_encode($xml), true);
$new_xml = '<products>';
foreach($obj['product'] as $val){
$name = $val['#attributes']['ProductName'];
$pro = explode('::', $val['#attributes']['ProductCategory']);
foreach($pro as $k=>$v){
$new_xml .= '<product ProductName="'.$name.'" ProductCategory="'.$v.'"></product>';
}
}
$new_xml .= '</products>';
$file = fopen("test.xml","w");
fwrite($file, $new_xml);
fclose($file);
?>
You example XML is not valid XML. Make sure to close the product element nodes.
Load the source document into a DOM, create a new target DOM. Import the document element into the target (without child). This will create a copy of that node.
Iterate the product nodes and read the ProductCategory attribute, explode it into an array. Iterate the array an copy the node into the target document (for each value), change the attribute to the value.
$source = new DOMDocument();
$source->loadXml($xml);
$xpath = new DOMXPath($source);
$target = new DOMDocument();
$target->formatOutput = true;
$root = $target->appendChild($target->importNode($source->documentElement));
foreach ($xpath->evaluate('/products/product') as $node) {
$list = explode('::', $node->getAttribute('ProductCategory'));
foreach ($list as $value) {
$newNode = $root->appendChild($target->importNode($node));
$newNode->setAttribute('ProductCategory', $value);
}
}
echo $target->saveXml();
Demo: https://eval.in/209729

XML creation on the fly using PHP

I have a small requirement where I need to create a XML file on the fly. It was no problem for me to create a normal xml file which would be looking like this:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<name></name>
</item>
</root>
But my requirement is such that I need to create a XML file whose output is:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<name url = "C:\htdocs\proj1\source_file1"/>
<name url = "C:\htdocs\proj1\source_file2"/>
<name url = "C:\htdocs\proj1\source_file3"/>
</item>
</root>
I have tried in this fashion:
<?php
$domtree = new DOMDocument('1.0', 'UTF-8');
$domtree->formatOutput = true;
$xmlRoot = $domtree->createElement("root");
$xmlRoot = $domtree->appendChild($xmlRoot);
$item = $domtree->createElement("item");
$item = $xmlRoot->appendChild($item);
$name= $domtree->createElement("name");
$name = $item->appendChild($name);
$sav_xml = $domtree->saveXML();
$handle = fopen("new.xml", "w");
fwrite($handle, $sav_xml);
fclose($handle);
?>
But I wanted to append/add the url="path" to my elements. I have tried declaring variables with url and path but this throws me errors like:
Uncaught exception 'DOMException' with message 'Invalid Character Error'
Any ideas how to approach this problem!
Thanks
You just have to declare that attributes via php DOM:
...
$name= $domtree->createElement("name");
$urlAttribute = $domtree->createAttribute('url');
$urlAttribute->value = 'C:\htdocs\proj1\source_file1';
$name->appendChild($urlAttribute);
$item->appendChild($name);
...
Link to DOMDocument docs

Categories