Reputation: 984
I have an array like this one:
SimpleXMLElement Object
(
[BrowseNodes] => SimpleXMLElement Object
(
[BrowseNode] => SimpleXMLElement Object
(
[BrowseNodeId] => 969391031
[Name] => Bambine e ragazze
[Children] => SimpleXMLElement Object
(
[BrowseNode] => Array
(
[0] => SimpleXMLElement Object
(
[BrowseNodeId] => 969394031
[Name] => Calze
)
[1] => SimpleXMLElement Object
(
[BrowseNodeId] => 3635837031
[Name] => Felpe
)
[2] => SimpleXMLElement Object
(
[BrowseNodeId] => 3635838031
[Name] => Giacche
)
[3] => SimpleXMLElement Object
(
[BrowseNodeId] => 3635839031
[Name] => Guanti da giocatore
)
[4] => SimpleXMLElement Object
(
[BrowseNodeId] => 969392031
[Name] => Maglie
)
[5] => SimpleXMLElement Object
(
[BrowseNodeId] => 4351854031
[Name] => Maglie per tifosi
)
[6] => SimpleXMLElement Object
(
[BrowseNodeId] => 3635840031
[Name] => Magliette da portiere
)
[7] => SimpleXMLElement Object
(
[BrowseNodeId] => 969393031
[Name] => Pantaloncini
)
[8] => SimpleXMLElement Object
(
[BrowseNodeId] => 3635841031
[Name] => Pantaloncini da portiere
)
[9] => SimpleXMLElement Object
(
[BrowseNodeId] => 3635842031
[Name] => Pantaloni
)
[10] => SimpleXMLElement Object
(
[BrowseNodeId] => 3635843031
[Name] => Tute da ginnastica
)
)
)
[Ancestors] => SimpleXMLElement Object
(
[BrowseNode] => SimpleXMLElement Object
(
[BrowseNodeId] => 969386031
[Name] => Abbigliamento
[Ancestors] => SimpleXMLElement Object
(
[BrowseNode] => SimpleXMLElement Object
(
[BrowseNodeId] => 937258031
[Name] => Calcio
[Ancestors] => SimpleXMLElement Object
(
[BrowseNode] => SimpleXMLElement Object
(
[BrowseNodeId] => 524013031
[Name] => Categorie
[IsCategoryRoot] => 1
[Ancestors] => SimpleXMLElement Object
(
[BrowseNode] => SimpleXMLElement Object
(
[BrowseNodeId] => 524012031
[Name] => Sport e tempo libero
)
)
)
)
)
)
)
)
)
)
)
What I need to do is to build a breadcrumb using Anchestors. The one that is at the end of the list should be the first one. So, as an example:
Sport e tempo libero > Categorie > calcio...
I'm trying to iterate the xml with a function in this way without success:
$rec=$result->BrowseNodes->BrowseNode->Ancestors->BrowseNode;
$bread=array();
function recursive($r)
{
do{
$bread[]=$r->BrowseNodeId;
recursive($r->Ancestors->BrowseNode);
}while(isset($r->Ancestors));
$bread=array_reverse($bread);
return $bread;
}
print_r(recursive($rec));
I found something similar on stackoverflow but no suggestions helped me to sort this out.
Upvotes: 11
Views: 8650
Reputation: 488
Alternatively you can use $_SESSION for getting data from recursion
function start(){
$_SESSION['new1']=[];
$this->recursive($array);
$newVal = $_SESSION['new1'];
$_SESSION['new1']=[];
return $newVal;
}
function recursive($array){
...
$_SESSION['new1'][] = $val;
...
}
Upvotes: -2
Reputation: 6363
To create a recursive function with an output, you need three things:
$r
in your code.
You got this one right.$bread
, but it is not holding any value because it is empty every time you call recursive()
.
A simple solution is to declare it as global
inside the function.if
statement that checks the stop condition, which you don't have.
Instead, you have a do-while
loop in your code.So, you have two mistakes there. Based on your code and modifying it as little as possible, this is the correct code:
$rec = $result->BrowseNodes->BrowseNode->Ancestors->BrowseNode;
$bread = array();
function recursive($r)
{
global $bread;
$bread[] = strval($r->BrowseNodeId);
if(isset($r->Ancestors)){
return recursive($r->Ancestors->BrowseNode);
}else{
return array_reverse($bread);
}
}
print_r(recursive($rec));
There you go.
Update: I agree with @FlameStorm, global
should be avoided if possible.
I also received suggestion to use static
instead, but it introduces a bug.
Thus, I recommend avoiding static
as well if you're not sure how to use it.
This is the improved code:
$rec = $result->BrowseNodes->BrowseNode->Ancestors->BrowseNode;
function recursive($r)
{
if(isset($r->Ancestors))
$bread = recursive($r->Ancestors->BrowseNode);
$bread[] = strval($r->BrowseNodeId);
return $bread;
}
print_r(recursive($rec));
The $bread
variable outside the function is no longer needed.
Also, neither global
or static
is used.
Upvotes: 11
Reputation: 69611
SimpleXMLElement
is not an array. You could convert it to an array, but PHP offers an iterator specifically for this case, SimpleXMLIterator
.
Since you have a recursive structure, my suggestion is to flatten it with RecursiveIteratorIterator
. Assuming your data is in a variable called $xml
, your solution might look something like this:
$xmlIterator = new SimpleXMLIterator($xml->Ancestors);
$flatIterator = new RecursiveIteratorIterator($xmlIterator, RecursiveIteratorIterator::SELF_FIRST);
$breadcrumb = [];
foreach($flatIterator as $node) {
$breadcrumb[] = $node['Name'];
}
$breadcrumb = array_reverse($breadcrumb);
Upvotes: 4
Reputation: 1004
Fixed one answer above - you should avoid global
if you can.
So, the code will be
$rec = $result->BrowseNodes->BrowseNode->Ancestors->BrowseNode;
function recursive($r)
{
$breads = [strval($r->BrowseNodeId)];
if (isset($r->Ancestors)) {
$breads = array_merge(recursive($r->Ancestors->BrowseNode), $breads);
}
return $breads;
}
$breadcrumbs = recursive($rec); // You got it.
print_r($breadcrumbs);
Upvotes: 0
Reputation: 1537
<?php
$sxe = new SimpleXMLElement("BrowseNodes.xml", NULL, TRUE);
// prepare a String for the SimpleXMLIterator.
// The SimpleXMLIterator expects:
// "A well-formed XML string or the path or URL to an XML document"
// therefore get the xml-string by calling asXML() on the
$partitialXMLString = $sxe->BrowseNodes->BrowseNode->Ancestors->asXML();
$recursiveIterator = new RecursiveIteratorIterator(
new SimpleXMLIterator($partitialXMLString),
RecursiveIteratorIterator::CHILD_FIRST
);
// if you need only the names
$name = array();
// if you need the links to something
$link = array();
$baseUrl = "http://example.com/BrowseNodeId/";
// if you need just the nodes in an array, and create the output later from it
// $xmlNode = array()
foreach($recursiveIterator as $node) {
if (false == empty($node->Name)){
$name[] = (string) $node->Name;
$link[] = "<a href='" . $baseUrl . $node->BrowseNodeId . "'>" . $node->Name . "</a>\n";
// for later processing
// $xmlNode[] = $node;
}
}
// Add the top BrowseNode->Name, from the node, where the First "Ancestors" is.
// This could be done also in the loop, when looping over all elements,
// by adding conditions in order to differentiate between Children and Ancestors
// But this is more readable, and for your structure should be enough.
$firstNode = $sxe->BrowseNodes->BrowseNode;
$name[] = $firstNode->Name;
$link[] = "<a href='" . $baseUrl . $firstNode->BrowseNodeId . "'>" . $firstNode->Name . "</a>\n";
// $xmlNode[] = $firstNode;
//output the path (child first)
// > is >
echo implode(' > ', $name);
echo "<br>\n";
//output the links (child first)
echo implode(' > ', $link);
The output in browser:
Sport e tempo libero > Categorie > Calcio > Abbigliamento > Bambine e ragazze
Sport e tempo libero > Categorie > Calcio > Abbigliamento > Bambine e ragazze
( the second line with links)
The generated html code:
Sport e tempo libero > Categorie > Calcio > Abbigliamento > Bambine e ragazze<br>
<a href='http://example.com/BrowseNodeId/524012031'>Sport e tempo libero</a>
> <a href='http://example.com/BrowseNodeId/524013031'>Categorie</a>
> <a href='http://example.com/BrowseNodeId/937258031'>Calcio</a>
> <a href='http://example.com/BrowseNodeId/969386031'>Abbigliamento</a>
> <a href='http://example.com/BrowseNodeId/969391031'>Bambine e ragazze</a>
Upvotes: 0