CakePHP包含:加载太多关系?

时间:2011-11-15 11:20:04

标签: php cakephp cakephp-1.3 cakephp-appmodel

我是cakephp可容纳元素的忠实粉丝,因为我一直认为,它会处理适当的其他模型的加载。但在最后几天,我深入挖掘并发现,确实存在记忆问题。

考虑以下模型结构:

  • 项目有很多墙
  • 项目有很多参与者
  • Wall有很多帖子
  • 帖子有很多评论
  • 参与者有很多帖子
  • 参与者有很多评论
  • 参与者属于用户
  • 参与者属于Project

反之亦然

  • 帖子属于参与者
  • 帖子属于Wall
  • 评论属于参与者
  • 评论属于Post
  • 用户有很多参与者

在墙控制器中,我有以下find-Statement:

$this->set(
  "posts", 
  $this->Post->find(
    "all", array(
      "conditions" => array("Post.wall_id" => $wall["Wall"]["id"]), 
      "contain" => array("Participant")
    )
  )
);

我希望,cakephp会找到所有帖子,只包含相应的参与者对象。但是,我得到的是所有帖子的列表(正确)&他们的参与者(正确),但也相应的墙(不正确)&相应的评论,如果可用(不正确)。所以从性能的角度来看:太多的对象,这可能导致“致命的错误 - 内存过载”。

而对于,理论上真正性感的&有趣的部分:

$this->set(
  "posts", 
  $this->Post->find(
    "all", array(
      "conditions" => array("Post.wall_id" => $wall["Wall"]["id"]), 
      "contain" => array("Participant.User")
    )
  )
);

因为我只对Post&amp ;;感兴趣 Participant.User 对象,我将包含数组更改为Participant.User。但是,现在我不仅要获得User对象,还要获得参与者的所有其他相关对象(项目,帖子,评论......),并且对象树比以前大得多。

所以我想知道,实现这个的正确方法是什么?我是否需要显式设置“join”选项,还是必须设置fields选项(在root或者contains-option中)?

来自奥地利的问候。

7 个答案:

答案 0 :(得分:1)

停止使用Containable。它确实会生成太多查询。使用joins语法。请查看this问题和食谱article

<强>更新

加入表格

在SQL中,您可以使用JOIN语句组合相关表。这允许您跨多个表执行复杂搜索(即:给出多个标签的搜索帖子)。

在CakePHP中,一些关联(belongsTohasOne)执行自动连接以检索数据,因此您可以发出查询以根据相关数据中的数据检索模型。

hasManyhasAndBelongsToMany关联并非如此。这是强迫联接来救援的地方。您只需定义必要的联接以组合表并获得所需的查询结果。

请记住,您需要将递归设置为-1才能生效。我:$this->Channel->recursive = -1;

要强制表之间的连接,您需要使用Model::find()的“现代”语法,向‘joins’数组添加$options键。例如:

$options['joins'] = array(
    array('table' => 'channels',
        'alias' => 'Channel',
        'type' => 'LEFT',
        'conditions' => array(
            'Channel.id = Item.channel_id',
        )
    )
);

$Item->find('all', $options);

请注意,‘join’数组不是键控的。

在上面的例子中,名为Item的模型保持连接到channels表。您可以使用Model名称对表进行别名,以便检索的数据符合CakePHP数据结构。

定义连接的键如下:

  • table:联接表。
  • alias:表格的别名。与表关联的模型的名称是最好的选择。
  • type:联接类型:内部,左侧或右侧。
  • conditions:执行加入的条件。

使用联接,您可以根据相关模型字段添加条件:

$options['joins'] = array(
    array('table' => 'channels',
        'alias' => 'Channel',
        'type' => 'LEFT',
        'conditions' => array(
            'Channel.id = Item.channel_id',
        )
    )
);

$options['conditions'] = array(
    'Channel.private' => 1
);

$privateItems = $Item->find('all', $options);

您可以根据需要在hasBelongsToMany中执行多个连接:

假设书籍hasAndBelongsToMany标签关联。此关系使用books_tags表作为连接表,因此您需要将books表连接到books_tags表,并使用tags表连接:

$options['joins'] = array(
    array('table' => 'books_tags',
        'alias' => 'BooksTag',
        'type' => 'inner',
        'conditions' => array(
            'Books.id = BooksTag.books_id'
        )
    ),
    array('table' => 'tags',
        'alias' => 'Tag',
        'type' => 'inner',
        'conditions' => array(
            'BooksTag.tag_id = Tag.id'
        )
    )
);

$options['conditions'] = array(
    'Tag.tag' => 'Novel'
);

$books = $Book->find('all', $options);

使用具有可包含行为的联接可能会导致一些SQL错误(重复的表),因此如果您的主要目标是基于相关数据执行搜索,则需要使用联接方法作为Containable的替代方法。 Containable最适合限制find语句带来的相关数据量。

答案 1 :(得分:0)

理论上,这应该可以正常工作。确保已设置

var $actsAs = array('Containable');

您正在引用的每个模型。

编辑:仔细观察,也许不是。请尝试以下方法......

$this->set(
  "posts", 
  $this->Post->find(
    "all", array(
      "conditions" => array("Post.wall_id" => $wall["Wall"]["id"]), 
      "contain" => array("Participant" => array("User"))
    )
  )
);

答案 2 :(得分:0)

这是否有助于:

$this->loadModel('Participant');
                $this->Participant->bindModel(array(
                    'belongsTo' => array(
                        'User' => array(
                            'className' => 'Wish',
                            'foreignKey' => 'user_id'
                        )
                    )
                ),0);
   
 $this->set(
  "posts", 
  $this->Post->find(
    "all", array(
      "conditions" => array("Post.wall_id" => $wall["Wall"]["id"]), 
      "contain" => false
    )
  )
);

答案 3 :(得分:0)

我在我的一个项目中使用ContainableBehaviour,即使有大量关系,它也能正常工作。我认为由于以下关系,它在您的情况下无法正常工作:

  • 参与者有很多帖子
  • 参与者有很多评论
  • 参与者属于Post
  • 参与者属于评论

为什么参与者属于帖子? 为什么参与者属于评论?

当你摆脱这些关系时,是否包含了预期的工作?

如果您需要这些所有这些关系,并且已正确设置HABTM关系。检查this post,其中解释了如何使用HABTM关系使用ContainableBehaviour。

答案 4 :(得分:0)

我从未使用过Dot Syntax。 你试过吗

"contain" => array('Participant'=>array('User'))

对我而言,似乎无法以某种方式附加可包含的行为。 你是如何附加/激活你的行为的?

答案 5 :(得分:0)

我一直在解决同样的问题然后我发现在某些模型中我设置了afterFind回调并在另一个SQL查询中,这使得可容纳的行为变得混乱。 所以我建议在回调中将内部查询重写为纯SQL,对我有帮助,并解决了可包含的链接。

答案 6 :(得分:0)

首先要做的是在你的父模型中,将礼节递归设置为-1 $this->recursive = -1; 通过做蛋糕将只加载我们在包含array()中设置的模型。 感谢