【问题标题】:Traversing Hierarchy Tree Adjacency List Model in Yii2Yii2中遍历层次树邻接表模型
【发布时间】:2017-08-18 05:37:35
【问题描述】:

我被困在一个我无法理解如何完成并遍历列表的逻辑上。

实际上,我正在创建一个类别列表,该列表将进一步用于创建产品。我希望类别列表应该是父节点及其子节点的形式,如邻接列表模型。

数据库:

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' 
  ]
 ] 
 

我试过:当我从表中获取行并将它们存储在关联数组中时。每个分支节点的子 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 以获得结果,但我无法获得所需的输出。

我已经解决了堆栈溢出问题,但没有一个对我有用。 所以任何人都可以帮助高级赞赏。

【问题讨论】:

    标签: php arrays yii tree yii2


    【解决方案1】:

    您可以为此使用Iterators。让我们扩展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);
        }
    }
    

    顺便提一下,顶级家长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 并推入这样的数组` [ [ 'id' => 1, 'categoryname' => 'a', ], [ 'id' => 2, 'categoryname' => 'b', ], ['id' => 3, 'categoryname' => 'b > c', ], ['id' => 4, 'categoryname' =>' b>c>f', ], ]`
    • @AkhileshSingh,您可以将此行 echo implode(' > ', array_slice($path, 0, $depth + 1)), PHP_EOL; 更改为您想要的任何逻辑
    • 你能看看输出部分吗
    • @AkhileshSingh,而不是回显 (echo implode(' > ', array_slice($path, 0, $depth + 1)), PHP_EOL;) 将 id 和路径存储到结果数组(在循环之前对其进行初始化)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多