HABTM with self需要2x连接表中的行吗?

我正在尝试构建一个以Nodes为主要模型的CMS。 每个Node belongsTo一个NodeType ,每个Node可以与任何/所有其他Node

所以 - 认为这需要HABTM:

//Node model
public $hasAndBelongsToMany = array(
    'AssociatedNode' => array(
        'className' => 'Node',
        'foreignKey' => 'node_id',
        'associationForeignKey' => 'associated_node_id',
        'joinTable' => 'node_associations'
    )
);

问题是,它似乎是唯一的工作方式是每个关联都有两行。

只有一个关联行的示例:

节点

  • ER(id = 1)
  • 乔治克鲁尼(编号= 2)
  • 连接表中的单行描述了这两个节点之间的关系:

  • 'node_id'= 1
  • 'associated_node_id'= 2
  • 现在 - 如果我查询电视节目并包含它的Actor节点:

    $nodes = $this->Node->find('all', array(
            'conditions' => array(
                'Node.node_type_id' => '645' //tv shows
            ),
            'contain' => array(
                'AssociatedNode' => array(
                    'conditions' => array(
                        'AssociatedNode.node_type_id' => '239' //actors
                    ),
                )
            )
        ));
    

    这工作,我得到ER - >乔治克鲁尼。

    但是 - 如果我想要拉乔治克鲁尼所在的所有节目,该怎么办?

    $nodes = $this->Node->find('all', array(
            'conditions' => array(
                'Node.node_type_id' => '239' //actors
            ),
            'contain' => array(
                'AssociatedNode' => array(
                    'conditions' => array(
                        'AssociatedNode.node_type_id' => '645' //tv shows
                    ),
                )
            )
        ));
    

    这不起作用,因为它正在寻找George Clooney的ID位于'node_id'字段,并且ER的ID位于'associated_node_id'字段中 - 实际上它们被颠倒过来。

    我想过的唯一解决方案是为每个关联保留两行。 但这似乎过分。 但是,我必须想出一些自定义的东西,确保每次重复保存或删除其中的每一个副本时都会保持同步,等等 - 这看起来像是一大堆蠕虫。

    有什么我失踪?


    你可以使用自定义查询来完成,但为了与标准的Cake函数保持一致,我能想到的一件事就是在节点之间声明两个关系:

    public $hasAndBelongsToMany = array(
      'AssociatedNode1' => array(
          'className' => 'Node',
          'foreignKey' => 'node_id',
          'associationForeignKey' => 'associated_node_id',
          'joinTable' => 'node_associations'
      ),
      'AssociatedNode2' => array(
          'className' => 'Node',
          'foreignKey' => 'associated_node_id',
          'associationForeignKey' => 'node_id',
          'joinTable' => 'node_associations'
      )
    );
    

    然后你可以在afterFind回调中合并两个数组。

    function afterFind($results)
    {
      foreach($results as &$result)
      {
        if(isset($result['AssociatedNode1']) || isset($result['AssociatedNode2']))
        {
          $associated_nodes = array();
    
          if(isset($result['AssociatedNode1']))
          {
            foreach($result['AssociatedNode1'] as $associated_node)
            {
              $associated_nodes[] = $associated_node;
            }
          }
    
          if(isset($result['AssociatedNode2']))
          {
            foreach($result['AssociatedNode2'] as $associated_node)
            {
              $associated_nodes[] = $associated_node;
            }
          }
    
          $result['AssociatedNode'] = $associated_nodes;
        }
      }
      unset($result);
    
      return $results;
    }
    

    但是这会迫使你在调用contains()的时候声明AssociatedNode1和AssociatedNode2;


    我不确定你的用例的细节是什么,但我有几个备选方案供你选择:

    你可以考虑使用树行为 - 这是为了将东西存储在树中而建立的,这听起来可能就是你正在做的事情。 我自己并没有使用它,所以我不确定它是如何适用于您的使用。

    另一方面,如果你以一致的方向存储关系(即总是电视节目 - >演员),并知道你的查询运行的方向(查看树的电视节目,演员是在向下看,寻找演员在一个电视节目),你应该能够查询AssociatedNode当你走向相反的方向,例如

    $nodes = $this->AssociatedNode->find('all', array(
        'conditions' => array(
            'AssociatedNode.node_type_id' => '239' //actors
        ),
        'contain' => array(
            'Node' => array(
                'conditions' => array(
                    'Node.node_type_id' => '645' //tv shows
                ),
            )
        )
    ));
    

    在这种情况下,为了清晰起见,最好使用“ChildNode”而不是“AssociatedNode”。

    但是,这两个答案都取决于你的用例的具体情况 - ncco的答案是一个很好的通用解决方案。 它(必然)很尴尬,可能会更慢,但它很好地剔除了尴尬。


    我在过去做过的一件事可能会有所帮助,那就是为连接表创建一个模型。 我能够在那里存储额外的数据,并执行我想查询的任何内容。 然后,在这个连接模型的两边只定义一个hasMany关联(也许是一个belongsTo)。 然后,您可以使用连接模型进行查找并写入类似于(来自控制器)的内容:

    $this->Node->NodesNode->find('all', array('conditions'=>array("or"=>array('node_id'=>$id,'sub_node_id'=>$id))));
    

    恕我直言:没有任何事情真的迫使你使用蛋糕约定。 我喜欢蛋糕,但有时它和ORM都会让事情变得复杂。 您可能只想编写自己的查询并自己分析结果。 它可能比处理另一种行为或模型的开销更快,另外你可能写出比给定的默认值更好的查询方式。

    哦,最后,当你使用1个模型用于多种用途时,我会注意。 真的认为,如果这个模型应该真的支持一切。 我发现,每当我这样做,我就在一两年内重写了整个事情。 你会很快地碰到一个瓶颈,一些节点需要这种额外的行为,另外一些需要别的东西,如果你的陈述(或者更聪明的话)到处散布。 再加上它真的减慢了事情的速度,在基于数据库中执行疯狂的树型查询。

    链接地址: http://www.djcxy.com/p/64883.html

    上一篇: HABTM with self requires 2x the rows in join table?

    下一篇: referential hasMany Through associations