我是cakephp的containable元素的超级粉丝,因为我一直认为,它可以适当地处理附加模型的加载。但在最后几天,我深入研究发现,这真的是一个记忆问题。
考虑以下模型结构:
- 项目有很多墙
- 项目有多个参与者
- 墙上有很多Post
- 帖子有很多评论
- 参与者有多个Post
- 参与者有很多评论
- 参与者属于用户
- 参与者属于项目
,反之
- 帖子属于参与者
- 帖子属于墙
- 评论属于参与者
- 评论属于Post
- 用户有多个参与者
在wall-Controller中,我有以下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 &的参与者。User对象,我将包含数组更改为Participant.User。但是,再一次,现在我不仅得到了User对象,还得到了所有其他与Participant相关的对象(项目,帖子,评论,…),并且对象树比以前大得多。
所以我想知道,什么是正确的方式来实现这一点?我是否需要显式设置"连接"选项,或者我是否必须设置字段选项(在根或在包含选项)?
来自奥地利的问候。
停止使用Containable。它确实产生了太多的查询。使用joins
语法。看看这个问题,看看那篇食谱文章。
<标题>连接表
在SQL中,可以使用JOIN
语句组合相关的表。这允许您跨多个表执行复杂的搜索(即:搜索给定多个标签的帖子)。
在CakePHP中,一些关联(belongsTo
和hasOne
)执行自动连接来检索数据,因此您可以根据相关关联中的数据发出查询来检索模型。
但hasMany
和hasAndBelongsToMany
的关联不是这样。这时强制连接就可以派上用场了。您只需要定义必要的连接来组合表,并为您的查询获得所需的结果。
请记住,您需要将递归设置为-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
: join类型:inner, left or right. -
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中执行多个连接:
假设Book 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);
对Containable行为使用join可能会导致一些SQL错误(重复的表),所以如果您的主要目标是基于相关数据执行搜索,则需要使用join方法作为Containable的替代方法。Containable最适合于限制find语句带来的相关数据的数量。
标题>我一直在解决同样的问题,然后我发现,在一些模型中,我设置后查找回调和另一个SQL查询,这使得混乱的可包含的行为。因此,我建议将回调中的内部查询重写为普通SQL,这对我有帮助,并解决了可包含的链。
理论上,这应该没问题。确保你设置了
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"))
)
)
);
这会有帮助吗?
$this->loadModel('Participant');
$this->Participant->bindModel(array(
'belongsTo' => array(
'User' => array(
'className' => 'Wish',
'foreignKey' => 'user_id'
)
)
),0);
<>前$ this ->设置("文章",$ this ->>后发现("所有",阵列("conditions" => array("Post。wall_id" => $wall[" wall "]["id"])," contains " => false)));我在我的一个项目中使用ContainableBehaviour,它完全如预期的那样工作,即使有大量的关系。我认为在您的情况下,由于以下关系,它不能正常工作:
- 参与者有多个Post
- 参与者有很多评论
- 参与者属于Post
- 与会者属于备注
为什么参与者属于Post?为什么参与者属于注释?
当你摆脱这些关系时,是否包含预期的工作?
如果您需要这些所有这些关系,并正确设置了您的HABTM关系。查看这篇文章,它解释了如何将ContainableBehaviour与HABTM关系一起使用。
我从未使用过点语法。
"contain" => array('Participant'=>array('User'))
?
对我来说,看起来可容纳的行为在某种程度上也没有附加。你是如何激活你的行为的?
首先要做的是在父模型中,像这样将适当递归设置为-1$this->recursive = -1;
通过这样做,蛋糕将只加载我们在包含数组()中设置的模型。由于