4

我在 Yii 中有一个模型,它代表一棵树,带有以下 MySQL 表:

CREATE TABLE IF NOT EXISTS `nodes` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `node` varchar(255) NOT NULL,
    [ something more not necessary to display here ]
    `parentid` int(11) NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`),
) ENGINE=InnoDB;

我对这个模型有以下关系:

return array(
    'parentnode'=>array(self::BELONGS_TO, 'Nodes', 'parentid'),
    'childnode'=>array(self::HAS_MANY, 'Nodes', 'parentid'),
);

我正在使用 CTreeView 来显示树。使用 $model->childnode 递归地迭代所有 parentid=0 的节点来构建源数组。这是迭代函数:

public static function nodetree($params) { //finds all top-level nodes
    $retval=array();
    $nodes=Nodes::model()->findAllByAttributes(array('parentid'=>0));
    foreach($nodes as $anode)
        $retval[]=Nodes::nodearray($anode, $params);
}

这是递归函数。

public static function nodearray($_node, $params) { // finds children
    $retval=array(
        'text'=>$_node->node, //may differ based on options
        'id'=>$_node->id,
        'expanded'=>false,    //may differ based on options
        'children'=>array(),
    );
    foreach ($_node->childnode as $c_node)
        $retval['children'][]=Nodes::nodearray($c_node, $params);
    return $retval;
}

由于 Yii 的开销,这可能不是最快的方法。在没有其他应用程序运行的开发服务器上生成页面需要超过 1 秒。节点超过 1K,并在需要时由用户更新。

如何更快地生成树/页面?

4

2 回答 2

2

好吧,很遗憾我回答了自己的问题,但不知何故我找到了解决方案。

我创建了一个缓存表:

CREATE TABLE IF NOT EXISTS `treecache` (
    `param` varchar(255) NOT NULL,
    `cachedtree` mediumtext NOT NULL,
    PRIMARY KEY (`param`)
) ENGINE=InnoDB;

我改变了nodetree函数如下:

public static function nodetree($param) { //finds all top-level nodes
    $paramstr = serialize($param);
    if (strlen($paramstr)<256) {
        $oval=Yii::app()->db->createCommand('SELECT cachedtree FROM treecache WHERE param=:param')->queryRow(true, array('param'=>$paramstr));
        if (isset($oval['cachedtree'])) return unserialize($oval['cachedtree']);                    
    }
    $retval=array();
    $nodes=Nodes::model()->findAllByAttributes(array('parentid'=>0));
    foreach($nodes as $anode)
        $retval[]=Nodes::nodearray($anode, $param);
    if (strlen($paramstr)<256)
        Yii::app()->db->createCommand('INSERT INTO treecache VALUES (:param, :cachedtree)')->execute(array('param'=>$paramstr, 'cachedtree'=>serialize($retval)));
    return $retval;
}

此外,为了应对节点变化,我在模型中添加了一个新功能:

public function afterSave() {
    Yii::app()->db->createCommand('TRUNCATE treecache')->execute();
    return parent::afterSave();
}

这不会加速树的生成过程,但会根据参数缓存生成的树,并比迭代/递归更快地返回结果。我仍然对任何评论持开放态度......

于 2013-09-15T20:08:19.090 回答
2

这是迄今为止我测试过的最有效的方法。源自http://blog.ideashower.com/post/15147134343/create-a-parent-child-array-structure-in-one-pass

public static function nodetree($param=array()) {
    $refs = array();
    $list = array();

    $nodes = Yii::app()->db->createCommand('select * from nodes')->queryAll();

    foreach ($nodes as $data) {
        $thisref = &$refs[ $data['id'] ];
        $thisref['parentid'] = $data['parentid'];
        $thisref['text'] = $data['node'];
        if ($data['parentid'] == 0) {
            $list[ $data['id'] ] = &$thisref;
        } else {
            $refs[ $data['parentid'] ]['children'][ $data['id'] ] = &$thisref;
        }   
    }           
    return $list;
}
于 2013-09-15T23:36:37.377 回答