SimpleXML add element before - php

I'm using SimpleXML to generate xml based invoice.
The structure has to be like this (heavily simplified):
<invoice>
<total>
<price>100</price>
</total>
<items>
<item>...</item>
</items>
</invoice>
But if first loop my items and add totals together, and then insert <total>:
<invoice>
<items>...</items>
<total>...</total>
</invoice>
But CUSTOM XSD says it invalid. This probably will not cause an error in applications, but I'd like it to be valid.
So can I insert <total> tag before <items> tag?
Note: <items> tag is not the first element in <invoice>.
Jquery equivalent of the function in need is .insertBefore()
Cheers!

You can do something like this:
$domelement = dom_import_simplexml($items);
$new = $dom->insertBefore(
$dom->ownerDocument->createElement("total"),
$dom->firstChild
);
$newsxml = simplexml_import_dom($new);
then add the items into total node.

Related

How to Insert new XML element at the top of other nodes? [duplicate]

I have a previously generated XML like this:
<newsletter>
<header>
</magazine>
</image>
<strap/>
</header>
<intro>
<date/>
<text/>
</edimg>
</intro>
<shop>
<heading/>
<article/>
<title/>
<img/>
<link/>
<excerpt/>
</shop>
<sidebar>
<cover/>
<cover_link/>
<text/>
<advert>
<link/>
<image/>
</advert>
</sidebar>
</newsletter>
I need to be able to insert an element in between the <intro> and the <shop> elements
this:
$section = $dom->documentElement->appendChild($dom->createElement('section'));
will just create the element within <newsletter> .
I assumed this would be fairly simple , but cannot seem to find a solution .
Thanks.
You might try this; I didn't test it, but the solution comes from using insertBefore instead of appendChild.
$shop = $dom->getElementsByTagName("shop")->item(0);
$section = $dom->documentElement->insertBefore($dom->createElement('section'),$shop);
Fetch the <shop> node and use
DOMNode::insertBefore — Adds a new child before a reference node
instead of appending to the documentElement.
You can do that from the DOMDocument as well when passing in the shop node as second argument. Personally, I find it easier to just do that from the shop node because you have to fetch it anyway:
$shopNode->insertBefore($newNode);
Try
$section = $dom->documentElement->insertBefore(
$dom->createElement('section'),
$shop)
);
where $shop points to the <shop> element.

How to insert one item before the first item currently without xpath in Domdocument using PHP [duplicate]

I have a previously generated XML like this:
<newsletter>
<header>
</magazine>
</image>
<strap/>
</header>
<intro>
<date/>
<text/>
</edimg>
</intro>
<shop>
<heading/>
<article/>
<title/>
<img/>
<link/>
<excerpt/>
</shop>
<sidebar>
<cover/>
<cover_link/>
<text/>
<advert>
<link/>
<image/>
</advert>
</sidebar>
</newsletter>
I need to be able to insert an element in between the <intro> and the <shop> elements
this:
$section = $dom->documentElement->appendChild($dom->createElement('section'));
will just create the element within <newsletter> .
I assumed this would be fairly simple , but cannot seem to find a solution .
Thanks.
You might try this; I didn't test it, but the solution comes from using insertBefore instead of appendChild.
$shop = $dom->getElementsByTagName("shop")->item(0);
$section = $dom->documentElement->insertBefore($dom->createElement('section'),$shop);
Fetch the <shop> node and use
DOMNode::insertBefore — Adds a new child before a reference node
instead of appending to the documentElement.
You can do that from the DOMDocument as well when passing in the shop node as second argument. Personally, I find it easier to just do that from the shop node because you have to fetch it anyway:
$shopNode->insertBefore($newNode);
Try
$section = $dom->documentElement->insertBefore(
$dom->createElement('section'),
$shop)
);
where $shop points to the <shop> element.

Adding Nodes to Existing XML

