2

我正在尝试构建一个以 CMSNodes为主要模型的内容。每个Node belongsToaNodeType和 everyNode都可以与 any/every other 相关Node

所以 - 认为这需要HABTM:

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

问题是,似乎唯一可行的方法是每个关联都有两行。

只有一个关联行的示例:

节点

  • 急诊室 (id=1)
  • 乔治克鲁尼 (id=2)

连接表中的一行描述了这两个节点之间的关系:

  • 'node_id' = 1
  • '关联节点ID' = 2

现在 - 如果我查询 TV Shows 并包含它的 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 -> George Clooney。

但是 - 如果我想取消所有乔治克鲁尼参加的节目怎么办?

$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' 字段中 - 而实际上它们是相反的。

我想到的唯一解决方案是为每个关联保留两行。但这似乎有些矫枉过正。但是随后我必须想出某种自定义的东西,以确保每次保存或删除关联时都使每个副本与另一个副本保持同步......等等 - 这似乎是一大罐蠕虫。

有什么我想念的吗?

4

3 回答 3

3

您可能可以使用自定义查询来做到这一点,但为了保持标准的 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;

于 2012-09-17T12:51:16.810 回答
1

我不确定您的用例的详细信息是什么,但我为您提供了几个替代选项:

您可能会考虑使用树行为- 这是为将事物存储在树中而构建的,这听起来可能就是您正在做的事情。我自己没有使用过,所以我不确定它对你的使用有多大的适用性。

另一方面,如果您以一致的方向存储关系(即始终是 TV Show->Actor)并知道您的查询运行的方向(查找树中的 TV Shows an Actor is in vs 向下查找 Actors in a电视节目),您应该能够在反向时查询 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”。

但是同样,这两个答案都取决于您的用例的细节 - nIcO 的答案是一个很好的通用解决方案。它(必然)很尴尬并且可能更慢,但它很好地抽象了尴尬。

于 2012-09-19T22:59:39.887 回答
0

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

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

恕我直言:没有什么真正迫使你使用蛋糕惯例。我喜欢蛋糕,但有时它和 ORM 都会使非常简单的事情变得复杂。您可能只想编写自己的查询并自己解析结果。它可能比处理其他行为或模型的开销要快,而且您可能会编写比给定默认值更好的查询方式。

哦,最后,当您将 1 个模型用于多种用途时,我会注意。真的想一想那个模型是否真的应该支持一切。我发现每当我这样做时,我都会在一两年内重写整个内容。您将很快遇到瓶颈,其中一些节点需要以这种方式进行额外的行为,而另一些则需要其他的东西,并且您的 if 语句(或者可能更聪明的东西)散布在各处。另外,在数据库中进行基于树的疯狂查询确实会减慢速度。

于 2012-09-21T19:50:08.727 回答