【问题标题】:CakePHP Containable: Loading too much relations?CakePHP Containable:加载太多关系?
【发布时间】:2011-12-29 10:49:03
【问题描述】:

我是 cakephp 的可包含元素的忠实粉丝,因为我一直认为它可以处理适当的附加模型的加载。但在最后几天,我深入挖掘并发现,确实存在内存问题。

想想下面的模型结构:

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

反之亦然

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

在墙控制器中,我有以下 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 和 Participant.User 对象感兴趣,所以我将包含数组更改为 Participant.User。但是,现在我不仅获得了 User 对象,还获得了 Participant 的所有其他相关对象(项目、帖子、cmets 等),并且对象树比以前大得多。

所以我想知道,实现这一点的正确方法是什么?我需要明确设置“加入”选项还是必须设置字段选项(在根或包含选项中)?

来自奥地利的问候。

【问题讨论】:

  • 似乎包含根本不起作用...只是为了检查一下,您是否将可包含行为添加到模型中?
  • 嗨,当然,所有模型都设置了行为,但如果我将其注释掉,它并没有什么不同。
  • 您可能还需要将 recursive = -1 设置为您的 AppModel 默认值。
  • 在 AppModel 中设置递归完全没有区别。 :-(
  • 您好,您对此有什么发现吗?

标签: php cakephp cakephp-1.3 cakephp-model


【解决方案1】:

首先要做的是在你的父模型中,像这样将适当的递归设置为 -1 $this->recursive = -1; 通过这样做,蛋糕将仅加载我们在包含数组()中设置的模型。 谢谢

【讨论】:

    【解决方案2】:

    停止使用 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 的模型左连接到通道表。您可以使用 Model 名称为表命名,因此检索到的数据符合 CakePHP 数据结构。

    定义连接的键如下:

    • table:连接的表。
    • alias:表的别名。最好选择与表关联的模型名称。
    • type:连接类型:内、左、右。
    • conditions:执行join的条件。

    通过连接,您可以根据相关模型字段添加条件:

    $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 最适合限制 find 语句带来的相关数据量。

    【讨论】:

    • 从死链接添加文本。
    【解决方案3】:

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

    【讨论】:

    • 从模型中删除所有其他内容后,我发现了同样的问题。不幸的是,afterFind 功能的文档记录很差,我不知道为什么其他查询会触发不需要的行为。
    【解决方案4】:

    我从未使用过点语法。 你试过了吗

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

    ?

    对我来说,似乎也没有附加可包含的行为。 你是如何附加/激活你的行为的?

    【讨论】:

    • 嗨,马克,尝试了嵌套数组语法,但结果相同。在每个模型中使用 $actsAs = array("Containable"); 设置容器
    • 好的,试试调试($this->Model->attached('Containable'));它返回真实吗?我认为您在错误的模型上使用了可包含。“在墙控制器中”=>您不应该使用 $this->Wall->Post->... 那么?
    • $this->Post->Behavior->attached("Containable") 返回 1。$this->Post 或 $this->Wall->Post 的区别在哪里?
    • 如果你使用正确的模型链,你不应该在你的控制器中有 $this->Post。
    • 为了方便起见,我将所有模型都包含在 app-controller $uses 数组中。但是从那里删除模型并调用 $this->Wall->Post->find(...) 也不会改变输出中的任何内容。
    【解决方案5】:

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

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

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

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

    如果您需要所有这些关系并正确设置了HABTM 关系。检查你this post,它解释了如何将 ContainableBehaviour 与 HABTM 关系一起使用。

    【讨论】:

    • 很抱歉,将反之亦然的关系更改为正确的实现。不幸的是,我以错误的顺序复制粘贴了关系。
    【解决方案6】:

    这是否有帮助:

    $this->loadModel('Participant');
                    $this->Participant->bindModel(array(
                        'belongsTo' => array(
                            'User' => array(
                                'className' => 'Wish',
                                'foreignKey' => 'user_id'
                            )
                        )
                    ),0);
    
     $this->set(
      “帖子”,
      $this->发布->查找(
        “全部”,数组(
          "条件" => 数组("Post.wall_id" => $wall["Wall"]["id"]),
          “包含” => 假
        )
      )
    );
    

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

      • 嗨内森,谢谢您的回复。当然,我在每个模型中都包含了 actAs (但如果我将其注释掉,它没有区别)。提到的对嵌套数组的更改是我的第一次尝试,但根本没有改变所描述的行为。
      猜你喜欢
      • 1970-01-01
      • 2011-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-06
      • 1970-01-01
      • 2023-04-03
      相关资源
      最近更新 更多