The problem i was having is the Root XML was being produced every time it writes to the XML.
The Main issue was setting up Child and Defining the Root. From the help of Łza
I now understand the Root XML Node is ignored.
So then you setup and create a Child and then add your content, And example of the correct format is.
$xml = simplexml_load_file('FILENAME.xml'); // Load XML File Need to add IF Statment to create if does not exist
$result = $xml->addchild('Result'); // Ignore Root NODE and Add Child Results
$result->addChild('Time', gmdate('D-M-Y -H:i:s')); // Rest of the below adds Child to Result and outputs results
$result->addChild('Channel', $Site);
$result->addChild('Type', '**');
$result->addChild('Process', $Status);
$result->addChild('SKU', $code->SKU);
$result->addChild('item', $item);
$result->addChild('Status', '$Feedback');
$result->addChild('ErrorID', '$Error');
$result->addChild('Message', '$Message');
$xml->asXml('FILENAME.xml'); //Write to file would be
// All of the above Code is using variables from another part of the script
The output would be
<Root>
<Result>
<Time>Fri-May-2013 -09:15:22</Time>
<Channel>20</Channel>
<Type>**</Type>
<Process>Update</Process>
<SKU>98746524765</SKU>
<Item/>
<Status>Problem</Status>
<ErrorID>999-Error</ErrorID>
<Message>Unknown file format support</Message>
</Result>
<Result>
<Time>Fri-May-2013 -09:15:22</Time>
<Channel>20</Channel>
<Type>**</Type>
<Process>Update</Process>
<SKU>5412254785</SKU>
<Item/>
<Status>Problem</Status>
<ErrorID>123-Error</ErrorID>
<Message>Invalid Item</Message>
</Result>
</Root>
Thanks
Try to use SimpleXMLElement library instead hardcoded xml creation. This is maybe more complicate to use at the begining, but much more safe (I mean avoid possible errors in xml structure when you hardcode the xml) and easy to use when you just get start to use it.
And easy to add/remove nodes, childnodes.
This is an example for your code:
$xml = new SimpleXMLElement('<xml/>');
$data = $xml->addChild('data');
$result = $data->addChild('Result');
$result->addChild('Time', gmdate('D-M-Y -H:i:s'));
$result->addChild('Channel', $SiteID);
// ... and the same way create all your xml nodes.
// if you want add next <result> node witch all elements repeat the code, (or put it in loop if you want more <result> elements):
$result = $data->addChild('Result');
$result->addChild('Time', gmdate('D-M-Y -H:i:s'));
$result->addChild('Channel', $SiteID);
// and after create all nodes save the file:
$xml->asXml('DHError.xml');
above code will create xml:
<xml>
<data>
<Result>
<Time>Fri-May-2013 -12:14:39</Time>
<Channel>data</Channel>
</Result>
<Result>
<Time>Fri-May-2013 -12:14:39</Time>
<Channel>data</Channel>
</Result>
</data>
</xml>
Thats it. Then if you need to load and process the xml it would be easy:
To load the File simply use:
$xml2 = simplexml_load_file('DHError.xml');
// to add new node <Result>:
$resultNext = $xml2->data->addchild('Result');
$resultNext->addChild('Time', gmdate('D-M-Y -H:i:s'));
$resultNext->addChild('Channel', $SiteID);
//and save file
$xml2->asXml('DHError.xml');
this create a xml:
<?xml version="1.0" ?>
<xml>
<data>
<Result>
<Time>Fri-May-2013 -12:27:24</Time>
<Channel>data</Channel>
</Result>
<Result>
<Time>Fri-May-2013 -12:27:24</Time>
<Channel>data</Channel>
</Result>
<Result>
<Time>Fri-May-2013 -12:27:24</Time>
<Channel>data</Channel>
</Result>
</data>
</xml>

Getting information from more than one large XML file

