可包含的行为不会返回更深的模型关联和选择字段

时间:2013-05-06 20:21:21

标签: cakephp

我想限制使用可包含的更深层关联返回的字段。

我的协会:

游戏有很多评论

分页和可包含的代码:

$this->paginate = array(
            'conditions' => $conditions,
            'fields' => array(
                'Game.id', 'Game.name',
                'Publisher.id', 'Publisher.name'
            ),
            'contain' => array(
                'Game' => array(                     
                    'Review' => array(
                        'fields' => array('Review.id', 'ROUND(AVG(Review.score),1)')
                    )
                ),
            )
        );
$games = $this->paginate('Game');

目前,返回Review表中的所有字段。永远不会返回'ROUND(AVG(Review.score),1)'。如何指定从Review关联返回的字段?


使用@theJetzah的答案,对两个搜索结果

SQL转储。第一个是搜索一个游戏,第二个是搜索返回三个游戏。

SELECT `Review`.`id`, `Review`.`review_text`, `Review`.`score`, `Review`.`user_id`, `Review`.`game_id`, `Review`.`created`, `Review`.`platform_id`, (ROUND(AVG(`Review`.`score`),1)) AS `Review__average_score` FROM `videogamedb`.`reviews` AS `Review` WHERE `Review`.`game_id` = (55)

SELECT `Review`.`id`, `Review`.`review_text`, `Review`.`score`, `Review`.`user_id`, `Review`.`game_id`, `Review`.`created`, `Review`.`platform_id`, (ROUND(AVG(`Review`.`score`),1)) AS `Review__average_score` FROM `videogamedb`.`reviews` AS `Review` WHERE `Review`.`game_id` IN (55, 56, 57)

2 个答案:

答案 0 :(得分:0)

我认为您的数组配置有点错误,请尝试:

$this->paginate = array(
    'Game' => array(
        'conditions' => $conditions,
        'fields' => array(
            'Game.id', 'Game.name',
            'Publisher.id', 'Publisher.name'
        ),
        'contain' => array(
            'Review' => array(
                'fields' => array('Review.id', 'ROUND(AVG(Review.score),1)')
            )
        )
    )
);
$games = $this->paginate('Game');

顺便说一句,根据个人经验,在查询中指定字段并不总能加快速度(当然对于少量字段),假设这是这样做的动机。它确实减少了内存占用,但这只相对于记录的原始大小和返回的记录数。

答案 1 :(得分:0)

不是完整的答案,而是试图让它发挥作用:)

Approach1(UPDATE:Containable不支持'group by')

首先,尝试将“Game”模型添加到Controller的$uses数组中(如果尚未包含),并重新组织分页数组(如Sam先前所建议的那样),所以你会对游戏模型本身进行分页。

然后,为计算得分创建虚拟字段可能会有所帮助,但“评论”的结果需要进行分组,否则您将无法计算平均分数。

我无法测试这个,但值得尝试

像这样的东西;

public $uses = array(
    'Game',
    // other models
);

public function myfunction()
{
    $this->Game->Review->virtualFields['average_score'] = 'ROUND(AVG(Review.score),1)';

    $this->paginate = array(
        'Game' => array(
            'fields' => array(
                'Game.id',
                'Game.name',
                'Publisher.id',
                'Publisher.name'
            ),
            'contain' => array(
                'Review' => array(
                    'fields' => array(
                        'Review.game_id,
                        'Review.average_score',
                    ),
                    'group' => array(
                        'Review.game_id,
                    ),
                )
            )
        )
    );

    // Conditions can be passed to paginate,
    // that way you can specify 'paginate' at
    // one place and don't have to modify it
    // to include the conditions
    $games = $this->paginate('Game', $conditions);
}

替代方法:使用连接和数据库视图

显然,Containable行为不喜欢group-by子句;有关详细信息,请参阅此票证:Containable behavior does not implement 'group' option

CakePHP允许您手动指定联接:Joining Tables

为简化操作并防止必须为所有字段添加“分组依据”,请在数据库中创建一个简单的数据库视图;

CREATE VIEW review_scores AS
   SELECT 
       game_id, 
       ROUND(AVG(score),1) AS average_score,
       COUNT(id) AS total_reviews
   FROM
       reviews
   GROUP BY
      game_id;

如果你不熟悉这个;数据库“视图”基本上是一个“存储查询”,可以像访问常规表一样进行访问。见Create View

然后,使用新创建的数据库视图作为源表,使用“手动”连接。在你的情况下,这看起来像这样;

$this->paginate = array(
    'Game' => array(
        'fields' => array(
            'Game.id',
            'Game.name',
            'Publisher.id',
            'Publisher.name',
            'ReviewScore.average_score',
            'ReviewScore.total_reviews',
        ),
        'joins' => array(
            array(
                'table' => 'review_scores',
                'alias' => 'ReviewScore',
                'type'  => 'LEFT',
                'conditions' => array(
                    'ReviewScores.game_id = Game.id',
                )
            )
        )
    )
);

希望这有帮助