I am trying to build a hierarchical array in PHP, from relational database contents that are stored using a closure table. For a given result, I will have the full path to the LEAF
node, the below would look like my result set.
1~root~the root node
1~root~the root node>>>2~category1~First category
1~root~the root node>>>3~category2~Second category
1~root~the root node>>>2~category1~First category>>>4~subCatOfCategory1~SubCategory of Cat 1
Anyway, those are my database results. So I want to traverse them and build a hierarchical structure in PHP so I can convert it to JSON and render a tree in DOJO
So as I walk through each row, I am building a "path" to the leaf because I only need to add an element to tree when the element is a "leaf"... Along that thinking I decided that I would tokenize each result, using ">>>" as the delimiter, giving me the nodes that are in that row. Then I loop through those nodes, tokenizing each one by "~" which gives me the attributes of each node.
So, I have a for loop to process each ROW and it basically determines that if the node being processed is NOT a leaf, add it's ID to an array that is to track the path to get to the eventual leaf that will be processed. THEN, when I finally do arrive at the LEAF, I can call a function to insert a node, using the PATH that I've compiled along the way.
Hopefully that all makes sense.. so I've included the code below.. Consider the second result from above. When I have processed that entire result and am about to call the function insertNodeInTreeV2(), the arrays look as below...
$fullTree
is an array with 1 element, indexed at [1]
That element contains an array with four elements: ID(1)
, NAME(root)
, Description(the root node)
, CHILDREN(empty array)
$pathEntries
is an array with only one element, (1). That is to mean that the PATH to the LEAF node being inserted is by node [1], which is the root node.
$nodeToInsert
is an array with four elements: ID(2)
, NAME(category1)
, Description(First Category)
, CHILDREN(empty array)
$treeRootPattern
is a STRING that contains the Variable name I'm using to store the whole array/tree, which in this case is "fullTree".
private function insertNodeInTreeV2( array &$fullTree, array $pathEntries, array $nodeToInsert, $treeRootPattern )
{
$compiledPath = null;
foreach ( $pathEntries as $path ) {
$compiledPath .= $treeRootPattern . '[' . $path . '][\'CHILDREN\']';
}
// as this point $compiledPath = "fullTree[1]['CHILDREN']"
$treeVar = $$compiledPath;
}
So when I make the assignment, $treeVar = $$compiledPath;, I THINK I am setting the variable $treeVar to be equal to $fullTree[1]['CHILDREN'] (which I have verified in my debugger is a valid array index). Even if I paste the contents of $compiledPath into a new Expression in Eclipse debugger, it shows me an empty array, which makes sense because that is what's located in $fullTree[1]['CHILDREN']
But instead, the runtime is telling me the following error...
troller.php line 85 - Undefined variable: fullTree[1]['CHILDREN']
Any help on this would be greatly appreciated... And if you have a better way for me to get from the result set I described to the hierarchical array I'm trying to build, I'd be eager to adopt a better method.
UPDATED TO ADD THE CODE THAT CALLS THE ABOVE FUNCTION -- THE FOR LOOP PROCESSES ROWS OF DATABASE RESULTS, AS DESCRIBED ABOVE
foreach ( $ontologyEntries as $entry ) {
// iterating over rows of '1~~root~~The root node>>>2~~category1~~The first category
$nodes = explode( '>>>', $entry['path'] );
$numNodes = count( $nodes ) - 1 ;
$pathToNewNode = null; // this is the path, based on ID, to get to this *new* node
for ( $level = 0; $level <= $numNodes; $level++ ) {
// Parse the node out of the database search result
$thisNode = array(
'ID' => strtok($nodes[$level], '~~'), /* 1 */
'NAME' => strtok( '~~'), /* Root */
'DESCRIPTION' => strtok( '~~'), /* This is the root node */
'CHILDREN' => array()
);
if ( $level < $numNodes ) { // Not a leaf, add it to the pathToThisNodeArray
$pathToNewNode[] = $thisNode['ID'];
}
else {
// processing a leaf, add it to the array
$this->insertNodeInTreeV2( $$treeRootPattern, $pathToNewNode, $thisNode, $treeRootPattern );
}
}
}
See my comments below your question for an explanation.
$paths = array(
"1~root~the root node",
"1~root~the root node>>>2~category1~First category",
"1~root~the root node>>>3~category2~Second category",
"1~root~the root node>>>2~category1~First category>>>4~subCatOfCategory1~SubCategory of Cat 1"
);
$tree = array();
foreach ($paths as $path)
{
$currentNode = &$tree;
$parts = explode(">>>", $path);
foreach ($parts as $part)
{
$node = explode("~", $part);
// create all nodes along this path
if (!isset($currentNode[$node[0]]))
{
$currentNode[$node[0]] = array(
"ID" => $node[0],
"NAME" => $node[1],
"DESCRIPTION" => $node[2],
"CHILDREN" => array(),
);
}
$currentNode = &$currentNode[$node[0]]["CHILDREN"];
}
}
var_dump($tree);
Outputs:
array
1 =>
array
'ID' => string '1' (length=1)
'NAME' => string 'root' (length=4)
'DESCRIPTION' => string 'the root node' (length=13)
'CHILDREN' =>
array
2 =>
array
'ID' => string '2' (length=1)
'NAME' => string 'category1' (length=9)
'DESCRIPTION' => string 'First category' (length=14)
'CHILDREN' =>
array
4 =>
array
'ID' => string '4' (length=1)
'NAME' => string 'subCatOfCategory1' (length=17)
'DESCRIPTION' => string 'SubCategory of Cat 1' (length=20)
'CHILDREN' => &
array
empty
3 =>
array
'ID' => string '3' (length=1)
'NAME' => string 'category2' (length=9)
'DESCRIPTION' => string 'Second category' (length=15)
'CHILDREN' =>
array
empty
The loop will create all nodes that are included in a path, so you won't need to insert 1~root~the root node
, if you also insert 1~root~the root node>>>2~category1~First category
.
You can change this by only creating nodes if the node is the path's last node. The length of a path is count($parts)
and you can count which level you are in inside the inner foreach-loop.
I hope this is what you wanted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With