如何在API Platform中按评分平均值过滤Games实体数据?
错误原因
你的HAVING子句写法不符合DQL语法要求:andHaving()方法需要传入一个返回布尔值的判断表达式,但你只写了avg('r.value')这个聚合计算结果,没有指定比较规则(比如大于等于多少分),DQL解析器在等待你输入比较符和比较值的时候,遇到了后续自动拼接的ORDER BY语句,就抛出了语法错误。
解决方案
根据你的业务需求选择对应修改方案:
方案1:筛选平均评分不低于指定阈值的游戏
比如请求/games?rate=4就返回平均评分≥4分的游戏,修改apply方法中的查询构造逻辑:
public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null, array $context = []) { if (isset($context['filters']) && is_array($context['filters'])) { $rate = $context['filters'][$this->parameterName] ?? null; if (!$rate || !is_numeric($rate)) { return; } } else { return; } // 生成唯一别名避免字段冲突 $avgRateAlias = $queryNameGenerator->generateParameterName('avg_rate'); $thresholdParam = $queryNameGenerator->generateParameterName('rate_threshold'); $queryBuilder ->join('o.rates', 'r') // HIDDEN关键字表示这个字段不参与结果返回,只用于查询逻辑 ->addSelect(sprintf('AVG(r.value) AS HIDDEN %s', $avgRateAlias)) ->groupBy('o.id') ->andHaving(sprintf('%s >= :%s', $avgRateAlias, $thresholdParam)) ->setParameter($thresholdParam, $rate) // 可选:按平均分从高到低排序 ->orderBy($avgRateAlias, 'DESC'); }
方案2:仅按平均评分从高到低排序返回游戏
如果参数rate只是触发排序的开关,不需要指定阈值,修改逻辑如下:
public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null, array $context = []) { if (isset($context['filters']) && is_array($context['filters'])) { if (!isset($context['filters'][$this->parameterName])) { return; } } else { return; } $avgRateAlias = $queryNameGenerator->generateParameterName('avg_rate'); $queryBuilder ->join('o.rates', 'r') ->addSelect(sprintf('AVG(r.value) AS HIDDEN %s', $avgRateAlias)) ->groupBy('o.id') ->orderBy($avgRateAlias, 'DESC'); }
额外注意
- 你当前请求参数传的是
?rate=game,如果是要按阈值筛选,需要传数字类型的参数值,比如?rate=3.5 - 如果需要支持筛选平均分区间,可以扩展参数格式,比如传入数组
?rate[gte]=4&rate[lte]=5做对应逻辑处理即可
内容的提问来源于stack exchange,提问作者kevin73
相关产品推荐
相关产品推荐

