Reputation: 4051
Here's the XML file i'm working on:
<list>
<activity>swimming</activity>
<activity>running</activity>
<activity>soccer</activity>
</list>
The index.php, page that shows the list of activities with checkboxes, a button to delete the checked activities, and a field to add new activities:
<html>
<head></head>
<body>
<?php
$xmldoc = new DOMDocument();
$xmldoc->load('sample.xml', LIBXML_NOBLANKS);
$count = 0;
$activities = $xmldoc->firstChild->firstChild;
//prints the list of activities, with checkboxes on the left for each item
//the $count variable is the id to each entry
if($activities!=null){
echo '<form name=\'erase\' action=\'delete.php\' method=\'post\'>' . "\n";
while($activities!=null){
$count++;
echo " <input type=\"checkbox\" name=\"activity[]\" value=\"$count\"/>";
echo ' '.$activities->textContent.'<br/>'."\n";
$activities = $activities->nextSibling;
}
echo ' <input type=\'submit\' value=\'erase selected\'>';
echo '</form>';
}
?>
//section used for inserting new entries. this feature is working as expected.
<form name='input' action='insert.php' method='post'>
insert activity:
<input type='text name='activity'/>
<input type='submit' value='send'/>
<br/>
</form>
</body>
</html>
the delete.php, which is not working as expected:
<?php
$xmldoc = new DOMDocument();
$xmldoc->load('sample.xml', LIBXML_NOBLANKS);
$atvID = $_POST['activity'];
foreach($atvID as $id){
$delnode = $xmldoc->getElementsByTagName('activity');
$xmldoc->firstChild->removeChild($delnode->item($id));
}
$xmldoc->save('sample.xml');
?>
I've tested the deletion routine without the loop, using an hard-coded arbitrary id, and it worked. I also tested the $atvID array, and it printed the selected id numbers correctly. When it is inside the loop, here's the error it outputs:
Catchable fatal error: Argument 1 passed to DOMNode::removeChild() must be an instance of DOMNode, null given in /directorypath/delete.php on line 9
What is wrong with my code?
Upvotes: 2
Views: 6756
Reputation: 21
The tricky thing about DOMNodeLists is that they are NOT arrays. If you delete a node, the list will re-index. This will cause your code to break if a user selects more than one item for deletion. If you selected swimming and running, swimming and soccer would be deleted.
You might want to start by giving each activity a unique identifier you can search for, say an attribute called 'id' (this likely wouldn't be a real ID. The DOM's getElementByID() only works for XML that has a DTD, like an HTML page. I'm guessing you don't want to go there.)
You might update you're XML to look like this.
<list>
<activity name="swimming">swimming</activity>
<activity name="running">running</activity>
<activity name="soccer">soccer</activity>
</list>
You'd use these name attributes instead of $count as the value inside your checkboxes.
You can then use xPath to find items to remove inside your foreach.
$xpath = new DOMXPath($xmldoc);
$xmldoc->firstChild->removeChild($xpath->query("/list/activity[@name='$id']")->item(0));
Hope this helps get you started.
Upvotes: 1
Reputation: 691
In addition to moving $count++ to the end of the while loop, it might be a good idea to have delete.php check to make sure the $_POST['activity'] is numeric and within the specified range, just to ensure there are no fatal error messages generated on your page.
Upvotes: 1
Reputation: 2534
the DOMNodeList items are indexed starting with 0; You need to move the $count++ to the end of the while loop in the output step.
Upvotes: 2