How to insert dynamic row in different html table position - php

I want to echo following xml data in html table using php,
<type>Debit</type>
<item>Item-1</item>
<price>150</price>
<type>Debit</type>
<item>Item-2</item>
<price>250</price>
<type>Debit</type>
<item>Item-3</item>
<price>100</price>
type>Debit</type>
<item>Item-4</item>
<price>200</price>
............
...so on
<type>Credit</type>
<item>Item-50</item>
<price>200</price>
<type>Credit</type>
<item>Item-51</item>
<price>300</price>
<type>Credit</type>
<item>Item-60</item>
<price>100</price>
............
...so on
here is my Html table structure before inserting data
Now if you see my xml data, i want to dynamically insert debit items under Debit List and credit items in Credit List like the following sample:
I know how to add rows dynamically using insertRow(), getElementById("id").But in this case i need to maintain two different position [1 for debit, 1 for credit] so that i can insert them in correct position.Moreover rows are dynamic, so fixed id with fixed number of rows will not work i guess.How can i do this in php.Please share your idea to solve it in a vary simple way.Please let me know for any further information.Thanks

try this solution. If you use javascript, you use two tables and use append() element row in tbody.
<!DOCTYPE html>
<html>
<body>
<?php
$myXMLData =
"
<data>
<row>
<type>Debit</type>
<item>Item-1</item>
<price>150</price>
</row>
<row>
<type>Debit</type>
<item>Item-2</item>
<price>250</price>
</row>
<row>
<type>Debit</type>
<item>Item-3</item>
<price>100</price>
</row>
<row>
<type>Debit</type>
<item>Item-4</item>
<price>200</price>
</row>
<row>
<type>Credit</type>
<item>Item-50</item>
<price>200</price>
</row>
<row>
<type>Credit</type>
<item>Item-51</item>
<price>300</price>
</row>
<row>
<type>Credit</type>
<item>Item-60</item>
<price>100</price>
</row>
</data>";
$xml=simplexml_load_string($myXMLData) or die("Error: Cannot create object");
$debit = array();
$credit = array();
$debit_total = $credit_total = 0;
foreach ($xml as $row) {
if($row->type == 'Debit') {
$debit[] = array('item'=> (string)$row->item, 'price'=> (float)$row->price);
$debit_total += (float)$row->price;
} else {
$credit[] = array('item'=> (string)$row->item, 'price'=> (float)$row->price);
$credit_total += (float)$row->price;
}
}
?>
<table>
<tr>
<td colspan="2">Debit list<td>
</tr>
<?php
foreach ($debit as $key => $value) {
echo "<tr><td>{$value['item']}</td><td>{$value['price']}</td></tr>";
}
echo "<tr><td>Total</td><td>{$debit_total}</td></tr>";
echo "<tr><td colspan='2'></td></tr>";
echo "<tr><td colspan='2'>Credit list</td></tr>";
foreach ($credit as $key => $value) {
echo "<tr><td>{$value['item']}</td><td>{$value['price']}</td></tr>";
}
echo "<tr><td>Total</td><td>{$credit_total}</td></tr>";
echo "<tr><td colspan='2'></td></tr>";
?>
</table>
</body>
</html>

Related

Selecting Attribute and listing Value base on NODE Value using Xpath

