Yii2遍历层次树邻接表模型
Traversing Hierarchy Tree Adjacency List Model in Yii2
我被困在一个逻辑上,我无法理解它是如何完成并遍历列表的。
实际上我正在创建一个类别列表,该列表将进一步用于创建产品。我希望类别列表应该采用父节点及其子节点的形式,如邻接列表模型。
数据库:
id categoryname parent [id is the foreign key for the parent]
1 a 1
2 b 2
3 c 2
4 e 2
5 f 3
6 g 4
在 yii2 中使用 ActiveQuery 获取详细信息:
$rows = Category::find()
->asArray()
->all();
$rows
数组包含这样形式的数据
Array
(
[0] => Array
(
[id] => 1
[categoryname] => a
[parent] => 1
)
[1] => Array
(
[id] => 2
[categoryname] => b
[parent] =>2
)
[2] => Array
(
[id] => 3
[categoryname] => c
[parent] => 2
)
)
And so on...
我希望所需的输出应该像这种形式的列表
[
[
'id' => 1,
'categoryname' => 'a'
],
[
'id' => 2,
'categoryname' => 'b'
],
[
'id' => 3,
'categoryname' => 'b > c'
],
[
'id' => 4,
'categoryname' => 'b>c>f'
]
]
我尝试过:当我从 table 中获取行并将它们存储在关联数组中时。每个分支节点的子 ID 存储在另一个关联数组中。
foreach ($rows as $row){
$id = $row["id"];
$parent_id = $row["parent"] === NULL ? "NULL" : $row["parent"];
$data[$id] = $row;
$index[$parent_id][] = $id;
}
function display_child_nodes($parent_id, $level,$data,$index)
{
$parent_id = $parent_id === NULL ? "NULL" : $parent_id;
if (isset($index[$parent_id])) {
foreach ($index[$parent_id] as $id) {
$result['id'] = $data[$id]['id'];
$result['name'] = $data[$id]['categoryname'];
$result['level'] = $level;
echo str_repeat("-", $level) . $data[$id]["categoryname"] . "\n";
display_child_nodes($id, $level + 1,$data,$index);
}
}
}
display_child_nodes(NULL, 0,$data,$index);
我按照此 reference 获得结果,但我无法获得所需的输出。
我已经解决了堆栈溢出问题,但 none 对我很有用。
所以任何人都可以帮助提前感谢。
您可以使用 Iterators for that. Let's extend RecursiveArrayIterator
并调用新的迭代器 AdjacencyListIterator
:
class AdjacencyListIterator extends RecursiveArrayIterator
{
private $adjacencyList;
public function __construct(
array $adjacencyList,
array $array = null,
$flags = 0
) {
$this->adjacencyList = $adjacencyList;
$array = !is_null($array)
? $array
: array_filter($adjacencyList, function ($node) {
return is_null($node['parent']);
});
parent::__construct($array, $flags);
}
private $children;
public function hasChildren()
{
$children = array_filter($this->adjacencyList, function ($node) {
return $node['parent'] === $this->current()['id'];
});
if (!empty($children)) {
$this->children = $children;
return true;
}
return false;
}
public function getChildren()
{
return new static($this->adjacencyList, $this->children);
}
}
顺便提一下,顶层parents parent
应该是null
(和id
不一样)。
有了这个迭代器,你可以生成这样的路径:
$iterator = new RecursiveIteratorIterator(
new AdjacencyListIterator($rows),
RecursiveIteratorIterator::SELF_FIRST
);
$path = [];
foreach ($iterator as $node) {
$depth = $iterator->getDepth();
$path[$depth] = $node['categoryname'];
echo implode(' > ', array_slice($path, 0, $depth + 1)), PHP_EOL;
}
这里是working demo.
这种方法可能比自定义递归函数慢一点。但它实际上更灵活。只改变遍历方式,可以只得到叶子,例如:
$iterator = new RecursiveIteratorIterator(
new AdjacencyListIterator($rows)
);
foreach ($iterator as $leaf) {
echo $leaf['categoryname'], PHP_EOL;
}
这个案例与之前的不同之处在于我们将RecursiveIteratorIterator
的$mode
设置为默认的RecursiveIteratorIterator::LEAVES_ONLY
。
我被困在一个逻辑上,我无法理解它是如何完成并遍历列表的。
实际上我正在创建一个类别列表,该列表将进一步用于创建产品。我希望类别列表应该采用父节点及其子节点的形式,如邻接列表模型。
数据库:
id categoryname parent [id is the foreign key for the parent]
1 a 1
2 b 2
3 c 2
4 e 2
5 f 3
6 g 4
在 yii2 中使用 ActiveQuery 获取详细信息:
$rows = Category::find()
->asArray()
->all();
$rows
数组包含这样形式的数据
Array
(
[0] => Array
(
[id] => 1
[categoryname] => a
[parent] => 1
)
[1] => Array
(
[id] => 2
[categoryname] => b
[parent] =>2
)
[2] => Array
(
[id] => 3
[categoryname] => c
[parent] => 2
)
)
And so on...
我希望所需的输出应该像这种形式的列表
[
[
'id' => 1,
'categoryname' => 'a'
],
[
'id' => 2,
'categoryname' => 'b'
],
[
'id' => 3,
'categoryname' => 'b > c'
],
[
'id' => 4,
'categoryname' => 'b>c>f'
]
]
我尝试过:当我从 table 中获取行并将它们存储在关联数组中时。每个分支节点的子 ID 存储在另一个关联数组中。
foreach ($rows as $row){
$id = $row["id"];
$parent_id = $row["parent"] === NULL ? "NULL" : $row["parent"];
$data[$id] = $row;
$index[$parent_id][] = $id;
}
function display_child_nodes($parent_id, $level,$data,$index)
{
$parent_id = $parent_id === NULL ? "NULL" : $parent_id;
if (isset($index[$parent_id])) {
foreach ($index[$parent_id] as $id) {
$result['id'] = $data[$id]['id'];
$result['name'] = $data[$id]['categoryname'];
$result['level'] = $level;
echo str_repeat("-", $level) . $data[$id]["categoryname"] . "\n";
display_child_nodes($id, $level + 1,$data,$index);
}
}
}
display_child_nodes(NULL, 0,$data,$index);
我按照此 reference 获得结果,但我无法获得所需的输出。
我已经解决了堆栈溢出问题,但 none 对我很有用。 所以任何人都可以帮助提前感谢。
您可以使用 Iterators for that. Let's extend RecursiveArrayIterator
并调用新的迭代器 AdjacencyListIterator
:
class AdjacencyListIterator extends RecursiveArrayIterator
{
private $adjacencyList;
public function __construct(
array $adjacencyList,
array $array = null,
$flags = 0
) {
$this->adjacencyList = $adjacencyList;
$array = !is_null($array)
? $array
: array_filter($adjacencyList, function ($node) {
return is_null($node['parent']);
});
parent::__construct($array, $flags);
}
private $children;
public function hasChildren()
{
$children = array_filter($this->adjacencyList, function ($node) {
return $node['parent'] === $this->current()['id'];
});
if (!empty($children)) {
$this->children = $children;
return true;
}
return false;
}
public function getChildren()
{
return new static($this->adjacencyList, $this->children);
}
}
顺便提一下,顶层parents parent
应该是null
(和id
不一样)。
有了这个迭代器,你可以生成这样的路径:
$iterator = new RecursiveIteratorIterator(
new AdjacencyListIterator($rows),
RecursiveIteratorIterator::SELF_FIRST
);
$path = [];
foreach ($iterator as $node) {
$depth = $iterator->getDepth();
$path[$depth] = $node['categoryname'];
echo implode(' > ', array_slice($path, 0, $depth + 1)), PHP_EOL;
}
这里是working demo.
这种方法可能比自定义递归函数慢一点。但它实际上更灵活。只改变遍历方式,可以只得到叶子,例如:
$iterator = new RecursiveIteratorIterator(
new AdjacencyListIterator($rows)
);
foreach ($iterator as $leaf) {
echo $leaf['categoryname'], PHP_EOL;
}
这个案例与之前的不同之处在于我们将RecursiveIteratorIterator
的$mode
设置为默认的RecursiveIteratorIterator::LEAVES_ONLY
。