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

Posted

技术标签:

【中文标题】Laravel Eloquent - 如何将范围查询内的计算值作为模型列或集合属性返回【英文标题】:Laravel Eloquent - How to return a calculated value inside a scope query as a model column or collection property 【发布时间】:2020-10-20 17:53:33 【问题描述】:

我正在尝试获取经过身份验证的用户附近的用户列表,并使用 Laravel 的 Eloquent 返回他们之间的距离。我使用原始 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');

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

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

有没有办法在返回用户列表时返回范围查询内的计算距离并将其持久化到用户对象上?

谢谢!

【问题讨论】:

【参考方案1】:

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

要向选择结果添加内容,您可以使用addSelect

【讨论】:

你对 get() 的看法是对的,我忘了从示例中删除它。我把它放在那里只是为了在使用dd($query-&gt;distance($coordinates, $radius)); 时查看结果但是,根据我已经编写的代码,我找不到使用 addSelect 的方法。你能给我举个例子吗? @DanielR。只需遵循 Laravel 的官方文档即可。 link 不幸的是,您的解决方案不起作用,因为我无法按照您的建议从本地范围内获取距离并将其添加到 addSelect() 中,但感谢您的尝试。【参考方案2】:

如果您想在结果中包含距离,您需要使用distance 属性加载location 关系。由于您已经在 Location 模型的查询范围内选择了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) 也会选择profile_iddistance 属性。

因此,当您在控制器中调用 auth()-&gt;user()-&gt;getUsersAround($coordinates, $radius) 时,您可以使用 $user-&gt;location-&gt;distance 访问 distance 属性:

$users = auth()->user()->getUsersAround($coordinates, $radius);

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

【讨论】:

您的解决方案有效!然而,我不得不稍微调整一下,因为with() 方法需要一个关联数组来约束急切的负载。感谢您的帮助! 感谢您的编辑,确实需要关联数组语法!

以上是关于Laravel Eloquent - 如何将范围查询内的计算值作为模型列或集合属性返回的主要内容,如果未能解决你的问题,请参考以下文章

laravel 的 Eloquent ORM 查询前50条数据 怎么查

PHP笔记-laravel框架中Eloquent ORM实现增删改查

Laravel / Eloquent 模型属性可见性

Laravel Eloquent在相关模型上按范围获取模型

Laravel Eloquent 在 JSON 列中查找日期在某个范围内的所有记录

如何将此 Laravel PHP 代码简化为一个 Eloquent 查询?