带有两个表的邻接表模型
作者:互联网
所以我认为我的问题归结为两个问题:
>当树使用邻接表模型方法存储在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