I have created a PHP script which builds a table from an XML document via AJAX. For example:
<bookstore>
<book>
<title>Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>
Would create a table with title, author, year and price columns plus an additional delete colum. When parsing the XML I have set the tr id to be that of the current XML element (0 and 1) in the case above.
When I click delete I fire off an AJAX request with the ID of the row I want to delete. The delete script is receiving the current row number without problem but I am having strange results when trying to delete it. The current code I’m trying is below (taken from http://quest4knowledge.wordpress.com/2010/09/04/php-xml-create-add-edit-modify-using-dom-simplexml-xpath/ 7.2)
if (isset($_POST['rowNumber'])) {
$rowNumber = $_POST['rowNumber'];
$file = $_POST['file'];
$dom = new DOMDocument();
$dom->load("../XML/".$file);
$xml = $dom->documentElement;
//PROBLEM HERE
$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item($rowNumber));
$handle = fopen("../XML/".$file, 'w');
fwrite($handle, $dom->saveXML());
}
I build the table on page load and then on every deletion. Trouble is that the incorrect rows are being deleted and I can’t figure out why.
ADDITIONAL TESTING…
Deletes the first node on 50% of clicks:
$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(0));
Always deletes the first node:
$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(1));
Deletes the second node on 50% of clicks:
$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(2));
Always deletes the second node:
$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(3));
Deletes the third node on 50% of clicks:
$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(4));
Always deletes the third node:
$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(5));
ADDED MY AJAX CODE
$('#generatedTable a.delete').live('click', function (e) {
e.preventDefault();
//TABLE ROW ID TO BE DELETED. CAN ALERT THIS FINE.
var trID = $(this).closest('tr').attr('id');
$.ajax({
url: "functions/xmlDelete.php",
type: "POST",
dataType: "json",
data: "rowNumber="+ trID + "&fileName=" + fileName,
success: function(data) {
$.ajax({
url: "functions/xmlParser.php",
type: "POST",
dataType: "json",
data: "fileName="+ fileName,
success: function(data) {
$('#xmlTable').html(data.table);
$('#xmlTable').fadeIn('fast');
oTable = $('#generatedTable').dataTable({
"bJQueryUI": true,
"bPaginate": false,
"bLengthChange": false,
"bFilter": false,
"bSort": false,
"bInfo": true
});
}
});
}
});
} );
This is what my table rows look like and I can alert the trID without problem.
<tr id="0" class="odd">
<td id="0">1999 Grammy Nominees</td>
<td id="1">Many</td>
<td id="2">USA</td>
<td id="3">Grammy</td>
<td id="4">10.20</td>
<td id="5">1999</td>
<td align="center"><a class="edit" href="">Edit</a></td>
<td align="center"><a class="delete" href="">Delete</a></td>
</tr>
Can anyone help explain what I’m seeing here. Thanks!
You are not taking into account that
childNodes()includes all nodes, not just elements.For the xml document that you provide,
$dom->documentElement->childNodes->item(0)is the whitespace between the end of<bookstore>and the beginning of<book>, not the first<book>node.Now you know why the DOM is so irritating.
I suggest you use either
DOMXPathorSimpleXMLrather than loop through thechildNodesto collect Element indexes.DOMXPathsolutionSimpleXMLsolutionProblems
However, there are two serious problems with your whole approach.
You are accepting untrusted input through the
filepost variable. There is nothing a user from reading and writing to any XML file on your drive. You should have a predefined list of validfilevalues that$_POST['file']must match before you will process the request.You do not account for concurrency. This manifests itself in two ways:
link()to the real file name), or you must open the file withflock()or some other locking mechanism.Because managing concurrent reads and writes (especially in a speedy way) is so difficult, I recommend you abandon this file-as-a-database approach and use a real database instead.
(And by the way, your HTML is invalid.
idattributes must be unique within a document, but you have twoid="0".)