laravel 拉瑞韦API:加载资源中的嵌套关系

x6yk4ghg  于 2022-11-18  发布在  其他
关注(0)|答案(3)|浏览(120)

我有三个模型:OrderOrderItemProduct(为简单起见,仅示出关系):
第一个
使用其各自的DB表:

orders: id, status, subtotal, total 
order_items: id, order_id, product_id, qty, price, total
products: id, name, slug, sku, description, price

我只有一个用于OrderProduct的控制器,但我有用于所有3个控制器的资源:
第一个
当点击我的订单控制器时,我返回如下数据(在本例中用于显示订单):

public function show(Order $order): JsonResponse
    {
        return (new OrdersResource($order->loadMissing('orderItems')))
            ->response()
            ->setStatusCode(Response::HTTP_OK);
    }

到此为止就这么走了,返回的顺序是这样的:

{
    "data": {
        "id": "20d9b0f3-2b32-45a7-8814-12c77210ad50",
        "type": "orders",
        "attributes": {
            "status": "new",
            "payment_type": "",
            "payment_transaction_no": "",
            "subtotal": 71000,
            "taxes": 0,
            "total": 71000,
            "items": [
                {
                    "id": 9,
                    "product_id": "444b0f3-2b12-45ab-3434-4453121231ad51",
                    "order_id": "20d9b0f3-2b32-45a7-8814-12c77210ad50",
                    "qty": 10,
                    "price": 200,
                    "total": 2000,
                    "created_at": "2022-11-05T16:26:07.000000Z",
                    "updated_at": "2022-11-05T16:28:02.000000Z"
                },
                {
                    "id": 10,
                    "product_id": "324b0f3-2b12-45ab-3434-12312330ad50",
                    "order_id": "20d9b0f3-2b32-45a7-8814-12c77210ad50",
                    "qty": 3,
                    "price": 23000,
                    "total": 69000,
                    "created_at": "2022-11-05T16:26:29.000000Z",
                    "updated_at": "2022-11-05T16:26:29.000000Z"
                }
            ],
            "created_at": "2022-11-05T16:26:07.000000Z",
            "updated_at": "2022-11-05T16:28:02.000000Z"
        }
    }
}

但现在我需要返回作为OrderItem的一部分的实际产品,而不仅仅是产品ID,因此我更新了我的资源以包括:'product' => new ProductsResource($this->whenLoaded('product')),
我资源最终看起来像这样:

