我正在尝试构建一个以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'
)
);
问题是,它似乎唯一可行的方法是每个关联都有两行。
只有一个关联行的示例:
节点
连接表中的一行描述了这两个节点之间的关系:
现在 - 如果我查询电视节目并包含它的演员节点:
$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'字段中 - 实际上它们是相反的。
我想到的唯一解决方案是为每个关联保留两行。但这似乎有点矫枉过正。但是,我必须提出某种自定义的东西,确保每次保存或删除关联时保持每个副本与其他副本同步 - 等等 - 这似乎是一大堆蠕虫。 / p>
我有什么遗失的吗?
答案 0 :(得分: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;
答案 1 :(得分:1)
我不确定您的用例的详细信息,但我有几个备用选项:
您可能会考虑使用Tree Behavior - 这是为了将内容存储在树中而构建的,这听起来可能就是您正在做的事情。我自己没有使用它,所以我不确定它对你的使用有多适用。
另一方面,如果你以一致的方向存储关系(即总是电视节目 - >演员),并知道你的查询运行的方向(查看电视节目的树,一个演员正在观看在电视节目中找到演员,你应该能够在反向时查询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的答案是一个很好的通用解决方案。它(必然)笨拙且可能更慢,但它很好地抽象了尴尬。
答案 2 :(得分:0)
我过去做过的一件事可能有帮助,就是为联接表创建一个模型。我能够在那里存储额外的数据,并根据我的查询做任何我想做的事情。然后在该连接模型的两侧只定义一个hasMany关联(也可以是belongsTo)。然后你可以使用连接模型进行查找并编写类似的东西(来自控制器):
$this->Node->NodesNode->find('all', array('conditions'=>array("or"=>array('node_id'=>$id,'sub_node_id'=>$id))));
恕我直言:没有什么能够迫使你使用蛋糕约定。我喜欢蛋糕,但有时它和ORM都很复杂。您可能只想编写自己的查询并自己解析结果。它可能比处理另一个行为或模型的开销更快,你可能会编写一个比给定的默认值更好的查询。
哦,最后,当你将1个模型用于多种用途时,我会留意。真的想想,如果一个模型真的应该支持一切。我发现,每当我做完这件事,我就会在一两年内重写整件事。你会很快碰到一个瓶颈,一些节点需要这样的额外行为,其他节点需要别的东西,你有if语句(或者更聪明的东西)分散在各处。另外,在db中执行基于疯狂树的查询确实会减慢速度。