Laravel 5.6中嵌套关系的扁平化结果

时间:2018-07-25 15:29:42

标签: php laravel eloquent

我有一个具有以下模型的laravel应用程序

Location (hasOne: Device)  : [id, device_id, lat, lng] 
Device (hasMany: Location) : [id, name]

我需要获取给定设备的最新位置。假设我有一组设备ID。

[1, 2, 3]

我正在尝试制作一个API,该API应该为请求提供平坦的响应。显然,响应应如下所示。

[
    {
    "device_id": "1",
    "name": "A",
    "lat":"8.311",
    "lng":"11.1111"
    },
    {
    "device_id": "2",
    "name": "B",
    "lat":"8.1111",
    "lng":"88.1111"
    },
    {
    "device_id": "3",
    "name": "C",
    "lat":"5.1111",
    "lng":"25.1111"
    }
]

我尝试了许多方法来找到有效的解决方案以获得正确的答案。到目前为止,我能想到的最好的解决方案是使用Locationdevice_id表中直接获取最新位置。所以我尝试了这个。

Location::whereIn('device_id', [1, 2, 3])->get()

给出给定设备的所有位置条目。但是我只想要每个设备的最新位置条目。所以我尝试了这个。

Location::whereIn('device_id', [1, 2])->latest()->first()->get()

显然,每个设备都给我多个位置。我不确定该如何解决。

>>> Location::whereIn('device_id', [1, 2])->get()
=> Illuminate\Database\Eloquent\Collection {#2883
     all: [
       App\Location {#2895
         id: 1,
         device_id: 1,
         lat: "39",
         lng: "53",
         extras: "JCRskRtaBJ",
         delete_flag: 0,
         created_at: "2018-07-25 12:11:21",
         updated_at: "2018-07-25 12:11:21",
       },
       App\Location {#2903
         id: 3,
         device_id: 1,
         lat: "73",
         lng: "23",
         extras: "0cOy8rne9S",
         delete_flag: 0,
         created_at: "2018-07-25 12:11:21",
         updated_at: "2018-07-25 12:11:21",
       },
       App\Location {#2894
         id: 5,
         device_id: 2,
         lat: "55",
         lng: "28",
         extras: "rT18VZxA7i",
         delete_flag: 0,
         created_at: "2018-07-25 12:11:21",
         updated_at: "2018-07-25 12:11:21",
       },
       App\Location {#2885
         id: 7,
         device_id: 1,
         lat: "58",
         lng: "74",
         extras: "TAbM4x53iH",
         delete_flag: 0,
         created_at: "2018-07-25 12:11:21",
         updated_at: "2018-07-25 12:11:21",
       },
       App\Location {#2900
         id: 8,
         device_id: 1,
         lat: "47",
         lng: "7",
         extras: "yhr4sbUs2R",
         delete_flag: 0,
         created_at: "2018-07-25 12:11:21",
         updated_at: "2018-07-25 12:11:21",
       },
       App\Location {#2882
         id: 9,
         device_id: 2,
         lat: "27",
         lng: "60",
         extras: "NqRPvaqosG",
         delete_flag: 0,
         created_at: "2018-07-25 12:11:21",
         updated_at: "2018-07-25 12:11:21",
       },
       App\Location {#2915
         id: 10,
         device_id: 1,
         lat: "35",
         lng: "37",
         extras: "6e7yHT4qP3",
         delete_flag: 0,
         created_at: "2018-07-25 12:11:21",
         updated_at: "2018-07-25 12:11:21",
       },
     ],
   }

我正在寻找一个查询,该查询仅从location类返回latlng并将其附加为属性,以便获得平坦的json响应。反正有可能做到吗?

2 个答案:

答案 0 :(得分:3)

您可以使用Eloquent: API Resources获得所需的内容:

首先在Device模型中创建一个新关系,即使hasMany是它们之间的真实关系,您也可以将hasOnelatest()一起使用以获取最后一个位置:

public function lastLocation()
{
    return $this->hasOne('App\Location')->latest();
}

然后,您必须创建一个设备资源:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class Device extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'device_id' => $this->id,
            'name' => $this->name,
            'lat' => $this->lastLocation->lat,
            'lng' => $this->lastLocation->lng,
        ];
    }
}

最后,您可以在控制器中执行以下操作:

use App\Device;
use App\Http\Resources\Device as DeviceResource;

function YourMethod() {
    $devices = Device::with('lastLocation')
                       ->whereIn([1, 2, 3])
                       ->get();
    return DeviceResource::collection($devices);
}

文档中提到的另一件事:

  

默认情况下,当   资源响应转换为JSON。因此,例如   资源收集响应如下所示:

{
    "data": [
        {
            "id": 1,
            "name": "Eladio Schroeder Sr.",
            "email": "therese28@example.com",
        },
        {
            "id": 2,
            "name": "Liliana Mayert",
            "email": "evandervort@example.com",
        }
    ]
}

要删除数据包装器:

  

如果您想禁用最外面的资源的包装,   您可以在基础资源类上使用noWrapping方法。   通常,您应该从AppServiceProvider调用此方法,或者   在向您的每个请求中加载的另一个服务提供商   应用程序:

public function boot()
{
    Resource::withoutWrapping();
}

答案 1 :(得分:1)

关键是将file:groupBy一起使用。然后仅将您想要的列传递给latest。然后根据结果集返回响应,并根据结果返回状态码。

get