CakePHP:使用多级可包含行为
我已经花了相当长的时间尝试在CakePHP中使用可包含的行为,但我无法让它像我预期的那样工作 我的应用程序不同,但为了简化,我将举这个例子。比如说,我有一个论坛,里面有线程和活动,活动可以分级。一般关系将是: 论坛:有许多[线程]CakePHP:使用多级可包含行为,php,cakephp,behavior,Php,Cakephp,Behavior,我已经花了相当长的时间尝试在CakePHP中使用可包含的行为,但我无法让它像我预期的那样工作 我的应用程序不同,但为了简化,我将举这个例子。比如说,我有一个论坛,里面有线程和活动,活动可以分级。一般关系将是: 论坛:有许多[线程] 线程:属于[论坛],有许多[活动] 活动:属于[Thread],有许多[Rating] 评级:属于[活动] 我想要实现的是,使用find方法,获得某个论坛上的所有评分。我认为应该做的是: $this->Rating->find('count', arra
线程:属于[论坛],有许多[活动]
活动:属于[Thread],有许多[Rating]
评级:属于[活动]
我想要实现的是,使用find方法,获得某个论坛上的所有评分。我认为应该做的是:
$this->Rating->find('count', array(
'contain' => array(
'Activity' => array(
'Thread'
)
),
'conditions' => array(
'Thread.forum_id' => 1
)
));
但查询的结果是:
SELECT COUNT(*) AS `count` FROM `ratings` AS `Rating` LEFT JOIN `activities` AS `Activity` ON (`Rating`.`activity_id` = `Activity`.`id`) WHERE `Thread`.`forum_id` = 1;
我已经使用“连接”选项完成了这项操作,但是它更复杂,我必须在许多情况下使用这种操作
与示例相关的所有文件都可以在此处找到:
谢谢
2011年11月23日更新
在调查了框架之后,感谢Moz Morris和api55的回答,我找到了问题的根源
基本问题是,正如我所理解的CakePHP,我认为它每次都在使用连接进行查询。它不这样做的事情,它将执行的实际操作,以获得我想要的结果,将是这样的:
SELECT * FROM Rating JOIN Activity...
SELECT * FROM Activity JOIN Thread...
SELECT * FROM Activity JOIN Thread...
...
这意味着它将执行一个查询以获取所有活动,然后针对每个活动执行一个查询以获取线程。。。我的方法之所以失败,不是因为Containable行为使用错误,而是因为“conditions”选项应用于所有查询,在第一个查询中,由于缺少线程表,它崩溃了。找到这一点后,有两种可能的解决方案:
- 正如api55所说,使用“contain”数组中的条件,它只会将它们应用于使用Thread表的查询。但这样做的问题仍然存在,因为我们有太多的查询
- 正如Moz Morris所说,将线程模型与评级绑定也会起作用,它将执行一个查询,这正是我们想要的。问题是,我认为这是一个跳过模型之间关系的补丁,不遵循CakePHP哲学
我将api55解决方案标记为正确,因为它解决了我遇到的具体问题,但两者都给出了问题的解决方案。首先,您是否已将actAs containable变量放入appModel中??如果没有它,这个beahaviour将根本无法工作(我看到它无法正常工作,因为它没有与线程表连接) 我会从上面做,我的意思是从论坛,所以你选择你的论坛(我不确定你想要论坛或线程)并获得它的所有评级,如果没有评级,你将以评级键为空结束 像这样的
'contain' => array(
'Activity' => array(
'Thread' => array(
'conditions' => array('Thread.forum_id' => 1)
)
)
),
应用模型
public $actsAs = array('Containable');
额定值控制器
$this->Rating->Activity->Thread->Forum->find('count', array(
'contain' => array(
'Thread' => array(
'Activity' => array(
'Rating' => array (
'fields' => array ( 'Rating.*' )
)
)
)
),
'conditions' => array(
'Forum.id' => 1
)
));
然后,若您只需要评级表中的一个值,那个么只需使用Set:extract来获取该值的数组
正如你所做的那样,它无论如何都应该有效,但我建议不要在那里使用论坛id,而是在这样的条件下使用
'contain' => array(
'Activity' => array(
'Thread' => array(
'conditions' => array('Thread.forum_id' => 1)
)
)
),
另外,永远不要忘记使用可包含行为(或应用程序内模型)的模型中的actsAs变量。尽管我喜欢api55的解决方案,但我认为结果有点混乱-这取决于您打算对数据做什么,我猜 我假设当你说使用“连接”方法时,你说的是使用这个方法:
$this->Rating->bindModel(array(
'belongsTo' => array(
'Thread' => array(
'foreignKey' => false,
'conditions' => 'Thread.id = Activity.thread_id',
),
'Forum' => array(
'foreignKey' => false,
'conditions' => 'Forum.id = Thread.forum_id'
)
)
));
$ratings = $this->Rating->find('all', array(
'conditions' => array(
'Forum.id' => 1 // insert forum id here
)
));
这对我来说似乎有点干净,您不必担心在应用程序模型中使用可包含的行为。值得考虑。在线程数组中移动“条件”将不起作用,因为其他所有内容的结果仍将返回,而“线程”数组为空。@MozMorris这是真的,因为它是左连接。。。但是,如果他从顶部运行它(从论坛/线程),它将获得所有评级或为空,尽管它会有很多恼人的树(我通常使用可链接的行为),谢谢回复。我在链接上提供的代码上尝试了这一点,查询是
选择COUNT(*)AS COUNT FROM AS Forum AS Forum WHERE Forum.id=1
。我已经在AppModel和所有东西上设置了可控制的行为。我不知道我是否有一些配置问题或其他问题,但它似乎工作不正常。@Noeldmartin要100%确保您正确加载行为,您可以在控制器中编写以下代码$this->Rating->Behaviors->attach('Containable')代码>这是用于测试目的。。。如果你在论坛中调用find do$this->Rating->Activity->Thread->Forum->behavies->attach('Containable')
@api55我已经用最终的解决方案更新了问题,谢谢你的回答,它帮助我找到了问题。如果你需要计数,那么很明显,只需将“all”替换为“count”。)我从来没有想过XD,但它确实更干净,另一种方式会产生许多递归…谢谢你的回答,这在这个例子中是可行的。问题是,在实际应用程序中,我必须多次这样做,并且我希望做得更模块化(使用带有“启用”模型的阵列)。按照你说的做,我每次都应该设置所有的条件。此外,它不起作用让我很烦,这就是可控制行为的目的,我不知道我是否遗漏了什么。无论如何谢谢你。