Reputation: 457
Imagine you have a tree representing a family that you want to persist to tables, but preserve nesting levels. I'd like to get the post data below into the structure underneath.
I believe a RecursiveIterator might be the way to do this, but I am not sure how I would do so. I have code that works for most cases, but it has become an ugly, bloated thing. I can post that if you'd like.
stdClass Object
(
[name] => Smith
[type] => Family
[children] => Array(
[1] => stdClass Object
(
[name] => Michael
[type] => Uncle
[children] => Array(
[0] => stdClass Object
(
[name] => Jared
[type] => cousin
)
)
)
[2] => stdClass Object
(
[name] => Jeff
[type] => Dad
[children] => Array(
[0] => stdClass Object
(
[name] => Jonas
[type] => self
)
[1] => stdClass Object
(
[name] => Leah
[type] => sister
[children] => Array(
[0] => stdClass Object
(
[name] => Jacob
[type] => nephew
)
)
)
)
)
)
)
The records for persistence should look like this:
Array
(
stdClass Object ( [name] => Smith [type] => Family [subgroup] => 0 [parent_subgroup] => )
stdClass Object ( [name] => Michael [type] => Uncle [subgroup] => 1 [parent_subgroup] => 0 )
stdClass Object ( [name] => Jared [type] => Cousin [subgroup] => 2 [parent_subgroup] => 1 )
stdClass Object ( [name] => Jeff [type] => Dad [subgroup] => 1 [parent_subgroup] => 0 )
stdClass Object ( [name] => Jonas [type] => self [subgroup] => 3 [parent_subgroup] => 1 )
stdClass Object ( [name] => Leah [type] => sister [subgroup] => 3 [parent_subgroup] => 1 )
stdClass Object ( [name] => Jacob [type] => nephew [subgroup] => 4 [parent_subgroup] => 3 )
)
P.S. No, me and my sister didn't have a kid. That was just my analogy falling on its face. ;)
Upvotes: 3
Views: 205
Reputation: 227260
RecursiveIterator
classes can get a bit messy, I like to try to keep them simple. You can use RecursiveIteratorIterator
to loop over the values of your iterator, it can even give you the current depth (or subgroup
in your case).
The challenge here is that the parent isn't an array, but we can take care of that in the constructor.
<?php
class FamilyIterator implements RecursiveIterator{
private $data, $counter;
public function __construct($familyTree){
$this->data = is_array($familyTree) ? $familyTree : [$familyTree];
}
public function current(){
$row = $this->data[$this->counter];
return (object)[
'name' => $row->name,
'type' => $row->type
];
}
public function key(){
return $this->counter;
}
public function next(){
$this->counter++;
}
public function rewind(){
$this->counter = 0;
}
public function valid(){
return $this->counter < count($this->data);
}
public function hasChildren(){
$row = $this->data[$this->counter];
return isset($row->children);
}
public function getChildren(){
$row = $this->data[$this->counter];
return new self($row->children);
}
}
Then you can use this class like:
$loop = new RecursiveIteratorIterator(
new FamilyIterator($dataObj),
RecursiveIteratorIterator::SELF_FIRST
);
When you foreach
over $loop
, it'll automatically call the getChildren
method when it needs, so in the foreach
you'll have each row. You can even ask RecursiveIteratorIterator
for the depth.
$newData = [];
foreach($loop as $row){
$row->subgroup = $loop->getDepth();
$newData[] = $row;
}
DEMO: https://eval.in/444078
This may not be exactly what you wanted, but hopefully it'll point you in the right direction. RecursiveIterator
s don't need to be complicated.
Upvotes: 3