How do i get All Attribute Value ID of every node FLOWER?
<Files>
<data id="1">
<Type>Flower</Type>
</data>
<data id="2">
<Type>Flower</Type>
</data>
<data id="3">
<Type>Flower</Type>
</data>
<data id="4">
<Type>Flower</Type>
</data>
</Files>
In mysql case it will be like SELECT id from Files WHERE Type="Flower"
How do i code xpath for this condition?
and List it using SimpleXML within option box.
<select>
<?php
foreach ($type as $id) {
echo '<option value="'.$id.'">'.$id.'</option>';
}
?>
</select>
To get all #id attribute's values try
/Files/data[normalize-space(Type) = 'Flower']/#id
'//data[(./Type/text()="Flower")]/#id'
Your XML is invalid, the closing root element does not match and the Type elements are closed as type. XML is case sensitive.
Xpath works uses location paths and conditions a location path is a hierarchical path to the element from the current context. They return a list of nodes. The list can be filtered using conditions.
SimpleXMLElement objects have a method xpath() to execute an expression in the context of the associated node.
$xml = <<<'XML'
<Files>
<data id="1">
<type>Flower</type>
</data>
<data id="2">
<type>Flower</type>
</data>
<data id="3">
<type>Flower</type>
</data>
<data id="4">
<type>Flower</type>
</data>
</Files>
XML;
$files = new SimpleXMLElement($xml);
$target = new SimpleXMLElement('<select/>');
foreach ($files->xpath('data[type = "Flower"]') as $data) {
echo '.';
$option = $target->addChild('option', $data['id']);
$option['value'] = $data['id'];
}
echo $target->asXml();
You should not create you XML as text. Use an XML Api for it.
DOM is more specific and powerful. For example you can serialize the created DOM as HTML.
$source = new DOMDocument();
$source->loadXml($xml);
$xpath = new DOMXpath($source);
$target = new DOMDocument();
$select = $target->appendChild($target->createElement('select'));
foreach ($xpath->evaluate('/Files/data[type = "Flower"]') as $data) {
$option = $select->appendChild($target->createElement('option'));
$option->setAttribute('value', $data->getAttribute('id'));
$option->appendChild($target->createTextNode($data->getAttribute('id')));
}
echo $target->saveHtml($select);
This is how i used the answer Feel free to use the code if you like.
Thanks!!
<?php
//I have used 2 given answer as example on how i used it. Feel Free to use the code below
$type = $_GET['type'];
if(file_exists("xml/data.xml")) {
$xml = simplexml_load_file('xml/data.xml') or die("Data Missing"); }
<!-- Code Example 1 -->
$ids = $xml->xpath('//data[(./Type/text()="'.$type.'")]/#id');
<!-- Code Example 2 -->
$idx = $xml->xpath('/Files/data[normalize-space(Type) = "'.$type.'"]/#id');
?>
<!-- Example 1 -->
<select>
<?php
//echo $ids[0];
foreach ($ids as $id) {
echo '<option value="'.$id[0].'">'.$id[0].'</option>';
}
?>
</select>
<!-- Example 2 -->
<select>
<?php
//echo $ids[0];
foreach ($idx as $id2) {
echo '<option value="'.$id2[0].'">'.$id2[0].'</option>';
}
?>
</select>
<a href="logout.php">Logout
</a>

php xpath iterating through inner nodes, get values based on specific attributes

I have an XML document in following structure.
<rows pos="0" rec_count="200">
<row id="109">
<cell style="" fieldName="pid">104</cell>
<cell style="" fieldName="title">Mr.</cell>
<cell style="" fieldName="name">Vladimir</cell>
<cell style="" fieldName="projectcode">879</cell>
<cell style="" fieldName="clientstatus">1000000000</cell>
</row>
<row id="111">
<cell style="" fieldName="pid">105</cell>
<cell style="" fieldName="title">Mr.</cell>
<cell style="" fieldName="name">Barak</cell>
<cell style="" fieldName="projectcode">789/cell>
<cell style="" fieldName="clientstatus">1000000000</cell>
</row>
</rows>
Now I need to get value of each row, to an array, which each row element is an associative array where fieldName of above becomes key, element value is node value.
How can I do accomplish this with XPath?
I tried several methods; none worked for me yet.
Thanks #JeffreyBosboom , #Makyen for your reply.
The most successfull I came is as follows.
foreach ($xml->xpath('//row') as $node) {
foreach($node->cell as $cell) {
var_dump((string)$cell[0]);
}
}
With it I can get the node value. But I need to get the fieldName attribute extracted in each iteration
Do you mean something like this:
$arr = array();
$s = simplexml_load_string($xml);
//First we search all the <cell> nodes within <row> nodes
foreach($s->xpath("//row/cell") as $node)
{
//For all found nodes we retrieve its ID from ancestor (<row>)
//attribute named id
$id = $node->xpath("../#id");
$id = (int)$id[0];
if(!isset($arr[$id]))
{
$arr[$id] = array();
}
//Then we iterate through all attributes of found <cell> nodes
//to find the attribute named fieldName
foreach($node->attributes() as $key=>$att)
{
if($key=='fieldName')
{
$arr[$id][(string)$att] = (string)$node;
}
}
}
//In $arr array there will be desired output
echo "<pre>";
print_r($arr);
echo "</pre>";
?

PHP XML DOM - Distinguishing nodes based on parent attributes/elements