public function toArray($request): array
    {
        return [
            'id' => $this->id,
            'order_id' => $this->order_id,
            'product' => new ProductsResource($this->whenLoaded('product')),
            'qty' => $this->qty,
            'price' => $this->price,
            'total' => $this->total,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }

然而,我的响应中看不到产品,我错过了什么?这不是加载它的正确方式吗?我需要从我的控制器加载它吗?
谢谢

更新

我已经更新了OrderItem中的关系,现在它看起来如下所示:

public function product(): HasOne
    {
        return $this->hasOne(Product::class, 'id', 'product_id');
    }

其中,id指向products表中的id列,而product_idorder_item中的FK。
此时如果我做以下操作:

// just get any orderItem
$orderItem = OrderItem::first();
dd($orderItem->product);

我确实看到关系在工作,因为产品正在打印,但对象仍然不是API响应的一部分,这意味着在我的资源中,此行没有按预期工作:

'product' => new ProductsResource($this->whenLoaded('product')),

更新2

我更新了在资源中加载product的方式,使其成为以下两种方式之一:
第一个
但这会在已经嵌套的对象上生成一个嵌套对象,如下所示:

{
    "data": {
        "id": "20d9b0f3-2b32-45a7-8814-12c77210ad50",
        "type": "orders",
        "attributes": {
            "status": "new",
            "payment_type": "",
            "payment_transaction_no": "",
            "subtotal": 71000,
            "taxes": 0,
            "total": 71000,
            "items": [
                {
                    "id": 9,
                    "order_id": "20d9b0f3-2b32-45a7-8814-12c77210ad50",
                    "product": {
                        "id": 9,
                        "order_id": "20d9b0f3-2b32-45a7-8814-12c77210ad50",
                        "product_id": "f6bd3290-7748-49fa-8995-e0de47291fc9",
                        "qty": 10,
                        "price": 200,
                        "total": 2000,
                        "created_at": "2022-11-05T16:26:07.000000Z",
                        "updated_at": "2022-11-05T16:28:02.000000Z",
                        "product": {
                            "id": "f6bd3290-7748-49fa-8995-e0de47291fc9",
                            "barcode": "010101010101010101",
                            "name": "Test 5",
                            "slug": "test-5",
                            "sku": "t55te345c",
                            "description": "asd asd asd asd asd",
                            "type": "goods",
                            "category_id": 4,
                            "wholesale_price": 34,
                            "retail_price": 200,
                            "base_picture": null,
                            "current_stock_level": 0,
                            "active": 1,
                            "created_at": "2022-09-23T16:29:18.000000Z",
                            "updated_at": "2022-09-23T22:00:40.000000Z"
                        }
                    },
                    "qty": 10,
                    "price": 200,
                    "total": 2000,
                    "created_at": "2022-11-05T16:26:07.000000Z",
                    "updated_at": "2022-11-05T16:28:02.000000Z"
                }
    ]
   }
  }
}

请注意,product现在如何重新拥有来自items的数据

vngu2lb8

vngu2lb81#

在控制器中:

$order->loadMissing('orderItems.product')
chhqkbe1

chhqkbe12#

有几个问题。
1.您的Product模型缺少与OrderItem的关系。您定义了:

// In OrderItem model
public function product(): HasOne
{
    return $this->hasOne(Product::class, 'id');
}

而你却缺少了所需的反关系:

// In Product model
public function orderItem(): BelongsTo
{
    return $this->belongsTo(OrderItem::class);
}

1.您的架构似乎不正确。请考虑文档中的hasOne()示例。如果UserhasOne()Phone,则:
在本例中,将自动假定Phone模型具有user_id外键
就模式而言,它意味着phones表具有user_id列。将其与OrderItemhasOne()Product)进行比较。上面示例中描述的约定意味着products表应具有order_item_id列。但实际上没有-您需要添加该列。
也许您是以另一种方式(ProducthasOneOrderItem)开始这个相关性,因为您确实有order_items.product_id数据行,但这不是您所显示相关性的必要项目。您应该移除该字段。
1.在OrderItem模型中,您已经否决了默认外键并将其指定为id

public function product(): HasOne
{
    return $this->hasOne(Product::class, 'id');
}

这意味着products表中的id列表示OrderItem ID。这没有任何意义,我猜这是不正确的。只要删除关系中的id,默认约定将意味着Laravel将在products表中查找order_item_id字段,完全按照上面2)中的描述。

public function product(): HasOne
{
    return $this->hasOne(Product::class);
}

根据我的经验,你几乎不需要覆盖Laravel的默认外键约定,除非你做了一些特别奇怪的事情。即使这样,它也足够复杂,也许值得重新考虑你的模式,只是为了让它匹配预期的约定!

ycl3bljg

ycl3bljg3#

好的,我终于想通了。感谢@鲍勃和@不要恐慌,他们的答案为我指明了正确的方向。
首先,我需要在OrderItem模型中更正我的关系定义,指定当前表和使用相关字段的表中使用的键:

public function product(): HasOne
    {
        return $this->hasOne(Product::class, 'id', 'product_id');
    }

但这本身并不足以让我的资源通过以下方式返回预期的数据:

'product' => new ProductsResource($this->whenLoaded('product')),

我还需要在控制器中加载关系:

public function show(Order $order): JsonResponse
    {
        return (new OrdersResource($order->loadMissing('orderItems', 'orderItems.product')))
            ->response()
            ->setStatusCode(Response::HTTP_OK);
    }

这是最后一步了。再次感谢你们。

相关问题