Symfony/Sonata get next child (Sibling) - php

I'm looking for the cleanest way to get the next child sibling of an object (next child of the parent).
-- Parent Object
-- Child 1
-- Child 2 (<== Current object)
-- Child 3 (<== Required object)
-- Child 4
Let assume in this example that we are talking about pages (Sonata pages). Currently I have Page 2 (Child 2), and need the next page of the same parent (in this case child 3). In the case I have the last page (child 4), then I need the first child again.
One option would be to request the parent, then request all the childs, loop over all the childs and look for the current child. Then take the next child, or the first one in case there is no next one. But this seems like a lot of code, with a lot of ugly if logic and loops. So I'm wondering if there is some sort of pattern to solve this.

Eventually I came up with the next solution:
/**
* $siblings is an array containing all pages with the same parent.
* So it also includes the current page.
* First check if there are siblings: Check if the parent has more then 1 child
**/
if (count($siblings) != 1) {
// Find the current page in the array
for ($i = 0; $i < count($siblings); $i++) {
// If we're not at the end of the array: Return next sibling
if ($siblings{$i}->getId() === $page->getId() && $i+1 != count($siblings)) {
return $siblings{$i+1};
}
// If we're at the end: Return first sibling
if ($siblings{$i}->getId() === $page->getId() && $i+1 == count($siblings)) {
return $siblings{0};
}
}
}
This seems like a quite clean solution to tackle this problem. We don't have an excessive amount of loops and if logic, however the code is still readable.

Related

Doctrine Tree getting disorganized after changing child position

When I create three-levels nested tree ( with only three entities ) that looks like this:
1 (lft 1, rgt:6)
-2 (lft 2, rgt:5)
-3 (lft 3, rgt:4)
and then I try to move node ( with id=3, i.e. ) from the third level to the second level as, let's say second child with this piece of code:
/* this line can be commented - it doesn't work with it either */ $chapter->setParent($parentEntity);
$repo->persistAsFirstChildOf($chapter, $parentEntity);
$repo->moveDown($chapter, 1);
As a result I got the tree that goes like this:
1 ( lft:-4, rgt:6 )
-3 (lft: 5, rgt:6)
-2 (lft 7, rgt:5)
instead of this:
1 (lft 1, rgt:6)
-2 (lft 2, rgt:5)
-3 (lft 3, rgt:4)
and child which should become second in order becomes first. As You can see, lft values aren't proper. Am I missing something?
You should update node and set new parent by gedmo TreeListener (get it by NestedTreeRepository->listener) :
<?php
class YourNestedTreeRepository extends NestedTreeRepository
.......
/**
* #param Node $node
* #param Node $newParent
*
* #return void
*/
public function setNewParent($node, $newParent)
{
$meta = $this->getClassMetadata();
$this->listener
->getStrategy($this->_em, $meta->name)
->updateNode($this->_em, $node, $newParent)
;
}
and then, anywhere in your code:
//set as first child of a new parent - Tree hierarchy, it doesn't touch ORM relation
$repo->setNewParent($node, $newParent);
//set new parent and save. It updates ORM relation only, doesn't touch Tree hierarchy
$node->setParent($newParent);
$entityProvider->save($node); // or $entityManager->flush()
//additionaly move it down
if ($yourCondition) {
$result = $repo->moveDown($node, $position);
}
You actually are OK there.
I think left/right values does not matter there since your order is still ok.
Your probleme is that you call
$repo->moveDown($chapter, 1);
This function will make your $chapter to move at next position (second one in your case).
Delete the call to moveDown and try it again.
As far as lft and rgt attributes are concerned, adding/removing node may recompute them.
If it's a real matter to you, then try calling (i'm not sur about this):
$repo->recover();
$em->flush(); // ensures cache clean
Actually guys, I've chosen bad tree type. Don't use nested when You want to change children position often.

PHP internal array pointer issue with variable passed by reference in recursive function with foreach

