其他分享
首页 > 其他分享> > 带有两个表的邻接表模型

带有两个表的邻接表模型

作者:互联网

所以我认为我的问题归结为两个问题:

>当树使用邻接表模型方法存储在MySQL中(两个表之间)时,如何在PHP中构建可遍历的树结构,同时牢记性能?
>一种以所需格式显示树而不重复遍历代码并用if / else和switch语句乱写逻辑的可维护方法是什么?

以下是更多详细信息:

我正在使用Zend框架.

我正在调查问卷.它存储在MySQL数据库中两个单独的表之间:questions和question_groups.每个表都扩展了适当的Zend_Db_Table_ *类.使用邻接列表模型方法表示层次结构.

我意识到我遇到的问题很可能是由于我将树结构填充到RDBMS中,所以我愿意接受其他选择.但是,我还将存储调查表的受访者及其回答,因此需要其他方法来支持.

问卷需要以各种HTML格式显示:

>作为输入响应的表单(使用Zend_Form)
>作为包含问题(和某些组)的有序列表(嵌套),作为按问题或组查看响应的链接.
>作为有序列表(嵌套),每个问题后附有答案.

问题是叶子节点,而question_groups可以包含其他question_groups和/或问题.总共有100多行要处理和显示.

当前,我有一个视图助手,它使用递归来进行所有处理,以检索question_group的子代(该查询在两个表之间执行UNION:QuestionGroup :: getChildren($id)).另外,在显示带有问题答案的问卷时,还需要另外两个查询来检索受访者及其对每个问题的回答.

虽然页面加载时间不是很长,但是这种方法感觉不对.递归加上几乎每个节点都需要进行多个数据库查询,这并没有使我感到内心非常温暖和模糊.

我已经尝试从UNION返回的完整树数组上尝试使用recursion-less和递归方法来构建要遍历和显示的分层数组.但是,由于组和问题存储在单独的表中,因此存在重复的节点ID,这似乎无法解决.也许我在那里缺少什么…

当前,以上述格式显示树的逻辑相当混乱.我希望不要在各处重复遍历逻辑.但是,各地的条件也不会生成最容易维护的代码.我已经阅读了Visitors,Decorators和一些PHP SPL迭代器,但对于如何与扩展Zend_Db_Table,Zend_Db_Table_Rowset和Zend_Db_Table_Row的类一起使用,我仍然不清楚.尤其是因为我还没有解决从数据库构建层次结构的先前问题.稍微容易地添加新的显示格式(或修改现有的显示格式)将是很好的.

解决方法:

>传统上,相邻列表在每行中为您提供parent_id列,该列将行链接到其直接父级.如果该行是树的根,则parent_id为NULL.但这会导致您运行许多SQL查询,这很昂贵.
>添加另一列root_id,以便每一行都知道它属于哪棵树.这样,您可以通过单个SQL查询获取给定树的所有节点.在您的Table类中添加一个方法,以通过树的根ID来获取行集.

class QuestionGroups extends Zend_Db_Table_Abstract
{
    protected $_rowClass = 'QuestionGroup';
    protected $_rowsetClass = 'QuestionGroupSet';
    protected function fetchTreeByRootId($root_id)
    {
         $rowset = $this->fetchAll($this
            ->select()
            ->where('root_id = ?', $root_id)
            ->order('id');
        );
        $rowset->initTree();
        return $rowset;
    }
}

>编写一个扩展Zend_Db_Table_Row的自定义类,并编写函数以检索给定行的父级以及子级的Rowset. Row类应包含受保护的数据对象,以引用父级和子级数组. Row对象还可以具有用于面包屑的getLevel()函数和getAncestorsRowset()函数.

class QuestionGroup extends Zend_Db_Table_Row_Abstract
{
    protected $_children = array();
    protected $_parent   = null;
    protected $_level    = null;
    public function setParent(Zend_Db_Table_Row_Abstract $parent)
    {
        $this->_parent = $parent;
    }
    public function getParent()
    {
        return $this->_parent;
    }
    public function addChild(Zend_Db_Table_Row_Abstract $child)
    {
        $this->_children[] = $child;
    }
    public function getChildren()
    {
        return $this->_children;
    }
    public function getLevel() {}
    public function getAncestors() {}
}

>编写一个扩展Zend_Db_Table_Rowset的自定义类,该类具有对行集中的行进行迭代的功能,设置父引用和子引用,以便随后可以将它们遍历为树.行集还应该具有getRootRow()函数.

class QuestionGroupSet extends Zend_Db_Table_Rowset_Abstract
{
    protected $_root = null;
    protected function getRootRow()
    {
        return $this->_root;
    }
    public function initTree()
    {
        $rows = array();
        $children = array();
        foreach ($this as $row) {
          $rows[$row->id] = $row;
          if ($row->parent_id) {
            $row->setParent($rows[$row->parent_id]);
            $rows[$row->parent_id]->addChild($row);
          } else {
            $this->_root = $row;
          }
        }
    }
}

现在,您可以在行集上调用getRootRow(),它将返回根节点.一旦有了根节点,就可以调用getChildren()并对其进行循环.然后,您还可以在这些中间子级中的任何一个上调用getChildren(),并以所需的任何格式递归输出树.

标签:tree,hierarchical-data,adjacency-list-model,zend-framework,php
来源: https://codeday.me/bug/20191106/2001960.html