First of all, thanks for the time reading this :)
I need help reordering an XML feed. I've tried many things and researched, but can't come up with a solution.
<xml>
<group>
<result>
<title>Title</title>
<url>URL</url>
<text>Text</text>
</result>
<result>
<title>Title</title>
<url>URL</url>
<text>Text</text>
</result>
</group>
<group region=top>
<result>
<title>Title</title>
<url>URL</url>
<text>Text</text>
</result>
</group>
<group type=bottom>
<result>
<title>Title</title>
<url>URL</url>
<text>Text</text>
<moreinfo>
<result>
<title>Title</title>
</result>
<result>
<title>Title</title>
</result>
</moreinfo>
</result>
</group>
</xml>
What I am trying to do is re-order the XML feed to display each node inside 'result'. However, I need the feed reordered so the 'result' from 'group region=top' is at the top, then the results from 'group', then results from 'group region=bottom'.
You might notice there is another 'result' tag nested inside of a 'result' tag in 'group region=bottom', which is causing most of the issues. The way I envision a solution to this is with the following pseudo-code:
$books = $doc->getElementsByTagName( "result" );
foreach( $books as $book )
{
if (parent_attribute = top){
$toptitle = $book->getElementsByTagName( "title" );
$toptitle = $toptitle->item(0)->nodeValue;
$topnew[] =array("title"=>$toptitle);
}
if (parent_attribute = null){
$middletitle = $book->getElementsByTagName( "title" );
$middletitle = $middletitle->item(0)->nodeValue;
$middlenew[] =array("title"=>$middletitle);
}
if (parent_attribute = bottom){
$bottomtitle = $book->getElementsByTagName( "title" );
$bottomtitle = $bottomtitle->item(0)->nodeValue;
if (parent_element = moreinfo){
$moretitle = $book->getElementsByTagName( "title" );
$moretitle = $moretitle->item(0)->nodeValue;
}
$bottomnew[] =array("title"=>$bottomtitle, "more"=>$moretitle);
}
}
The XML you posted is invalid and not consistent, as attribute values have to be enclosed in "", for consistency see my comment above.
Here's how I do it using simplexml and xpath, it can be easily adapted to DOM as well.
$xml = simplexml_load_string($x); // assuming XML in $x
$regions['top'] = $xml->xpath("group[#region='top']/result");
$regions['middle'] = $xml->xpath("group[not(#*)]/result"); // <group> with no attribute
$regions['bottom'] = $xml->xpath("group[#region='bottom']/result");
// output:
foreach ($regions as $region => $results) {
echo "$region:<br />";
foreach ($results as $result) {
echo $result->title . "<br />";
if (count($result->moreinfo) > 0)
foreach ($result->moreinfo->result as $subresult)
echo "____$subresult->title<br />";
} // foreach $results
} // foreach $regions
see it working: http://codepad.viper-7.com/5adWxC

Retrieving data from siblings to node that match

