You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Yii框架Users模型的getRating方法仅执行一次查询?

解决查询重复执行的问题

问题根源

你当前的getRating()方法每次被调用都会触发一次数据库查询,若视图中该方法被多次调用(比如模板解析时重复计算表达式),就会执行多次查询。另外代码存在SQL注入风险,直接把$id拼到SQL里是严重的安全问题。

改写方案

方案1:缓存查询结果到模型属性

在Users模型里加个私有属性存储查询结果,避免重复执行:

class Users extends \yii\db\ActiveRecord
{
    private $_rating = null; // 缓存评分结果

    public function getRating()
    {
        if ($this->_rating === null) {
            // 用参数绑定替代字符串拼接,彻底避免SQL注入
            $this->_rating = \Yii::$app->db->createCommand(
                "SELECT * FROM (
                    SELECT *, (@position:=@position+1) as rate FROM (
                        SELECT executor_id, SUM(rate) / COUNT(rate) as pts
                        FROM user_replies, (SELECT @position:=0) as a
                        GROUP BY executor_id ORDER BY pts DESC
                    ) AS subselect
                ) as general WHERE executor_id = :id",
                [':id' => $this->id] // 用模型自身ID替代从request取,逻辑更合理
            )->queryOne();
        }
        return $this->_rating;
    }
}

方案2:用Yii查询构建器重构(更符合框架规范)

用Yii自带的查询构建器改写,可读性和可维护性更好:

class Users extends \yii\db\ActiveRecord
{
    private $_rating = null;

    public function getRating()
    {
        if ($this->_rating === null) {
            // 第一步:计算所有执行者的平均分
            $avgQuery = (new \yii\db\Query())
                ->select(['executor_id', 'SUM(rate)/COUNT(rate) as pts'])
                ->from('user_replies')
                ->groupBy('executor_id')
                ->orderBy('pts DESC');

            // 第二步:计算排名
            $rankQuery = (new \yii\db\Query())
                ->select(['*', '(@position:=@position+1) as rate'])
                ->from(['subselect' => $avgQuery])
                ->crossJoin(['(SELECT @position:=0) as a']);

            // 第三步:筛选当前用户的排名数据
            $this->_rating = (new \yii\db\Query())
                ->select('*')
                ->from(['general' => $rankQuery])
                ->where(['executor_id' => $this->id])
                ->createCommand()
                ->queryOne();
        }
        return $this->_rating;
    }
}

视图调用保持不变

视图里依然可以用原来的写法:

<?php echo $singleUser->getRating()['rate']; ?>

现在不管调用多少次getRating(),只会执行一次数据库查询。

额外提醒:原来从request取id的逻辑不合理,因为Users模型对应具体的用户实例,直接用$this->id才是面向对象的正确做法,还能避免请求参数和模型实例不匹配的问题。

内容的提问来源于stack exchange,提问作者user17045604

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 19:54:20