CakePHP Containable:加载太多关系


CakePHP Containable: Loading too much relations?

我是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中,一些关联(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: 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;通过这样做,蛋糕将只加载我们在包含数组()中设置的模型。由于