I'm iterating through a xml doc with SimpleXML. I have an array ($ids) with id's and I'm checking whether there's a match or not in the XML (Worksheet/Table/Row/Cell/Data). If it's a match I want to be able to get to the data from the following two siblings, but I can't figure out how.
from the php:
// $ids <---- array('8', '53', '38')
foreach ($thePositions->Worksheet->Table->Row as $row) {
if($row->Cell->Data == true) {
for ($i = 0; $i < count($ids); $i++) {
foreach($row->Cell->Data as $data) {
if ($data == $ids[$i]) {
echo 'match!';
/*
Tried $siblings = $data->xpath('preceding-sibling::* | following-sibling::*');
but doesn't seem to work in this case.
*/
}
}
}
}
}
the xml:
<?xml version="1.0"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<LastAuthor>Herpa Derp </LastAuthor>
<Created>2012-09-25T13:44:01Z</Created>
<LastSaved>2012-09-25T13:48:24Z</LastSaved>
<Version>14.0</Version>
</DocumentProperties>
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
<AllowPNG/>
</OfficeDocumentSettings>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>14060</WindowHeight>
<WindowWidth>25040</WindowWidth>
<WindowTopX>25540</WindowTopX>
<WindowTopY>4100</WindowTopY>
<Date1904/>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="12" ss:Color="#000000"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s62">
<Font ss:FontName="Courier" ss:Color="#000000"/>
</Style>
</Styles>
<Worksheet ss:Name="Workbook1.csv">
<Table ss:ExpandedColumnCount="5" ss:ExpandedRowCount="79" x:FullColumns="1"
x:FullRows="1" ss:DefaultColumnWidth="65" ss:DefaultRowHeight="15">
<Column ss:Index="2" ss:AutoFitWidth="0" ss:Width="43"/>
<Column ss:AutoFitWidth="0" ss:Width="113"/>
<Column ss:Index="5" ss:AutoFitWidth="0" ss:Width="220"/>
<Row ss:Index="6">
<Cell ss:Index="3" ss:StyleID="s62"/>
</Row>
<Row>
<Cell ss:Index="3" ss:StyleID="s62"/>
</Row>
<Row>
<Cell ss:Index="3" ss:StyleID="s62"/>
</Row>
<Row>
<Cell ss:Index="2"><Data ss:Type="String">id</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="String">latitude</Data></Cell>
<Cell><Data ss:Type="String">longitude</Data></Cell>
</Row>
<Row>
<Cell ss:Index="2"><Data ss:Type="Number">8</Data></Cell>
<Cell ss:StyleID="s62"><Data ss:Type="Number">57.4999</Data></Cell> // to be saved to $latutude
<Cell><Data ss:Type="Number">15.8280</Data></Cell> // to be saved to $longitude
</Row>
<Row>
<Cell ss:Index="2"><Data ss:Type="Number">38</Data></Cell>
<Cell><Data ss:Type="Number">56.5659</Data></Cell>
<Cell><Data ss:Type="Number">16.1380</Data></Cell>
</Row>
The reason asking for siblings isn't working is that the <Data> elements aren't siblings; they're more like cousins - children of adjacent <Cell> elements.
For the same reason, you shouldn't be using foreach($row->Cell->Data as $data), as this is equivalent to foreach($row->Cell[0]->Data as $data), i.e. look at all <Data> children of the first <Cell> node. Since there will only ever be one <Data> element in a <Cell>, you might as well just write $data = $row->Cell[0]->Data - which in this case is fine, because the values you're looking for are at the beginning of rows.
What you actually need to do is loop over the <Cell>s: foreach($row->Cell as $cell) { $data = $cell->Data; /* ... */ }
You then have a couple of options for finding the adjacent cells, including XPath. A more "PHP-ish" way would be to use array indexes (siblings are indexed numerically in SimpleXML loop/array access):
foreach($row->Cell as $cell_index => $cell)
{
$data = $cell->Data;
if ($data == $ids[$i])
{
// Tip: always cast SimpleXML objects to string when you're done with their magic XMLiness
$latitudes[$i] = (string)$row->Cell[ $cell_index + 1 ]->Data;
$longitudes[$i] = (string)$row->Cell[ $cell_index + 2 ]->Data;
}
}
Alternatively, you could rely on your IDs always being in the first column, and the lat and long in the next two (this is a spreadsheet, after all!) and avoid the inner loop altogether:
if ( $row->Cell[0]->Data == $ids[$i] )
{
$latitudes[$i] = (string)$row->Cell[1]->Data;
$longitudes[$i] = (string)$row->Cell[2]->Data;
}
It looks like in case of this XML, cells are always in same order so it can be done as follows:
$ids = array('8', '53', '38');
foreach ($xml->Worksheet->Table->Row as $row) {
$children = $row->children();
if (count($children) == 3 && in_array(((string) $children[0]->Data), $ids)) {
echo 'lat: ' . $children[1]->Data . ' lng: ' . $children[2]->Data . "\n";
}
}
You can do it entirely in XPath, without any loops, like:
//Row[Cell/Data[. = '8' or . = '53' or . = '38']]/following-sibling::*[position() <= 2]
That search all rows with the id in any data element and then takes the next two siblings.
Or
//Row[Cell[1]/Data[. = '8' or . = '53' or . = '38']]/following-sibling::*[position() <= 2]
if it is sure that the id is always in the first cell. (which also prevents errors due to an id being the same as a longitude/langitude)
Or
//Row[Cell[#ss:Index = "2"]/Data[. = '8' or . = '53' or . = '38']]/following-sibling::*[position() <= 2]
if the id is in the cell with index 2.
But in all cases, you need to initialize the namespaces correctly
An alternative approach if you have lots of IDs to match is to create a "hash" of all the rows based on their ID, and then look into that hash rather than looping through searching for matches.
// Initialise an empty array to use as the hash
$rows_by_id = array();
// Loop over all the rows in the spreadsheet
foreach ($thePositions->Worksheet->Table->Row as $row) {
// Skip rows with less than 3 cells
if ( count($row->Cell) < 3 ) {
continue;
}
// Take the ID from the first cell on the row
$id = (string)$row->Cell[0]->Data;
// Add this row to the hash, identified by it's ID
$rows_by_id[$id] = array(
'latitude' => (string)$row->Cell[1]->Data,
'longitude' => (string)$row->Cell[2]->Data
);
// Or if your IDs are not unique, and you want all matches:
// $rows_by_id[$id][] = array( ... )
}
foreach ( $ids as $required_id ) {
// Retrieve the results from the hash
if ( isset($rows_by_id[$required_id]) ) {
$matched_row = $rows_by_id[$required_id];
echo "ID $required_id has latitude {$matched_row['latitude']} and longitude {$matched_row['longitude']}.\n";
}
else {
echo "ID $required_id was not matched. :(\n";
}
// If you have non-unique matches, you'll need something like this:
// $all_matched_rows = $rows_by_id[$required_id]; ... foreach ( $all_matched_rows as $matched_row )
}

