【问题标题】:How to use Doctrine nested set for storing multiple trees in one single table?如何使用 Doctrine 嵌套集在一张表中存储多棵树?
【发布时间】:2011-06-21 12:01:14
【问题描述】:

我计划使用 orm 学说提供的嵌套集功能来拥有线程化 cmets。换句话说,我想将多个独立的树存储在一个表中。在我的具体情况下,会有一些实体“新闻”有很多“评论”。

News:
  columns:
    content: clob

Comment:
  actAs:
    NestedSet:
      hasManyRoots: true
      rootColumnName: root_id
 columns:
    benutzer_id:       { type: integer, notnull: true }
    ref_id:            { type: integer, notnull: true }
    content:            { type: clob, notnull: true}
  relations:
    News:
      class: News
      local: ref_id
      foreign: id

我如何告诉教义使用comment.ref_id 作为鉴别器列?因此,只有具有相同 ref_id 的节点才与一棵树相关。目前所有的树操作都会影响存储在评论表中的所有节点。希望只有具有给定列名(“ref_id”)的节点才能充当一棵树。

更新 为了解决这个问题,我正在考虑产生一种在一个表中拥有许多 hasManyRoots-trees 的方法。 要加载一棵树,必须像这样创建一棵树:

$category->$treeObject = Doctrine_Core::getTable('Category')->getTree('ref_id',12);

所有的树操作操作都应该包括“WHERE ref_id=12 AND ...”。在我的例子中,你会为新闻#12 操作 cmets-tree。因此,数据库更新语句会更少。由于 ref_id 与新闻有关,因此 ref_id 上已经有一个索引,因此它应该运行得相当快。

最终解决方案 - 不是问题的一部分

经过大量讨论和沉思,我想出了以下架构。它包括减少评论表中的列(踢出 ref_id,root_id 现在引用根评论而不是新闻了)。

News:
  columns:
    content: clob
    comment_root_id:      { type: integer, notnull: false }
  relations:
    CommentRoot:
      class: Comment
      local: comment_root_id
      type: one

Comment:
  actAs:
    NestedSet:
      hasManyRoots: true
  columns:
    content:            { type: clob, notnull: true}

我认为这样更干净。创建新闻需要创建一个虚拟根节点。

        $treeObject = Doctrine_Core::getTable('Comment')->getTree();

        $root = new Comment();
        $root ->content = 'root';
        $root->save();
        $root = $treeObject->createRoot($root);

        $news->setCommentRoot($root);
        $news->save();

最后,您可以在查询“新闻”时使用左连接来获取根评论以告诉您有多少孩子。

对于性能问题,您可能希望在 root_id 列上手动放置索引。完成。

【问题讨论】:

    标签: php tree doctrine nested


    【解决方案1】:

    rootColumnName 的值是标识符,所以如果你想使用 ref id,你会这样做:

      actAs:
        NestedSet:
          hasManyRoots: true
          rootColumnName: ref_id
    

    更新:

    为了解决这个问题,我正在考虑产生一种在一个表中拥有许多 hasManyRoots-trees 的方法。

    您正在尝试重做附加功能! :-)

    调用 getTree 实际上并不运行查询...它只是返回实现实例 - Doctrine_Tree_NestedSet。您有机会在查询之前获得您想要的东西......

    只得到一棵树:

    $category->$treeObject = Doctrine_Core::getTable('Category')->getTree()->fetchTree(array('root_id' => 12));
    

    fetchTree 的第一个参数是一个选项数组,您可以在此处指定多项内容,包括

    • root_id(将转换为您在架构中作为 root_id 选项提供的任何内容)
    • 要获取的深度为depth

    此外,如果您需要比这更复杂的选项,您可以在获取具有您想要的任何查询条件的树之前提供基本查询:

    // join you news item
    $q = Doctrine_Core::getTable('Category')->create('c')
      ->leftJoin('c.News n with n.id = ?', $articleId);
    
    $tree = Doctrine_Core::getTable('Category')->getTree();
    $tree->setBaseQuery($q);
    $nodes = $tree->fetchTree(array('root_id' => 12));
    

    树的 root_id 应该总是指向树中的另一个节点...而不是外部的东西。通过设置基本查询,您可以在整个树上运行连接或提供其他限定条件。


    但是,ref_id 不应该应用 FK 约束。根和树由树和节点 API 管理,它为您处理所有这些。它将为 NestedSet 相关列生成的 SQL 如下所示:

    CREATE TABLE category (id BIGINT AUTO_INCREMENT,  
    ref_id INT, 
    lft INT, 
    rgt INT, 
    level SMALLINT, 
    PRIMARY KEY(id)) ENGINE = INNODB
    

    如果您不想让自己头疼,请留下这些列定义(除了命名,如果那不合适),您将不会头疼。如果您需要与新闻相关联,请使用与实际嵌套集无关的单独列/关系。

    【讨论】:

    • 1) 按照您的方式,我将不得不为每个 ref_id 放置一个虚拟评论,以仅显示他的孩子。 2) 从一棵树更新 cmets 仍会影响迄今为止完成的所有其他 cmets。 - 我不应该担心这里的表现吗?我对最初的问题写了一些想法......
    • 您的 ref_id 不会是虚拟内容...请参阅我的更新。您的树根 id 始终是树上的另一个节点 id,而不是与其他事物的关系。因此,您需要另一列来为您的新闻项目添加 FK。
    • 我试过你的方法。每次我插入新评论时,它都会使用 lft 和 rgt 作为标准对 lft 和 rgt 列上的所有评论运行更新。两列都没有索引。拥有 30k+ cmets 这仍然会表现良好吗?如果您可以在初始化树时传递一些如上所述的附加标准(ref_id),您将能够在一个表中存储许多独立的 hasManyRoots-trees。我不满意只存储一个 hasManyRootsTree 和许多 hasOneRoot-trees 作为孩子。
    • 它应该只更新单个树 (root_id) 的 lft 和 rgt,即使这样也只更新受更新影响的节点。如果它在表中的每条记录上运行更新,那么您可能没有正确使用 hasManyRoots 或者您的节点插入有问题。如果您发布代码以插入可能有帮助的评论。如果您仍然对可能的性能影响不满意,另一种选择可能是将每个顶级评论及其回复存储为单独的树,因为您只需要“线程化”评论及其后续回复。
    • 感谢@prodigitalson 的讨论。帮助我理解了教义树。你的答案似乎是正确的。我在最初的问题中添加了我的最终想法,只是为了让其他人知道。
    猜你喜欢
    • 2011-01-02
    • 1970-01-01
    • 2012-06-25
    • 2012-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-19
    • 2010-09-17
    相关资源
    最近更新 更多