I am trying to get information from external large xml files; from file 1 (vehicleList.xml) and file 2 (CheckVehicles.xml) into a PHP file. All values in XML file 2 are in XML file 1. I would like to display only values in file 1 that are in XML file 2.
My foreach loop code can bring results for up to 130 items (that is if I reduce the items in XML file 2 to 130 items/nodes). However if I remove the if statement, I am able to get all the 3340 items/vehicles from XML file 1.
Where am I going wrong? I tried arrays but failed.
Here is my code:
//XML FILE 1 with 1300 items
$myXML = new SimpleXMLElement('CheckVehicles.xml', NULL, TRUE);//
foreach($myXML->root->item as $item){
$listArrayNew[(int)$item->value] = (int)$item->value;
}
//XML FILE 2 with 3340 vehicles
$parser = new SimpleXMLElement('vehicleList.xml', NULL, TRUE);
foreach ($parser->GetVehiclesListResponse->GetVehiclesListResult->Vehicle as $Vehicle) {
if($listArrayNew[(int)$Vehicle->ID] == (int)$Vehicle->ID){
$vehicle = $Vehicle->Description;
$regNumber = $Vehicle->RegistrationNumber;
$siteID = $Vehicle->SiteID;
$row .= "<tr>
<td>".$vehicle."</td>
<td>".$regNumber."</td>
<td>".$siteId."</td>
</tr>";
}
}
Here are the XML files:
XML file 1: vehicleList.xml
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope>
<GetVehiclesListResponse>
<GetVehiclesListResult>
<Vehicle>
<ID>153</ID>
<SiteID>11</SiteID>
<GroupID>3</GroupID>
<Description>A.O Basid KAR 459 E</Description>
<RegistrationNumber>KAR 459 E</RegistrationNumber>
</Vehicle>
..............................
<Vehicle>
<ID>3340</ID>
<SiteID>25</SiteID>
<GroupID>4</GroupID>
<Description>UAR 712B White Nissan Tiida (Deus Mubangizi)</Description>
<RegistrationNumber>UAR 712B</RegistrationNumber>
</Vehicle>
</GetVehiclesListResult>
</GetVehiclesListResponse>
</soap:Envelope>
XML file 2: CheckVehicles.xml
<?xml version="1.0" encoding="utf-8"?>
<Result>
<root>
<item>
<index>0</index>
<value>153</value>
</item>
...................
<item>
<index>1300</index>
<value>128</value>
</item>
</root>
</Result>
I don't know where you go wrong in your case. However if you want to select elements from the second file based on a criteria (e.g. an ID / unique Number) from the first file I suggest you make use of xpath in your case:
Obtain the numbers from the first file that are the criteria (e.g. /*/root/item/value)
Select all elements from the second file that match the criteria (e.g. ID in /*/GetVehiclesListResponse/GetVehiclesListResult/Vehicle).
The later point can best be achieved by using the technique outlined in Is there anything for XPATH like SQL “IN”? which is creating a comma separated list of the numbers to select and then compare this against each elements number.
Example:
Consider there 2 500 out of 10 000 elements in a first file and in a second file there are 10 000 elements. Each element can be uniquely identified by it's ID.
The first file has this layout:
<?xml version="1.0"?>
<root>
<item>
<index>0</index>
<id>604</id>
</item>
<item>
<index>1</index>
<id>2753</id>
</item>
...
</root>
And the second file has this layout.
<?xml version="1.0"?>
<list>
<item>
<id>1</id>
<some>Number: 33</some>
</item>
<item>
<id>2</id>
<some>Number: 35</some>
</item>
...
</list>
The xpath query to get all IDs from the first file therefore is:
//item/id
And the query for the second file can be expressed with SimpleXML in PHP as:
$ids = implode(',', $file1->xpath('//item/id'));
$query = '//item[contains(",' . $ids . ',", concat(",", id, ","))]';
You can find example code of that example here: http://eval.in/6370

PHP XML inserting element after (or before) another element

I have a previously generated XML like this:
<newsletter>
<header>
</magazine>
</image>
<strap/>
</header>
<intro>
<date/>
<text/>
</edimg>
</intro>
<shop>
<heading/>
<article/>
<title/>
<img/>
<link/>
<excerpt/>
</shop>
<sidebar>
<cover/>
<cover_link/>
<text/>
<advert>
<link/>
<image/>
</advert>
</sidebar>
</newsletter>
I need to be able to insert an element in between the <intro> and the <shop> elements
this:
$section = $dom->documentElement->appendChild($dom->createElement('section'));
will just create the element within <newsletter> .
I assumed this would be fairly simple , but cannot seem to find a solution .
Thanks.
You might try this; I didn't test it, but the solution comes from using insertBefore instead of appendChild.
$shop = $dom->getElementsByTagName("shop")->item(0);
$section = $dom->documentElement->insertBefore($dom->createElement('section'),$shop);
Fetch the <shop> node and use
DOMNode::insertBefore — Adds a new child before a reference node
instead of appending to the documentElement.
You can do that from the DOMDocument as well when passing in the shop node as second argument. Personally, I find it easier to just do that from the shop node because you have to fetch it anyway:
$shopNode->insertBefore($newNode);
Try
$section = $dom->documentElement->insertBefore(
$dom->createElement('section'),
$shop)
);
where $shop points to the <shop> element.

Categories