CakePHP:使用多级可包含行为

时间:2011-11-22 12:10:21

标签: php cakephp behavior

我已经花了很长时间尝试在CakePHP中使用Containable Behavior,但我无法让它像我预期的那样工作。

我的应用程序有所不同,但为了简化,我将把这个例子。假设我有一个包含线程和活动的论坛,可以对活动进行评级。一般关系是:

论坛:hasMany [主持人]
线程:belongsTo [论坛],hasMany [活动]
活动:belongsTo [Thread],hasMany [Rating]
评级:belongsTo [活动]

我想要实现的是,使用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;

我使用'join'选项完成了这个,但它更复杂,我必须在很多情况下使用这种动作。

可以在此处找到与该示例相关的所有文件:http://dl.dropbox.com/u/3285746/StackOverflow-ContainableBehavior.rar

由于

更新23/11/2011

在调查框架后,感谢Moz Morris和api55的答案,我找到了问题的根源。

基本问题是,正如我所理解的CakePHP,我认为每次使用连接都在查询。它没有那样做的事情,它为获得我正在寻找的结果而执行的实际操作将是这样的:

SELECT * FROM Rating JOIN Activity...
SELECT * FROM Activity JOIN Thread...
SELECT * FROM Activity JOIN Thread...
...

意味着它将进行查询以获取所有活动,然后,对于每个活动,执行查询以获取线程...我的方法失败不是因为使用了可包含行为错误,而是因为' conditions'选项适用于所有查询,并且在第一个查询中,由于缺少Thread表而崩溃了。找到这个后,有两种可能的解决方案:

  • 正如api55所说,使用'contains'数组中的条件,它只会将它们应用于使用Thread表的查询。但是这样做仍然存在问题,因为我们的查询太多了。

  • 正如Moz Morris所说,将Thread模型绑定到Rating也会起作用,它会执行单个查询,这就是我们想要的。问题在于,我认为这是一个跳过模型之间关系而不遵循CakePHP理念的补丁。

我将api55解决方案标记为正确,因为它解决了我遇到的具体问题,但两者都解决了问题。

2 个答案:

答案 0 :(得分:5)

首先,您是否将actAs可包含变量放在appModel中?没有它这个beahaviour根本不会工作(我发现它没有正常工作,因为它没有与Thread表连接)

我会从顶部开始,我的意思是从论坛,所以你选择你的论坛(我不确定你想要论坛或线程)并得到它的所有评级,如果没有评级你最终将评级键为空

类似这样的事情

appModel

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来获取此值的数组。

正如你所做的那样,它应该可以工作,但我不会在那里使用forum_id,但在内部条件中包含这样的

'contain' => array(
    'Activity' => array(
        'Thread' => array(
              'conditions' => array('Thread.forum_id' => 1)
         )
    )
),

此外,永远不要忘记使用可包容的行为(或在应用程序模型中)中的actsAs变量

答案 1 :(得分:2)

惠斯特我喜欢api55的解决方案,我认为结果有点混乱 - 取决于你打算用我想做的数据。

我假设当你说使用'joins'方法时,你正在谈论使用这种方法:

$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
  )
));

这对我来说似乎有点干净,而且您不必担心在AppModel中使用可包含的行为。值得考虑。