I need to save some values from XML.
First step - I get the structure:
$xml = $dom_xml->saveXML();
$xml_ = new \SimpleXMLElement($xml);
dd($xml_);
Here TextFrame
has 8 arrays. Each of them has PathPointType
, which has 4 more arrays with 3 attributes each. And these attributes I need from each TextFrame
.
I can get, for instance, Anchor
value doing this:
$res = $xml_
->Spread
->TextFrame
->Properties
->PathGeometry
->GeometryPathType
->PathPointArray
->PathPointType
->attributes();
dd($res['Anchor']);
(BTW: is there more prettier way to get it?)
But the question is - how is it possible to loop through all arrays and save values separately for each array?
I assume here has to be a multidimensional foreach
loop in conjunction with for
loop?
Or is better to achieve it using DOMDocument
?
As it looks as though you are starting off with DOMDocument (as you are using $dom_xml->saveXML()
to generate the XML), it may be easier to continue using it and it also has some easy features for getting the details your after.
Using getElementsByTagName()
allows you to get a list of the elements with a specific tag name from a start point, so starting with $dom_xml
, get all of the <TextFrame>
elements. Then foreach()
over this list and using this element as a start point, use getElementsByTagName("PathPointType")
to get the nested <PathPointType>
elements. At this point you can then use getAttribute("Anchor")
for each of the attributes you need from the <PathPointType>
elements...
$textFrames = $dom_xml->getElementsByTagName("TextFrame");
foreach ( $textFrames as $frame ) {
$pathPointTypes = $frame->getElementsByTagName("PathPointType");
foreach ( $pathPointTypes as $type ) {
echo $type->getAttribute("Anchor").PHP_EOL;
}
}
Edit
You can extend the code to build an array of frames and then the anchors within that. This code also stores the anchor in an associative array so that if you add the other attributes, you can add them here (or remove it if you don't need another layer of detail)...
$frames =[];
foreach ( $textFrames as $frame ) {
$anchors = [];
$pathPointTypes = $frame->getElementsByTagName("PathPointType");
foreach ( $pathPointTypes as $type ) {
$anchors[] = ['Anchor' => $type->getAttribute("Anchor")];
}
$frames[] = $anchors;
}
Also if you have some way of identifying the frames, you could create an associative array at that level as well...
$frames[$frameID] = $anchors;
As a complement to the existing answer from Nigel Ren, I thought I'd show how the same loops look with SimpleXML.
Firstly, note that you don't need to convert the XML to string and back if you want to switch between DOM and SimpleXML for any reason, you can use simplexml_import_dom which just swaps out the interface:
$sxml = simplexml_import_dom($dom_xml);
Next we need our TextFrame
elements; we could either step through the structure explicitly, as you had before:
$textFrames = $sxml->Spread->TextFrame;
Or we could use XPath to search for matching tag names within our current node (.
is the current element, and //
means "any descendant":
$textFrames = $sxml->xpath('.//TextFrame');
The first will give you a SimpleXMLElement
object, and the second an array, but either way, you can use foreach
to go through the matches.
This time we definitely want an XPath expression to get the PathPointType
nodes, to avoid all the nested loops through levels we're not that interested in:
foreach ( $textFrames as $frame ) {
$pathPointTypes = $frame->xpath('.//PathPointType');
foreach ( $pathPointTypes as $type ) {
echo $type['Anchor'] . PHP_EOL;
}
}
Note that you don't need to call $type->attributes()
; unless you're dealing with namespaces, all you need to get an attribute is $node['AttributeName']
. Beware that attributes in SimpleXML are objects though, so you'll often want to force them to be strings with (string)$node['AttributeName']
.
To take the final example, you might then have something like this:
$frames = [];
foreach ( $sxml->Spread->TextFrame as $frame ) {
$anchors = [];
$pathPointTypes = $frame->xpath('.//PathPointType');
foreach ( $pathPointTypes as $type ) {
$anchors[] = ['Anchor' => (string)$type['Anchor']];
}
$frames[] = $anchors;
}