SimpleXML addChild() adding child node titles not values - php

When attempting to use SimpleXML's addChild() method towards the end of the code block below and printing the feed, the output is not the required result. When tested with a string value, addChild() correctly outputs both the name of the node and the value, however I'm not sure if the array or the object inside it are actually within the scope of the first foreach loop.
I want to access the 'name' and 'type' properties of the $nameType object and output them to my main feed with addChild().
//Turns base feed into SimpleXML object.
$feed = simplexml_load_string($xml);
foreach($feed->property as $property) {
//Gets latitude and longitude from each property.
$latitude = $property->latitude;
$longitude = $property->longitude;
//Adds latitude and longitude values into Google Places API URL.
$googleURL = 'https://maps.googleapis.com/maps/api/place/nearbysearch/xml?location='.$latitude.','.$longitude.'&radius=1000&types=train_station&key=my-google-key';
//Gets XML from Google Places and parses into SimpleXML Object.
$googleXMLfile = file_get_contents($googleURL);
$googleXMLdata = simplexml_load_string($googleXMLfile);
//Array for limiting number of results.
$googleStoredXMLDataArray = array();
foreach ($googleXMLdata->result as $result) {
//Assigns result 'name' and 'type' to variables.
$name = $result->name;
$type = $result->type;
//Creates object to store name and type together.
$nameType = new StdClass();
$nameType->name = $name;
$nameType->type = $type;
//Pushes object to array and outputs results limiting feed to 3 results.
array_push($googleStoredXMLDataArray, $nameType);
$output = array_slice($googleStoredXMLDataArray, 0, 3);
}
//Adding proximityTo destination to property nodes in the feed parser.
//Error - needs to pull properties from object inside the array.
$property->addChild('proximityTo_name', '$googleStoredXMLDataArray->$nameType->name');
$property->addChild('proximityTo_type', '$googleStoredXMLDataArray->$nameType->type');
}
print_r($feed);

Do not forget, $googleStoredXMLDataArrayis an indexed array of stdClass objects. Getting the value of these objects in the array is done with an index like this:
$googleStoredXMLDataArray[$index]->name;
$googleStoredXMLDataArray[$index]->type;
where $index is between 0 and count($googleStoredXMLDataArray)-1. I believe 0 is the most accurate hit and the last value the most inaccurate. To get the last value (most inaccurate), use this:
$i = count( $googleStoredXMLDataArray ) - 1;
$property->addChild('proximityTo_name', $googleStoredXMLDataArray[$i]->name);
$property->addChild('proximityTo_type', $googleStoredXMLDataArray[$i]->type);
Rewrite the last part should do the trick of adding ALL proximity values:
foreach( $googleStoredXMLDataArray as $prox ) {
$property->addChild('proximityTo_name', $prox->name);
$property->addChild('proximityTo_type', $prox->type);
}
If you want to skip the XML parsing, and just parse an array of data you can always use JSON instead:
$googleURL = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json'
. '?location='.$latitude.',' . $longitude
. '&radius=1000&types=train_station&key=my-google-key';
$googleObject = json_decode( file_get_contents($googleURL), true );

Related

Complex Loop through a complex SimpleXMLElement

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;
}

PHP iterate through sequental key values

