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'
)
);
问题是,它似乎是唯一的工作方式是每个关联都有两行。
只有一个关联行的示例:
节点
连接表中的单行描述了这两个节点之间的关系:
现在 - 如果我查询电视节目并包含它的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