PHP рекурсия: сплющить дерево, сохранить метаданные

Представьте, что у вас есть дерево, представляющее семейство, которое вы хотите сохранить в таблицах, но сохранить уровни вложенности. Я хотел бы получить данные поста ниже в структуре ниже.

Я полагаю, что RecursiveIterator мог бы быть способом сделать это, но я не уверен, как бы я это сделал. У меня есть код, который работает в большинстве случаев, но он стал уродливым, раздутым. Я могу опубликовать это, если хотите.

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
)
)
)
)
)
)
)

Записи на постоянство должны выглядеть так:

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 )
)

Постскриптум Нет, у меня и моей сестры не было ребенка. Это была просто моя аналогия, падающая на его лицо. 😉

3

Решение

RecursiveIterator классы могут быть немного запутанными, мне нравится стараться сделать их простыми. Ты можешь использовать RecursiveIteratorIterator чтобы перебрать значения вашего итератора, он может даже дать вам текущую глубину (или subgroup в твоем случае).

Проблема в том, что родитель не является массивом, но мы можем позаботиться об этом в конструкторе.

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

Тогда вы можете использовать этот класс как:

$loop = new RecursiveIteratorIterator(
new FamilyIterator($dataObj),
RecursiveIteratorIterator::SELF_FIRST
);

Когда ты foreach над $loop, он будет автоматически вызывать getChildren метод, когда это необходимо, поэтому в foreach у вас будет каждый ряд. Вы даже можете спросить RecursiveIteratorIterator для глубины.

$newData = [];
foreach($loop as $row){
$row->subgroup = $loop->getDepth();
$newData[] = $row;
}

DEMO: https://eval.in/444078

Это не может быть именно так что вы хотели, но, надеюсь, это укажет вам правильное направление. RecursiveIteratorне должно быть сложным.

3

Другие решения

Других решений пока нет …