Exporting defined elements from XML

I would like to export (or to keep) the subtags which are with the defined attribute in XML. As I don't know the name of this process, I can't find any relevant information about it on the net. And since it's hard to explain, I decided to put an examples for my issue.
Let's say, I have this XML file:
<results>
<result idSite="1">
<row>
<label>category</label>
<visits>2</visits>
<idsubdatatable>5</idsubdatatable>
<subtable>
<row>
<label>uncategorized</label>
<visits>2</visits>
<idsubdatatable>6</idsubdatatable>
<subtable>
<row>
<label>/index</label>
<visits>2</visits>
<url>http://mysite1.com/category/uncategorized/</url>
</row>
</subtable>
</row>
</subtable>
</row>
<row>
<label>about</label>
<visits>1</visits>
<idsubdatatable>7</idsubdatatable>
<subtable>
<row>
<label>/index</label>
<visits>1</visits>
<url>http://mysite1.com/about/</url>
</row>
</subtable>
</row>
</result>
<result idSite="2">
<row>
<label>/calendar</label>
<visitors>1</visitors>
<url>http://mysite2.com/calendar</url>
</row>
</result>
</results>
And I have to parse the results and keep only the rows which are with a <url> attribute. Like this:
After parsing I have to combine these rows in a new XML file, and the final result must be like this:
<result>
<row>
<label>/index</label>
<visits>2</visits>
<url>http://mysite1.com/category/uncategorized/</url>
</row>
<row>
<label>/index</label>
<visits>1</visits>
<url>http://mysite1.com/about/</url>
</row>
<row>
<label>/calendar</label>
<visitors>1</visitors>
<url>http://mysite2.com/calendar</url>
</row>
</result>
Generally I want to do this process in PHP but it maybe in other languages too.
So, if you have any idea to solve this problem, please comment.
I would use an xpath query to find all url nodes inside row nodes. Then, just append the parent node of each url element you find to a new DomDocument like so:
$xml = '...';
$dom = new DomDocument();
$dom->preserveWhiteSpace = FALSE;
$dom->loadXML($xml);
$new_dom = new DomDocument();
$result = $new_dom->createElement('result');
$new_dom->appendChild($result);
$xpath = new DOMXPath($dom);
$rows = $xpath->query('//row/url');
for ($i=0;$i<$rows->length;$i++) {
$node = $new_dom->importNode($rows->item($i)->parentNode, TRUE);
$result->appendChild($node);
}
$new_dom->formatOutput = TRUE;
echo $new_dom->saveXML();
I'd use simplexml to read as your input, so your parsing would be easy. And then, i'd create a recursive function such as:
function isUrlElement($element){
foreach($element->children() as $children){
if($children->getName() == 'url'){
return true;
}else{
isUrlElement($children);
}
}
}
Now this is far from complete, but you could make it recursive calling it for each children. When this returns true, you'd know you found a node that has URL as a children. Use that $element node to for example add it to an array of simplexmlelements and then just foreach it back into XML.
Does that make sense?

Categories