I have an XML which is a catalog containing several range
nodes which in turn contain several item
nodes:
<cat>
<range>
<item>
<a></a>
<b></b>
<c></c>
</item>
<item>
<a></a>
<b></b>
<c></c>
</item>
<item>
<a></a>
<b></b>
<c></c>
</item>
<item>
<a></a>
<b></b>
<c></c>
</item>
<!-- node 'a' need to be added here -->
<b></b>
<d></d>
</range>
<range>
<item>
<a></a>
<b></b>
<c></c>
</item>
<item>
<a></a>
<b></b>
<c></c>
</item>
<!-- node 'a' need to be added here -->
<b></b>
<d></d>
</range>
</cat>
Now I need to add node a
inside each range just after ALL item
nodes and just before node b
.
NOTE: nodes a
& b
inside and outside item
is the same
I was able to add the b
node inside range
, like this:
foreach($dom->getElementsByTagname('range') as $range) {
forearch ($range->getElementsByTagName('d') as $d) {
$b = $dom->createElement('b');
$d = $d->parentNode->insertBefore($b, $d);
}
}
If I use the same code to try and add node a
outside all the item
nodes, it won't work since the reference node b
is the same inside and outside the item
nodes; so it will just add node a
inside the item
nodes.
I found an insertAfter
function;
function insertAfter(\DOMNode $newNode, \DOMNode $referenceNode)
{
if($referenceNode->nextSibling === null) {
return $referenceNode->parentNode->appendChild($newNode);
} else {
return $referenceNode->parentNode->insertBefore($newNode, $referenceNode->nextSibling);
}
}
but it will not give me the desired result since if I used item
as the reference node, it will add the new node just after the first item
whereas I need it after all the item
nodes.
The easiest way to achieve what you are trying to do is by using xPath.
$xpath = new DOMXpath($dom);
$bs = $xpath->query("//range/b");
$a = new DOMElement('a');
foreach ($bs as $b) {
$b->parentNode->insertBefore($a, $b);
}
This gets alls the "b" that are a direct child of range but not its grandchildren.
Getting the first level children compare until find the one you want ($b
) and insert before it:
foreach($dom->getElementsByTagname('range') as $range) {
foreach($range->childNodes as $first_level_child) {
if($first_level_child->tagname=='b') {
$a = $dom->createElement('a');
$first_level_child->parentNode->insertBefore($a, $first_level_child);
}
}
}