I am grabbing Pokemon data from a JSON API, that is showing values for any Pokemon. The aim here is to grab data that shows what games a Pokemon can be found in and which areas in the game they are found located.
https://pokeapi.co/api/v2/pokemon/183/
This is accessed via its encounters key value. I am particularly interested in accessing via :
['location_area']['name'] and ['version']['name']
As a good URL example: https://pokeapi.co/api/v2/pokemon/183/encounters
Problem is, the encounters array index value is a URL, which I am accessing through my own Curl json function, which works fine, but will only give me array numeric index values.
public function getPokemonLocation($searchTerm)
{
/*
Get the Pokemon via search term name
*/
$url = $this->baseUrl.$searchTerm;
$pokemonLocation = $this->getUrl($url);
/*
$pokemonLocationUrl is the encounters subset i the array URL
*/
$pokemonLocationUrl = $pokemonLocation['location_area_encounters'];
$pokemonLocationEncounters = $this->getUrl('https://pokeapi.co/' . $pokemonLocationUrl);
echo "<pre>";
print_r($pokemonLocationEncounters);
echo "</pre>";
/*
Now grab the data needed from the name of location and what game regions they are from
*/
$pokemonAreaEncounterArea = array();
$pokemonAreaEncounterGame = array();
foreach($pokemonLocationEncounters as $key => $encounter)
{
$pokemonAreaEncounterArea[] = $encounter['location_area']['name'];
$pokemonAreaEncounterGame[] = $encounter['version_details'][0]['version']['name'];
}
$pokemonLocationAndRegion = array_combine($pokemonAreaEncounterGame,$pokemonAreaEncounterArea);
// print_r($pokemonLocationAndRegion);
return $pokemonLocationAndRegion;
}
Problem is, the encounters array index value is a URL, which I am accessing through my own Curl json function, which works fine, but will only give me array numeric index values.
I can easily get data, as shown above . Howwever would like to fully access all of the array indexes, not just one or a handful of data. Tempting to do this via multiple foreach, but would rather implement it cleanly.
EDIT: Here is 2nd example of Poke APi link above, if you get a 504 server Error - https://jsoneditoronline.org/?id=73911ec678c850f9b1ff131ac7ee738c
According to your json which is returning an index based array, this is how your code should be to work.
public function getPokemonLocation($searchTerm) {
/*
Get the Pokemon via search term name
*/
$url = $this->baseUrl . $searchTerm;
$pokemonLocation = $this->getUrl($url);
/*
$pokemonLocationUrl is the encounters subset i the array URL
*/
$pokemonLocationUrl = $pokemonLocation['location_area_encounters'];
$pokemonLocationEncounters = $this->getUrl('https://pokeapi.co/' . $pokemonLocationUrl);
echo "<pre>";
print_r($pokemonLocationEncounters);
echo "</pre>";
$pokemonAreaEncounterArea = array();
$pokemonAreaEncounterGame = array();
/*
Now grab the data needed from the name of location and what game regions they are from
*/
for($i=0,$size=count($pokemonLocation);$i<$size;$i++) {
$pokemonAreaEncounterArea[] = $pokemonLocation[$i]['location_area']['name'];
$pokemonAreaEncounterGame[] = $pokemonLocation[$i]['version_details'][0]['version']['name'];
}
$pokemonLocationAndRegion = array_combine($pokemonAreaEncounterGame, $pokemonAreaEncounterArea);
// print_r($pokemonLocationAndRegion);
return $pokemonLocationAndRegion;
}
to make it clearer
for($i=0,$size=count($pokemonLocation);$i<$size;$i++) {
$pokemonAreaEncounterArea[] = $pokemonLocation[$i]['location_area']['name'];
$pokemonAreaEncounterGame[] = $pokemonLocation[$i]['version_details'][0]['version']['name'];
}
this will allow you to traverse all elements in the result array, and put them on the same index in respective arrays of $pokemonAreaEncounterArea and $pokemonAreaEncounterGame
You can combine the target data as you iterate like this:
$array=json_decode($json,true);
foreach($array as $item){
$result[$item['version_details'][0]['version']['name']]=$item['location_area']['name'];
}
var_export($result);
Output:
array (
'diamond' => 'great-marsh-area-6',
'platinum' => 'sinnoh-route-215-area',
'heartgold' => 'mt-mortar-1f',
'ruby' => 'hoenn-route-120-area',
'emerald' => 'hoenn-safari-zone-expansion-south',
'leafgreen' => 'four-island-area',
'black-2' => 'route-22-area',
'x' => 'kalos-route-3-area',
)

Edit SimpleXML node in a foreach loop

I'm trying to edit a node value while in a loop. I can edit nodes with unique names just fine.
$gdNodes->orgName = 'test';
But when I'm in a loop, the value is not saved when I output my XML.
foreach($gdNodes->phoneNumber as $phone)
{
$phone = '1234567';
}
Both are SimpleXMLElement class objects. I don't understand why it's not saving. How is it done?
It won't save because $phone is a scalar copy of the original value.
You should be able to reach your goal like this:
foreach($gdNodes->phoneNumber as $key => $phone)
{
$gdNodes->phoneNumber[$key] = '1234567';
}

store a node into (object) into an array