I'm trying to build a hierarchical associative array with parent-child relationships. The input data comes in as an array of elements. Each element has an id and a parent-id.
For efficiency, when an element finds its parent I want to remove that child element from source data so cycles aren't wasted checking if that element is a child of other elements.
Here is the code:
// parentid == 0 indicates a root node
function buildTree(&$elements, $parentid=0){
$branch = array();
foreach($elements as $key => $element){
if($element['parentid'] == $parentid){
$twig = $element;
unset($elements[$key]);
$twig['children'] = buildTree($elements, $element['pageid']);
$branch[] = $twig;
}
}
return $branch;
}
The problem I'm having is that in certain cases the some elements never find their parent.
I think this is happening because the $elements variable is being passed by reference and deeper levels of recursion are corrupting the position of the internal array pointer for the outer level, causing other children to be skipped over and not touched in the outer level's foreach loop.
I've tried to see if there's a way to save the position of the internal array pointer before going in another level of recursion, and then restoring the position when the function returns, but it does not appear that this is possible in PHP.
If I take the & out to not pass the $elements variable by reference, then the function works perfectly. Unfortunately this leaves elements to be processed by the foreach even if they've already been saved under their proper parent, wasting a bit of processing time.
Any ideas about how I could remove child elements from the unsorted pool of elements after they've been assigned to their parent?
Edit: Of course some sample data would help:
$source_data = unserialize('a:33:{i:0;a:5:{s:6:"pageid";s:2:"53";s:3:"key";s:2:"53";s:8:"parentid";s:2:"52";s:7:"tooltip";s:0:"";s:5:"title";s:8:"Example 2.0";}i:1;a:5:{s:6:"pageid";s:2:"68";s:3:"key";s:2:"68";s:8:"parentid";s:2:"53";s:7:"tooltip";s:0:"";s:5:"title";s:5:"Audit";}i:2;a:5:{s:6:"pageid";s:2:"65";s:3:"key";s:2:"65";s:8:"parentid";s:2:"25";s:7:"tooltip";s:26:"CastIron Deployment Helper";s:5:"title";s:8:"helper";}i:3;a:5:{s:6:"pageid";s:2:"50";s:3:"key";s:2:"50";s:8:"parentid";s:2:"49";s:7:"tooltip";s:17:"Calendar Template";s:5:"title";s:8:"Calendar";}i:4;a:5:{s:6:"pageid";s:2:"28";s:3:"key";s:2:"28";s:8:"parentid";s:2:"27";s:7:"tooltip";s:0:"";s:5:"title";s:15:"querty Gifts";}i:5;a:5:{s:6:"pageid";s:2:"24";s:3:"key";s:2:"24";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:4:"More_Samples";}i:6;a:5:{s:6:"pageid";s:2:"35";s:3:"key";s:2:"35";s:8:"parentid";s:1:"0";s:7:"tooltip";s:38:"Ideas for features of this wiki webapp";s:5:"title";s:6:"Design";}i:7;a:5:{s:6:"pageid";s:2:"70";s:3:"key";s:2:"70";s:8:"parentid";s:2:"25";s:7:"tooltip";s:0:"";s:5:"title";s:26:"Environments & Credentials";}i:8;a:5:{s:6:"pageid";s:2:"67";s:3:"key";s:2:"67";s:8:"parentid";s:2:"36";s:7:"tooltip";s:0:"";s:5:"title";s:19:"Helping Downstreams";}i:9;a:5:{s:6:"pageid";s:1:"1";s:3:"key";s:1:"1";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:4:"Home";}i:10;a:5:{s:6:"pageid";s:2:"64";s:3:"key";s:2:"64";s:8:"parentid";s:2:"53";s:7:"tooltip";s:0:"";s:5:"title";s:5:"ITC02";}i:11;a:5:{s:6:"pageid";s:2:"27";s:3:"key";s:2:"27";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:14:"More_Samples topics";}i:12;a:5:{s:6:"pageid";s:2:"26";s:3:"key";s:2:"26";s:8:"parentid";s:2:"25";s:7:"tooltip";s:0:"";s:5:"title";s:2:"MQ";}i:13;a:5:{s:6:"pageid";s:2:"55";s:3:"key";s:2:"55";s:8:"parentid";s:2:"36";s:7:"tooltip";s:0:"";s:5:"title";s:5:"Music";}i:14;a:5:{s:6:"pageid";s:2:"73";s:3:"key";s:2:"73";s:8:"parentid";s:2:"52";s:7:"tooltip";s:0:"";s:5:"title";s:5:"Other";}i:15;a:5:{s:6:"pageid";s:2:"29";s:3:"key";s:2:"29";s:8:"parentid";s:1:"1";s:7:"tooltip";s:0:"";s:5:"title";s:3:"Sample";}i:16;a:5:{s:6:"pageid";s:2:"34";s:3:"key";s:2:"34";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:14:"Sample 2013 Goals";}i:17;a:5:{s:6:"pageid";s:2:"33";s:3:"key";s:2:"33";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:8:"Sample 2014";}i:18;a:5:{s:6:"pageid";s:2:"31";s:3:"key";s:2:"31";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:6:"Sample R2";}i:19;a:5:{s:6:"pageid";s:2:"32";s:3:"key";s:2:"32";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:13:"Sample Text Only";}i:20;a:5:{s:6:"pageid";s:2:"25";s:3:"key";s:2:"25";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:9:"Reference";}i:21;a:5:{s:6:"pageid";s:2:"62";s:3:"key";s:2:"62";s:8:"parentid";s:2:"52";s:7:"tooltip";s:0:"";s:5:"title";s:8:"S&D reps";}i:22;a:5:{s:6:"pageid";s:2:"36";s:3:"key";s:2:"36";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:7:"Sandbox";}i:23;a:5:{s:6:"pageid";s:2:"71";s:3:"key";s:2:"71";s:8:"parentid";s:2:"52";s:7:"tooltip";s:0:"";s:5:"title";s:9:"Schedules";}i:24;a:5:{s:6:"pageid";s:2:"72";s:3:"key";s:2:"72";s:8:"parentid";s:2:"52";s:7:"tooltip";s:0:"";s:5:"title";s:9:"Schedules";}i:25;a:5:{s:6:"pageid";s:2:"51";s:3:"key";s:2:"51";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:13:"Special Pages";}i:26;a:5:{s:6:"pageid";s:2:"63";s:3:"key";s:2:"63";s:8:"parentid";s:2:"35";s:7:"tooltip";s:36:"How to handle tasks and task parsing";s:5:"title";s:5:"Tasks";}i:27;a:5:{s:6:"pageid";s:2:"49";s:3:"key";s:2:"49";s:8:"parentid";s:1:"0";s:7:"tooltip";s:47:"Template pages for use when creating new pages.";s:5:"title";s:9:"Templates";}i:28;a:5:{s:6:"pageid";s:2:"52";s:3:"key";s:2:"52";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:4:"Work";}i:29;a:5:{s:6:"pageid";s:2:"14";s:3:"key";s:2:"14";s:8:"parentid";s:1:"1";s:7:"tooltip";s:0:"";s:5:"title";s:8:"another!";}i:30;a:5:{s:6:"pageid";s:2:"54";s:3:"key";s:2:"54";s:8:"parentid";s:2:"25";s:7:"tooltip";s:0:"";s:5:"title";s:4:"tmux";}i:31;a:5:{s:6:"pageid";s:2:"12";s:3:"key";s:2:"12";s:8:"parentid";s:1:"1";s:7:"tooltip";s:0:"";s:5:"title";s:5:"what?";}i:32;a:5:{s:6:"pageid";s:2:"13";s:3:"key";s:2:"13";s:8:"parentid";s:1:"1";s:7:"tooltip";s:0:"";s:5:"title";s:5:"zippy";}}');
$correct_result = unserialize('a:9:{i:0;a:7:{s:6:"pageid";s:2:"24";s:3:"key";s:2:"24";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:4:"More_Samples";s:4:"href";s:8:"?page=24";s:8:"children";a:0:{}}i:1;a:7:{s:6:"pageid";s:2:"35";s:3:"key";s:2:"35";s:8:"parentid";s:1:"0";s:7:"tooltip";s:38:"Ideas for features of this wiki webapp";s:5:"title";s:6:"Design";s:4:"href";s:8:"?page=35";s:8:"children";a:1:{i:0;a:7:{s:6:"pageid";s:2:"63";s:3:"key";s:2:"63";s:8:"parentid";s:2:"35";s:7:"tooltip";s:36:"How to handle tasks and task parsing";s:5:"title";s:5:"Tasks";s:4:"href";s:8:"?page=63";s:8:"children";a:0:{}}}}i:2;a:7:{s:6:"pageid";s:1:"1";s:3:"key";s:1:"1";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:4:"Home";s:4:"href";s:7:"?page=1";s:8:"children";a:4:{i:0;a:7:{s:6:"pageid";s:2:"29";s:3:"key";s:2:"29";s:8:"parentid";s:1:"1";s:7:"tooltip";s:0:"";s:5:"title";s:3:"Sample";s:4:"href";s:8:"?page=29";s:8:"children";a:4:{i:0;a:7:{s:6:"pageid";s:2:"34";s:3:"key";s:2:"34";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:14:"Sample 2013 Goals";s:4:"href";s:8:"?page=34";s:8:"children";a:0:{}}i:1;a:7:{s:6:"pageid";s:2:"33";s:3:"key";s:2:"33";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:8:"Sample 2014";s:4:"href";s:8:"?page=33";s:8:"children";a:0:{}}i:2;a:7:{s:6:"pageid";s:2:"31";s:3:"key";s:2:"31";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:6:"Sample R2";s:4:"href";s:8:"?page=31";s:8:"children";a:0:{}}i:3;a:7:{s:6:"pageid";s:2:"32";s:3:"key";s:2:"32";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:13:"Sample Text Only";s:4:"href";s:8:"?page=32";s:8:"children";a:0:{}}}}i:1;a:7:{s:6:"pageid";s:2:"14";s:3:"key";s:2:"14";s:8:"parentid";s:1:"1";s:7:"tooltip";s:0:"";s:5:"title";s:8:"another!";s:4:"href";s:8:"?page=14";s:8:"children";a:0:{}}i:2;a:7:{s:6:"pageid";s:2:"12";s:3:"key";s:2:"12";s:8:"parentid";s:1:"1";s:7:"tooltip";s:0:"";s:5:"title";s:5:"what?";s:4:"href";s:8:"?page=12";s:8:"children";a:0:{}}i:3;a:7:{s:6:"pageid";s:2:"13";s:3:"key";s:2:"13";s:8:"parentid";s:1:"1";s:7:"tooltip";s:0:"";s:5:"title";s:5:"zippy";s:4:"href";s:8:"?page=13";s:8:"children";a:0:{}}}}i:3;a:7:{s:6:"pageid";s:2:"27";s:3:"key";s:2:"27";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:14:"More_Samples topics";s:4:"href";s:8:"?page=27";s:8:"children";a:1:{i:0;a:7:{s:6:"pageid";s:2:"28";s:3:"key";s:2:"28";s:8:"parentid";s:2:"27";s:7:"tooltip";s:0:"";s:5:"title";s:15:"querty Gifts";s:4:"href";s:8:"?page=28";s:8:"children";a:0:{}}}}i:4;a:7:{s:6:"pageid";s:2:"25";s:3:"key";s:2:"25";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:9:"Reference";s:4:"href";s:8:"?page=25";s:8:"children";a:4:{i:0;a:7:{s:6:"pageid";s:2:"65";s:3:"key";s:2:"65";s:8:"parentid";s:2:"25";s:7:"tooltip";s:26:"CastIron Deployment Helper";s:5:"title";s:8:"helper";s:4:"href";s:8:"?page=65";s:8:"children";a:0:{}}i:1;a:7:{s:6:"pageid";s:2:"70";s:3:"key";s:2:"70";s:8:"parentid";s:2:"25";s:7:"tooltip";s:0:"";s:5:"title";s:26:"Environments & Credentials";s:4:"href";s:8:"?page=70";s:8:"children";a:0:{}}i:2;a:7:{s:6:"pageid";s:2:"26";s:3:"key";s:2:"26";s:8:"parentid";s:2:"25";s:7:"tooltip";s:0:"";s:5:"title";s:2:"MQ";s:4:"href";s:8:"?page=26";s:8:"children";a:0:{}}i:3;a:7:{s:6:"pageid";s:2:"54";s:3:"key";s:2:"54";s:8:"parentid";s:2:"25";s:7:"tooltip";s:0:"";s:5:"title";s:4:"tmux";s:4:"href";s:8:"?page=54";s:8:"children";a:0:{}}}}i:5;a:7:{s:6:"pageid";s:2:"36";s:3:"key";s:2:"36";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:7:"Sandbox";s:4:"href";s:8:"?page=36";s:8:"children";a:2:{i:0;a:7:{s:6:"pageid";s:2:"67";s:3:"key";s:2:"67";s:8:"parentid";s:2:"36";s:7:"tooltip";s:0:"";s:5:"title";s:19:"Helping Downstreams";s:4:"href";s:8:"?page=67";s:8:"children";a:0:{}}i:1;a:7:{s:6:"pageid";s:2:"55";s:3:"key";s:2:"55";s:8:"parentid";s:2:"36";s:7:"tooltip";s:0:"";s:5:"title";s:5:"Music";s:4:"href";s:8:"?page=55";s:8:"children";a:0:{}}}}i:6;a:7:{s:6:"pageid";s:2:"51";s:3:"key";s:2:"51";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:13:"Special Pages";s:4:"href";s:8:"?page=51";s:8:"children";a:0:{}}i:7;a:7:{s:6:"pageid";s:2:"49";s:3:"key";s:2:"49";s:8:"parentid";s:1:"0";s:7:"tooltip";s:47:"Template pages for use when creating new pages.";s:5:"title";s:9:"Templates";s:4:"href";s:8:"?page=49";s:8:"children";a:1:{i:0;a:7:{s:6:"pageid";s:2:"50";s:3:"key";s:2:"50";s:8:"parentid";s:2:"49";s:7:"tooltip";s:17:"Calendar Template";s:5:"title";s:8:"Calendar";s:4:"href";s:8:"?page=50";s:8:"children";a:0:{}}}}i:8;a:7:{s:6:"pageid";s:2:"52";s:3:"key";s:2:"52";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:4:"Work";s:4:"href";s:8:"?page=52";s:8:"children";a:5:{i:0;a:7:{s:6:"pageid";s:2:"53";s:3:"key";s:2:"53";s:8:"parentid";s:2:"52";s:7:"tooltip";s:0:"";s:5:"title";s:8:"Example 2.0";s:4:"href";s:8:"?page=53";s:8:"children";a:2:{i:0;a:7:{s:6:"pageid";s:2:"68";s:3:"key";s:2:"68";s:8:"parentid";s:2:"53";s:7:"tooltip";s:0:"";s:5:"title";s:5:"Audit";s:4:"href";s:8:"?page=68";s:8:"children";a:0:{}}i:1;a:7:{s:6:"pageid";s:2:"64";s:3:"key";s:2:"64";s:8:"parentid";s:2:"53";s:7:"tooltip";s:0:"";s:5:"title";s:5:"ITC02";s:4:"href";s:8:"?page=64";s:8:"children";a:0:{}}}}i:1;a:7:{s:6:"pageid";s:2:"73";s:3:"key";s:2:"73";s:8:"parentid";s:2:"52";s:7:"tooltip";s:0:"";s:5:"title";s:5:"Other";s:4:"href";s:8:"?page=73";s:8:"children";a:0:{}}i:2;a:7:{s:6:"pageid";s:2:"62";s:3:"key";s:2:"62";s:8:"parentid";s:2:"52";s:7:"tooltip";s:0:"";s:5:"title";s:8:"S&D reps";s:4:"href";s:8:"?page=62";s:8:"children";a:0:{}}i:3;a:7:{s:6:"pageid";s:2:"71";s:3:"key";s:2:"71";s:8:"parentid";s:2:"52";s:7:"tooltip";s:0:"";s:5:"title";s:9:"Schedules";s:4:"href";s:8:"?page=71";s:8:"children";a:0:{}}i:4;a:7:{s:6:"pageid";s:2:"72";s:3:"key";s:2:"72";s:8:"parentid";s:2:"52";s:7:"tooltip";s:0:"";s:5:"title";s:9:"Schedules";s:4:"href";s:8:"?page=72";s:8:"children";a:0:{}}}}}';
$by_reference = unserialize('a:9:{i:0;a:7:{s:6:"pageid";s:2:"24";s:3:"key";s:2:"24";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:4:"More_Samples";s:4:"href";s:8:"?page=24";s:8:"children";a:0:{}}i:1;a:7:{s:6:"pageid";s:2:"35";s:3:"key";s:2:"35";s:8:"parentid";s:1:"0";s:7:"tooltip";s:38:"Ideas for features of this wiki webapp";s:5:"title";s:6:"Design";s:4:"href";s:8:"?page=35";s:8:"children";a:1:{i:0;a:7:{s:6:"pageid";s:2:"63";s:3:"key";s:2:"63";s:8:"parentid";s:2:"35";s:7:"tooltip";s:36:"How to handle tasks and task parsing";s:5:"title";s:5:"Tasks";s:4:"href";s:8:"?page=63";s:8:"children";a:0:{}}}}i:2;a:7:{s:6:"pageid";s:1:"1";s:3:"key";s:1:"1";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:4:"Home";s:4:"href";s:7:"?page=1";s:8:"children";a:1:{i:0;a:7:{s:6:"pageid";s:2:"29";s:3:"key";s:2:"29";s:8:"parentid";s:1:"1";s:7:"tooltip";s:0:"";s:5:"title";s:3:"Sample";s:4:"href";s:8:"?page=29";s:8:"children";a:4:{i:0;a:7:{s:6:"pageid";s:2:"34";s:3:"key";s:2:"34";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:14:"Sample 2013 Goals";s:4:"href";s:8:"?page=34";s:8:"children";a:0:{}}i:1;a:7:{s:6:"pageid";s:2:"33";s:3:"key";s:2:"33";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:8:"Sample 2014";s:4:"href";s:8:"?page=33";s:8:"children";a:0:{}}i:2;a:7:{s:6:"pageid";s:2:"31";s:3:"key";s:2:"31";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:6:"Sample R2";s:4:"href";s:8:"?page=31";s:8:"children";a:0:{}}i:3;a:7:{s:6:"pageid";s:2:"32";s:3:"key";s:2:"32";s:8:"parentid";s:2:"29";s:7:"tooltip";s:0:"";s:5:"title";s:13:"Sample Text Only";s:4:"href";s:8:"?page=32";s:8:"children";a:0:{}}}}}}i:3;a:7:{s:6:"pageid";s:2:"27";s:3:"key";s:2:"27";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:14:"More_Samples topics";s:4:"href";s:8:"?page=27";s:8:"children";a:1:{i:0;a:7:{s:6:"pageid";s:2:"28";s:3:"key";s:2:"28";s:8:"parentid";s:2:"27";s:7:"tooltip";s:0:"";s:5:"title";s:15:"querty Gifts";s:4:"href";s:8:"?page=28";s:8:"children";a:0:{}}}}i:4;a:7:{s:6:"pageid";s:2:"25";s:3:"key";s:2:"25";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:9:"Reference";s:4:"href";s:8:"?page=25";s:8:"children";a:4:{i:0;a:7:{s:6:"pageid";s:2:"65";s:3:"key";s:2:"65";s:8:"parentid";s:2:"25";s:7:"tooltip";s:26:"CastIron Deployment Helper";s:5:"title";s:8:"helper";s:4:"href";s:8:"?page=65";s:8:"children";a:0:{}}i:1;a:7:{s:6:"pageid";s:2:"70";s:3:"key";s:2:"70";s:8:"parentid";s:2:"25";s:7:"tooltip";s:0:"";s:5:"title";s:26:"Environments & Credentials";s:4:"href";s:8:"?page=70";s:8:"children";a:0:{}}i:2;a:7:{s:6:"pageid";s:2:"26";s:3:"key";s:2:"26";s:8:"parentid";s:2:"25";s:7:"tooltip";s:0:"";s:5:"title";s:2:"MQ";s:4:"href";s:8:"?page=26";s:8:"children";a:0:{}}i:3;a:7:{s:6:"pageid";s:2:"54";s:3:"key";s:2:"54";s:8:"parentid";s:2:"25";s:7:"tooltip";s:0:"";s:5:"title";s:4:"tmux";s:4:"href";s:8:"?page=54";s:8:"children";a:0:{}}}}i:5;a:7:{s:6:"pageid";s:2:"36";s:3:"key";s:2:"36";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:7:"Sandbox";s:4:"href";s:8:"?page=36";s:8:"children";a:2:{i:0;a:7:{s:6:"pageid";s:2:"67";s:3:"key";s:2:"67";s:8:"parentid";s:2:"36";s:7:"tooltip";s:0:"";s:5:"title";s:19:"Helping Downstreams";s:4:"href";s:8:"?page=67";s:8:"children";a:0:{}}i:1;a:7:{s:6:"pageid";s:2:"55";s:3:"key";s:2:"55";s:8:"parentid";s:2:"36";s:7:"tooltip";s:0:"";s:5:"title";s:5:"Music";s:4:"href";s:8:"?page=55";s:8:"children";a:0:{}}}}i:6;a:7:{s:6:"pageid";s:2:"51";s:3:"key";s:2:"51";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:13:"Special Pages";s:4:"href";s:8:"?page=51";s:8:"children";a:0:{}}i:7;a:7:{s:6:"pageid";s:2:"49";s:3:"key";s:2:"49";s:8:"parentid";s:1:"0";s:7:"tooltip";s:47:"Template pages for use when creating new pages.";s:5:"title";s:9:"Templates";s:4:"href";s:8:"?page=49";s:8:"children";a:1:{i:0;a:7:{s:6:"pageid";s:2:"50";s:3:"key";s:2:"50";s:8:"parentid";s:2:"49";s:7:"tooltip";s:17:"Calendar Template";s:5:"title";s:8:"Calendar";s:4:"href";s:8:"?page=50";s:8:"children";a:0:{}}}}i:8;a:7:{s:6:"pageid";s:2:"52";s:3:"key";s:2:"52";s:8:"parentid";s:1:"0";s:7:"tooltip";s:0:"";s:5:"title";s:4:"Work";s:4:"href";s:8:"?page=52";s:8:"children";a:1:{i:0;a:7:{s:6:"pageid";s:2:"53";s:3:"key";s:2:"53";s:8:"parentid";s:2:"52";s:7:"tooltip";s:0:"";s:5:"title";s:8:"Example 2.0";s:4:"href";s:8:"?page=53";s:8:"children";a:2:{i:0;a:7:{s:6:"pageid";s:2:"68";s:3:"key";s:2:"68";s:8:"parentid";s:2:"53";s:7:"tooltip";s:0:"";s:5:"title";s:5:"Audit";s:4:"href";s:8:"?page=68";s:8:"children";a:0:{}}i:1;a:7:{s:6:"pageid";s:2:"64";s:3:"key";s:2:"64";s:8:"parentid";s:2:"53";s:7:"tooltip";s:0:"";s:5:"title";s:5:"ITC02";s:4:"href";s:8:"?page=64";s:8:"children";a:0:{}}}}}}}';
$source_data is the unsorted source data, $correct_result is what I get when I don't pass $elements by reference (this is what I want), and $by_reference is what I get when I pass &$elements by reference (incorrect result).
Ithing you have to reindex the $elements array after unset try this ..
unset($elements[$key]);
$temp_array=array_values($elements);
$twig['children'] = buildTree($temp_array, $element['id']);

Create a multidimensional array by processing the same function over and over

I am trying to create a multidimensional array of my navigation. All my pages are created in a CMS and then stored in my DB. Each page will have the usual fields like title, url etc but also they will have a field which tells me if it has any children 'p_has_children' as well as a field which will tell me its parent page 'p_parent' (if it is a child of a page).
I thought I could write a function which I could call and send it an array of all the top level navigation items. Then as I loop through each item I would check if it had any children, get the children (and assign them to an array) and then send them through the same function like so....
function sideNavArray()
{
//Get the side navigation
$getSidenav = $this->crud_model->fetch_rows_where('pages', array('p_side_nav' => 'y', 'p_active' => 'y'));
//Call the navbuilder function
$sidenav = $this->sidenavBuilder($getSidenav, 'navitem', 'nav');
//return the generated nav
return $sidenav;
}
function sidenavBuilder($navItems, $itemClass, $navLevel, $i = 0, $sidenav = array())
{
//Loop over each nav item
foreach($navItems as $navItem){
//For each item I want to add th nav_title, slug, url and page type
$sidenav[$i]['p_nav_title'] = $navItem->p_nav_title;
$sidenav[$i]['p_slug'] = $navItem->p_slug;
$sidenav[$i]['p_url'] = $navItem->p_title;
$sidenav[$i]['p_type'] = $navItem->p_type;
//See if this page has any children
if($navItem->p_has_children == 'y'){
//If the page has children then I want to fetch them from the DB
$subnav = $this->crud_model->fetch_rows_where('pages', array('p_parent' => $navItem->id, 'p_active' => 'y', 'p_protected !=' => 'y'));
if(!empty($subnav)){
//Change the item class and level to subnavitem and subnav
$itemClass = 'sub' . $itemClass;
$navLevel = 'sub' . $navLevel;
//Assign the children to the same array as its parent in the "subnav" level and send it through the sitenavBuilder function
$sidenav[$i][$navLevel] = $this->sitenavBuilder($subnav, $itemClass, $navLevel);
}
}
$i++;
//End foreach loop
}
return $sidenav;
}
I am sure some of you are looking at this right now and saying well of course that won't work!
I currently have a navigation being generated but it is on the front end and every time I want to add an additional level I have to go into the code and add it manually. I want to avoid having to write each level of the navigation as I want it to be able to grow to as many levels as it can without me having to go in and code that extra level if it is needed every time.
I hope this all makes sense. Basically I want to know if there is a way to get this nav array to be built by running through the same loop over and over until it doesn't need to anymore.

Recursive tree traversal with PHP - layout issue

I am creating a questionnaire for a client that requires the questions to be organized by 3 layers of levels, with a unknown amount of questions and categories. I've successfully created the U.I. and the code to pull the information from the database however I've been trying to find out how to get everything loading in the right place. The database is organized like so by the client so I have no control over it:
id description parentId
1 Level 1 0
2 Level 2 0
3 Level 1a 1
4 Level 1b 1
5 Level 1a1 3
and the code I'm using right now is this:
function printChildQuestions($parentid) {
$sql="SELECT * FROM pB_test WHERE parentID=$parentid";
$result=mysql_query($sql);
$i=0;
while (true) {
$row=mysql_fetch_array($result);
if (!$row) break;
if ($row['parentId'] == 0) {
echo '<div class="tabs">';
echo '<span class="level1">'.$row['description'].'</span>';
echo '<ul id="main-nav"><h2 align="center">Categories</h2></ul>';
echo '<div>'.$row['id'].' '.$row['description'].' '.$row['parentId'].'</div>';
if ($row['parentId'] == 1) {
}
echo '</div>';
} else {
echo '<div>'.$row['id'].' '.$row['description'].' '.$row['parentId'].'</div>';
}
printChildQuestions($row['id']);
}
}
printChildQuestions(0);
and the html that needs to be generated for is here: http://jsfiddle.net/Cyb2N/
The issue is every idea I've come up with needs me to hardcode the levels in and breaks when level 2 is introduced. Could someone shove me in the right direction? Thanks!
If I understand your question correctly, you're looking for a way to resolve the levels implied by the parentId references in the client's data? Like so:
Level 1
Level 1a
Level 1a1
Level 1b
Level 2
How about creating a Level class with a parent property which references the parent Level? Then you can iterate through the client's data, resolving the Level instances into a nice tree, and do anything you want with it from there.
Alternatively, you could give the Level class a children property, which would be a collection of the levels that reference that instance as a parent... anyway the point is that you can transform your client's flat data into a useful tree model with a fairly simple class. By counting parents (or children), you can tell how far up/down the tree a given Level is.
printChildQuestions is a recursive function. It sounds like you don't know how to determine which level of recursion you're at. Here's how:
printChildQuestions($parent_id);
function printChildQuestions($parent_id, $level = 1){
if( $need_to_recurse ){
printChildQuestions($new_parent, $level + 1);
}
}

Linear Array with nodes randomly linked to other nodes in array, shortest path

INFO:
I have an Array of 100 nodes, [ 0 .. 99 ]. Each node can have an arbitrary number of linked nodes:
eg1, 0 links to 5, 10, 15, 20.
eg2, 1 links to 30, 40, 50.
eg3, etc..
All 100 nodes have at least one linked node, nodes do not know who links to them.
QUESTION:
How can I find the shortest link-path if provided with START and END.
eg. START=5, END=80, Link Path (example) : [5]->10->24->36->[80]?
I'm using Pascal and/or PHP, but understanding how is what I'm looking for [code helps too].
Plenty of reading/algorithms:
Shortest path problem. You effectively just have every edge ("link", as you called it) with an equal weight.
Do a Breadth First Traversal starting with the Start node and quit as soon as you find the end node.
Does this have cycles? i.e. is it a DAG?
If there aren't cycles:
List<Node> GetShortestPath(Node startNode, Node endNode)
{
//If this is the node you are looking for...
if (startNode.ReferenceEquals(endNode))
{
//return a list with just the end node
List<Nodes> result = new List<Nodes>();
result.Add(endNode);
return result;
}
List<Node> bestPath = null;
foreach(Node child in startNode.Children)
{
//get the shortest path from this child
List<Node> childPath = GetShortestPath(child, endNode);
if (childPath != null &&
( bestPath == null || childPath.Count < bestPath.Count))
{
bestPath = childPath;
}
}
bestPath.Insert(0, startNode);
return bestPath;
}
[Edit: Added an example for cycles]
If there can be cycles:
List<Node> GetShortestPath(Node startNode, Node endNode)
{
List<Node> nodesToExclude = new List<Node>();
return GetShortestPath(startNode, endNOde, nodesToExclude);
}
List<Node> GetShortestPath(Node startNode, Node endNode, List<Node> nodesToExclude)
{
nodesToExclude.Add(startNode);
List<Node> bestPath = null;
//If this is end node...
if (startNode.ReferenceEquals(endNode))
{
//return a list with just the child node
List<Nodes> result = new List<Nodes>();
result.Add(endNode);
return result;
}
foreach(Node child in startNode.Children)
{
if (!nodesToExclude.Contains(child))
{
//get the shortest path from this child
List<Node> childPath = GetShortestPath(child, endNode);
if (childPath != null &&
( bestPath == null || childPath.Count < bestPath.Count))
{
bestPath = childPath;
}
}
}
nodesToExclude.Remove(startNode);
bestPath.Insert(0, child);
return bestPath;
}
Two structures: a set and a list.
In the set, you store nodes you have already visited. This prevents you from following cycles.
The list is of objects containing: (1) a node, and (2) a pointer back to the node that found it.
Starting at the start node, add it to the set, add it to the list with a null back reference, and then add all the nodes it can reach to the list with back references to the index 0 in the list (the start node).
Then for each element in the list thereafter, up until you reach the end, do the following:
if it is in the set already skip it (you have already visited it) and move to the next item in the list.
otherwise, add it to the set, and add all nodes it can reach to the list with back references to the index you are 'looking at' to the end of the list. Then go to the next index in the list and repeat.
If at any point you reach the end node (optimally as you are adding it to the list - as opposed to visiting it in the list), track back through the back references to the start node and invert the path.
Example:
Given nodes 0 through 3, where
node0 --> node1
node0 --> node2
node1 --> node2
node2 --> node3
and node0 is START and node3 is END
SET = {}
LIST = []
Step 1 - add START:
SET = {node0}
LIST = [[node0, null]]
Step 2 - at index 0 of the list - add reachable nodes:
SET = {node0, node1, node2}
LIST = [[node0, null], [node1, 0], [node2, 0]]
Step 3 - at index 1 of the LIST - add reachable nodes:
node2 is already in the SET. skip adding reachable nodes to LIST.
SET = {node0, node1, node2}
LIST = [[node0, null], [node1, 0], [node2, 0]]
Step 4 - at index 2 of the LIST - add reachable nodes:
SET = {node0, node1, node2, node3}
LIST = [[node0, null], [node1, 0], [node2, 0], [node3, 2]]
Step 5 - reached END, now backtrack:
The END node (node3) inserted in the LIST has a back reference to index 2 in the list, which is node2. This has a back reference to index 0 in the list, which is node0 (START). Invert this and you get a shortest path of node0 --> node2 --> node3.
Is this a tree/graph or a forest? If it is a forest, the path may not be defined always. In case this is a tree/graph, try using Breadth-First-Search.
Think of it this way: say, you are out on a stealth mission to find cute chicks in your neighbourhood. You start at your own house and mark it as the START. You'd next go to knock on your closest neighbours, right? So, we'll do just that -- push all nodes connected to the start in a queue. Now, repeat the neighbour search for all the nodes in this queue. And keep doing this till you get your girl, err, the END.

Categories