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

为何whereHas条件不满足时PortingItem模型仍返回查询结果?

问题原因分析与解决方案

我来帮你梳理下这个问题的根源——其实是查询逻辑的优先级和分组在搞鬼!

为什么会返回porting为空的记录?

你的原查询结构是:

$result = PortingItem::whereHas(...)
    ->where(...)
    ->orWhere(...)
    ->get();

翻译成SQL的话,等价于:

SELECT * FROM porting_items
WHERE (exists (SELECT * FROM portings ...)) 
AND (phone_number_start BETWEEN ... OR phone_number_end BETWEEN ...)
OR (phone_number_start <= ... AND phone_number_end >= ...)

注意这里的OR是和前面的AND同级的!也就是说,只要最后那个orWhere的条件满足,不管前面的whereHas(关联是否存在、状态是否符合)是否成立,这条PortingItem都会被查询出来。这就导致了porting为空的记录也能被返回——因为它们可能满足最后那个号码覆盖的条件。

修正后的查询方案

你的需求应该是要找同时满足两个条件的记录:

  1. 关联的Porting存在且处于指定状态
  2. 号码范围符合要求(要么起始/结束在给定区间,要么覆盖整个区间)

所以需要把所有号码相关的条件都放到同一个逻辑组里,再和whereHas做AND运算。修正后的代码如下:

$result = PortingItem::whereHas('porting', function ($query) {
    $query->whereIn('status', [
        Porting::STATUS_REQUESTED,
        Porting::STATUS_ACCEPTED,
        Porting::STATUS_DELAYED,
    ]);
})->where(function ($query) use ($numbers) {
    // 把所有号码相关的条件都放到这个闭包里,确保是OR关系
    $query->whereBetween('phone_number_start', [$numbers[0], $numbers[1]])
          ->orWhereBetween('phone_number_end', [$numbers[0], $numbers[1]])
          ->orWhere([
              ['phone_number_start', '<=', $numbers[0]],
              ['phone_number_end', '>=', $numbers[1]],
          ]);
})->get();

验证方法

如果你想确认生成的SQL是否符合预期,可以在查询末尾加上->toSql()来查看:

dd(PortingItem::whereHas(...)->where(...)->toSql());

修改后的SQL会变成:

SELECT * FROM porting_items
WHERE (exists (SELECT * FROM portings ...))
AND (
    phone_number_start BETWEEN ... 
    OR phone_number_end BETWEEN ... 
    OR (phone_number_start <= ... AND phone_number_end >= ...)
)

这样就确保了只有当关联的Porting存在且状态符合,同时号码范围满足任一条件时,才会返回结果,彻底排除了porting为空的记录。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 19:22:29