I've this piece of code, it retrieves a node from my node type 'Student'.
I try different things to, save all values in '$node' into an array. But $node is a object.
My question is how doe I store 'all' values in $node into a array. In Java an C# it's simpler to do that.
$results = db_query(db_rewrite_sql("SELECT nid FROM {node} WHERE type =
'student'"));
while($nid = db_result($results)) {
$node = node_load($nid);
// Do something with $node
}
in java or c# you can say in a for/foreach/while 'loop'
String item[] = null;
for(int i = 0; i<=myNode; i++) {
item.add(myNode[i]); // the item and value UgentID, name student, location student are been stored in item array.
}
I don't know if PHP has that too. "yourObject.Add(otherObject)"
The following let's your object behave (access-wise) as an array:
$result = new ArrayObject( $node );
If you truly want an array, simply cast it afterwards:
$result = (array) $result;
Heck, come to think of it, you could even simply do:
$result = (array) $node;
:-)
Both methods of casting to array will actually expose protected/private properties as well, I just found out. :-S Horrible.
edit:
// initiate array
$nodes = array();
while($nid = db_result($results)) {
// either do one of the following, to push
$nodes[] = node_load($nid);
// or:
array_push( $nodes, node_load($nid) );
// Do something with $node
}

iterating over unknown XML structure with PHP (DOM)

I want to write a function that parses a (theoretically) unknown XML data structure into an equivalent PHP array.
Here is my sample XML:
<?xml version="1.0" encoding="UTF-8"?>
<content>
<title>Sample Text</title>
<introduction>
<paragraph>This is some rudimentary text</paragraph>
</introduction>
<description>
<paragraph>Here is some more text</paragraph>
<paragraph>Even MORE text</paragraph>
<sub_section>
<sub_para>This is a smaller, sub paragraph</sub_para>
<sub_para>This is another smaller, sub paragraph</sub_para>
</sub_section>
</description>
</content>
I modified this DOM iterating function from devarticles:
$data = 'path/to/xmldoc.xml';
$xmlDoc = new DOMDocument(); #create a DOM element
$xmlDoc->load( $data ); #load data into the element
$xmlRoot = $xmlDoc->firstChild; #establish root
function xml2array($node)
{
if ($node->hasChildNodes())
{
$subNodes = $node->childNodes;
foreach ($subNodes as $subNode)
{
#filter node types
if (($subNode->nodeType != 3) || (($subNode->nodeType == 3)))
{
$arraydata[$subNode->nodeName]=$subNode->nodeValue;
}
xml2array($subNode);
}
}
return $arraydata;
}
//The getNodesInfo function call
$xmlarray = xml2array($xmlRoot);
// print the output - with a little bit of formatting for ease of use...
foreach($xmlarray as $xkey)
{
echo"$xkey<br/><br/>";
}
Now, because of the way I'm passing the elements to the array I'm overwriting any elements that share a node name (since I ideally want to give the keys the same names as their originating nodes). My recursion isn't great... However, even if I empty the brackets - the second tier of nodes are still coming in as values on the first tier (see the text of the description node).
Anyone got any ideas how I can better construct this?
You might be better off just snagging some code off the net
http://www.bin-co.com/php/scripts/xml2array/
/**
* xml2array() will convert the given XML text to an array in the XML structure.
* Link: http://www.bin-co.com/php/scripts/xml2array/
* Arguments : $contents - The XML text
* $get_attributes - 1 or 0. If this is 1 the function will get the attributes as well as the tag values - this results in a different array structure in the return value.
* $priority - Can be 'tag' or 'attribute'. This will change the way the resulting array sturcture. For 'tag', the tags are given more importance.
* Return: The parsed XML in an array form. Use print_r() to see the resulting array structure.
* Examples: $array = xml2array(file_get_contents('feed.xml'));
* $array = xml2array(file_get_contents('feed.xml', 1, 'attribute'));
*/
function xml2array($contents, $get_attributes=1, $priority = 'tag') {
You might be interested in SimpleXML or xml_parse_into_struct.
$arraydata is neither passed to subsequent calls to xml2array() nor is the return value used, so yes "My recursion isn't great..." is true ;-)
To append a new element to an existing array you can use empty square brackets, $arr[] = 123; $arr[$x][] = 123;
You might also want to check out XML Unserializer
http://pear.php.net/package/XML_Serializer/redirected

Categories