CakePHP:使用多级可容纳行为
我已经尝试在 CakePHP 中使用 Containable Behaviour 有一段时间了,但我无法让它按我的预期工作。
我的应用程序有所不同,但为了简化我将举这个例子。假设我有一个包含主题和活动的论坛,并且可以对活动进行评级。一般关系是:
论坛:hasMany [Thread]
主题:belongsTo [论坛],hasMany [活动]
活动:belongsTo [Thread],hasMany [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;
我已经使用“连接”选项完成了此操作,但它更复杂,我必须在很多情况下使用这种操作。
与该示例相关的所有文件都可以在这里找到: http://dl.dropbox .com/u/3285746/StackOverflow-ContainableBehavior.rar
谢谢
2011年11月23日更新
调查后框架并感谢 Moz Morris 和 api55 的回答,我找到了问题的根源。
基本问题是,根据我对 CakePHP 的理解,我认为它每次都是使用连接进行查询。但它没有这样做,它为获得我正在寻找的结果而执行的实际操作将是这样的:
SELECT * FROM Rating JOIN Activity...
SELECT * FROM Activity JOIN Thread...
SELECT * FROM Activity JOIN Thread...
...
这意味着它将执行查询来获取所有活动,然后对于每个活动,执行查询来获取线程...我的方法失败不是因为可包含行为使用错误,而是因为“条件”选项应用于所有查询,并且在第一个查询中,由于缺少线程表。发现这一点后,有两种可能的解决方案:
正如 api55 所说,使用“包含”数组内的条件只会将它们应用于使用线程表的查询。但这样做问题仍然存在,因为我们有太多查询。
正如 Moz Morris 所说,将 Thread 模型绑定到 Rating 也可以工作,并且它将执行单个查询,这正是我们想要的。问题是,我认为这是一个跳过模型之间关系并且不遵循 CakePHP 哲学的补丁。
我将 api55 解决方案标记为正确的,因为它解决了我遇到的具体问题,但两者都给出了问题的解决方案。
I've been quite some time trying to use the Containable Behavior in CakePHP but I can't get to make it work as I expected.
My application is different, but to simplify I'll put this example. Let's say I have a forum with threads and activities, and the activities can be rated. The general relations would be:
Forum: hasMany [Thread]
Thread: belongsTo [Forum], hasMany [Activity]
Activity: belongsTo [Thread], hasMany [Rating]
Rating: belongsTo [Activity]
What I want to achieve is, using the find method, get all the ratings performed on a certain forum. What I suppose should be done is the following:
$this->Rating->find('count', array(
'contain' => array(
'Activity' => array(
'Thread'
)
),
'conditions' => array(
'Thread.forum_id' => 1
)
));
But the result query is:
SELECT COUNT(*) AS `count` FROM `ratings` AS `Rating` LEFT JOIN `activities` AS `Activity` ON (`Rating`.`activity_id` = `Activity`.`id`) WHERE `Thread`.`forum_id` = 1;
I've accomplished this using the 'joins' option, but it's more complex and I have to use this kinda action in many situations.
All the files related with the example can be found here: http://dl.dropbox.com/u/3285746/StackOverflow-ContainableBehavior.rar
Thanks
Update 23/11/2011
After investigating the framework and thanks to the answers of Moz Morris and api55 I found the source of the problem.
The basic problem was that, as I understood CakePHP, I thought it was querying using joins each time. The thing it that it doesn't do that, the real operation it would perform to obtain the result I was looking for would be something like this:
SELECT * FROM Rating JOIN Activity...
SELECT * FROM Activity JOIN Thread...
SELECT * FROM Activity JOIN Thread...
...
Meaning that it would do a query to get all the activities and then, for each activity, perform a query to get the Threads... My approach was failing not because of the Containable Behaviour being used wrong, but because the 'conditions' option was applied to all queries and, on the first one, it crashed because of the absence of the Thread table. After finding this out, there are two possible solutions:
As api55 said, using the conditions inside the 'contain' array it would apply them only to the queries using the Thread table. But doing this the problem persists, because we have way too many queries.
As Moz Morris said, binding the Thread model to Rating would also work, and it would perform a single query, which is what we want. The problem is that I see that as a patch that skips the relations betweem models and doesn't follow CakePHP philosophy.
I marked api55 solution as the correct because It solves the concrete problem I had, but both give a solution to the problem.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,你是否将 actAs 可包含变量放入了 appModel 中?如果没有它,这种行为根本无法工作(我发现它无法正常工作,因为它没有与线程表连接)
我会从顶部进行,我的意思是从论坛,所以你选择你的论坛(我不确定您想要论坛或主题)并获得其所有评级,如果没有评级,您最终的评级键将为空。
像这样的
appModel
评级控制器
然后,如果您只需要评级表中的一个值,只需使用 Set:extract 来获取该值的数组。
正如你所做的那样,它应该可以工作,但我建议不要在那里使用forum_id,但在包含这样的条件下
另外,永远不要忘记使用可包含行为的模型中的actAs变量(或在应用程序模型中)
First of all, have you put the actAs containable variable in the appModel?? without it this beahaviour won't work at all (i see it is not working correctly since it didn't join with Thread table)
I would do it from the top, i mean from forum, so you choose your forum (im not sure you want forum or thread) and get all its rating, if theres no rating you will end up with the rating key empty.
something like this
appModel
rating controller
Then if you need only a value in rating table just use Set:extract to get an array of this value.
As you did it IT SHOULD work anyways but i sugest not to use forum_id there, but in conditions inside contain like this
Also, never forget the actsAs variable in the model using the containable behaviuor (or in app model)
虽然我喜欢 api55 的解决方案,但我认为结果有点混乱 - 取决于你打算如何处理我猜的数据。
我假设当您说使用“连接”方法时,您正在谈论使用此方法:
这对我来说似乎更干净,并且您不必担心在 AppModel 中使用可包含的行为。值得考虑。
Whist I like api55's solution, I think the results are a little messy - depends on what you intend to do with the data I guess.
I assume that when you said using the 'joins' method you were talking about using this method:
This just seems a little cleaner to me, and you don't have to worry about using the containable behaviour in your AppModel. Worth considering.