Laravel Eloquent - 如何 return 范围查询内的计算值作为模型列或集合 属性

Laravel Eloquent - How to return a calculated value inside a scope query as a model column or collection property

我正在尝试使用 Laravel 的 Eloquent 获取经过身份验证的用户附近的用户列表以及 return 他们之间的距离。我使用原始 SQL 让它工作,但现在我几乎可以使用 eloquent.

使其正常工作

所以,在用户模型上我有这个方法:

public function getUsersAround($coordinates, $radius = 5)
{
    return $this->whereHas('location', function ($query) use ($coordinates, $radius) {
            $query->distance($coordinates, $radius);
        })->paginate(10);
}

然后在 Location 模型上,我有以下范围:

public function scopeDistance($query, $coordinates, $radius)
{
    return $query
        ->join('profiles', 'profiles.id', '=', 'locations.profile_id')
        ->having('distance', '<', $radius)
        ->selectRaw("profile_id,
                 (6371 * ACOS(COS(RADIANS($coordinates->latitude))
                       * COS(RADIANS(latitude))
                       * COS(RADIANS(longitude) - RADIANS($coordinates->coordinates))
                       + SIN(RADIANS($coordinates->latitude))
                       * SIN(RADIANS(latitude)))) AS distance")
        ->orderBy('distance', 'asc');
}

因此,半径内的所有用户都被正确 returned,但我还想将计算的距离添加到每个 returned 用户。

奇怪的是,如果我 dd($query->distance($coordinates, $radius)); 我看到所有用户 + 每个用户对象的新距离 属性(具有正确的值),但如果我只是 return控制器内的查询和 dd(auth()->user()->getUsersAround($coordinates, $radius)),距离 属性 不存在了。

有没有办法 return 在范围查询内计算出的距离,并在 return 用户列表时将其保存在用户对象上?

谢谢!

那是因为您在范围 内使用 get(),这是不正确的。本地范围旨在定义通用的约束集(例如用于过滤数据)。 查看 local scopes 上的文档了解更多详情。

要向 select 结果添加内容,您可以使用 addSelect.

如果要在结果中包含距离,则需要使用 distance 属性加载 location 关系。由于您已经在 Location 模型的查询范围内 selecting distance,因此您可以很容易地实现这一点:

public function getUsersAround($coordinates, $radius = 5)
{
    return $this->whereHas('location', function ($query) use ($coordinates, $radius) {
        $query->distance($coordinates, $radius);
    })->with([
        'location' => function ($query) use ($coordinates, $radius) {
            $query->select('locations.*')->distance($coordinates, $radius);
        }
    ])->paginate(10);
}

注意 with('location'),它急切地加载 Location 模型。我添加了一个 select('locations.*') 以便 location 关系将具有自己的属性,并且 distance($coordinates, $radius) 也将 select profile_iddistance属性。

因此,当您在控制器中调用 auth()->user()->getUsersAround($coordinates, $radius) 时,您可以使用 $user->location->distance:

访问 distance 属性
$users = auth()->user()->getUsersAround($coordinates, $radius);

foreach ($users as $user) {
    $distance = $user->location->distance;
}