Laravel使用whereHas与with后关联关系为空的问题
问题诊断与解决方案
我一眼就发现问题出在你的scopeGetByDistance方法里——你用selectRaw直接指定了要查询的字段,这会覆盖Eloquent默认的全字段查询逻辑,导致预加载的addresses缺少关键的关联字段(比如id和user_id),Eloquent没法把这些地址和用户匹配起来,所以关联结果才会是空的。
为什么会出现这个问题?
当你在with的闭包里调用getByDistance时,这个scope里的selectRaw('lat, lng, ...')只返回了三个字段:lat、lng和计算出的distance。但Eloquent需要地址模型的主键(比如id)以及关联外键(比如user_id)来建立用户和地址之间的关联关系,没有这些字段,Eloquent无法识别这些地址属于哪个用户,自然就不会把它们填充到addresses关联属性里。
而你的whereHas部分其实是正常工作的——它能正确筛选出存在符合距离条件地址的用户,因为whereHas只关心子查询是否有结果,不关心返回的字段内容。
修复方案
把scope里的selectRaw改成addSelect,这样就能在保留模型所有默认字段的基础上,额外添加计算的distance字段:
public static function scopeGetByDistance($query, $lat, $lng, $max_distance) { return $query->addSelect([ 'lat', 'lng', \DB::raw('( 3959 * acos( cos( radians( '.$lat.') ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(' . $lng . ') ) + sin( radians(' . $lat .') ) * sin( radians(lat) ) ) ) AS distance') ]) ->having('distance', '<', $max_distance ) ->orderBy( 'distance', 'ASC' ); }
另外建议你用参数绑定来避免SQL注入风险(直接把变量拼进SQL里存在安全隐患),优化后的scope代码如下:
public static function scopeGetByDistance($query, $lat, $lng, $max_distance) { return $query->addSelect([ 'lat', 'lng', \DB::raw('( 3959 * acos( cos( radians( ? ) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians( ? ) ) + sin( radians( ? ) ) * sin( radians( lat ) ) ) ) AS distance', [$lat, $lng, $lat]) ]) ->having('distance', '<', $max_distance ) ->orderBy( 'distance', 'ASC' ); }
修改之后再运行你的查询,应该就能看到ID为191的用户的符合条件地址被正确加载出来了。
内容的提问来源于stack exchange,提问作者Adam
相关产品推荐
相关产品推荐

