代码之家  ›  专栏  ›  技术社区  ›  Akhilesh Singh

Yii2中的遍历层次树邻接列表模型

  •  1
  • Akhilesh Singh  · 技术社区  · 7 年前

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

    数据库:

    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

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

    1 回复  |  直到 3 年前
        1
  •  1
  •   sevavietl    7 年前

    您可以使用 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;
    }
    

    这种情况与之前的不同之处在于我们设置了 $mode RecursiveIteratorIterator RecursiveIteratorIterator::LEAVES_ONLY .