I've got 3 models: Order
, OrderItem
and Product
(for simplicity just showing the relationships):
class Order extends BaseModel
{
use Uuid;
protected $casts = [
'status' => OrderStatuses::class,
];
/**
* An Order has multiple OrderItems associated to it.
* @return HasMany
*/
public function orderItems(): HasMany
{
return $this->hasMany(OrderItem::class);
}
class OrderItem extends BaseModel
{
/**
* Get the Order the OrderItem belongs to.
* @return BelongsTo
*/
public function order(): BelongsTo
{
return $this->belongsTo(Order::class)
->withDefault();
}
/**
* Get the product associated to this OrderItem.
* @return HasOne
*/
public function product(): HasOne
{
return $this->hasOne(Product::class, 'id');
}
}
class Product extends BaseModel
{
use Uuid;
/**
* Get the category the product belongs to.
* @return BelongsTo
*/
public function category(): BelongsTo
{
return $this->belongsTo(Category::class, 'category_id');
}
with their respective DB tables:
orders: id, status, subtotal, total
order_items: id, order_id, product_id, qty, price, total
products: id, name, slug, sku, description, price
I've got only a controller for Order
and Product
but I do have resources for all 3:
class OrdersResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => (string)$this->id,
'type' => 'orders',
'attributes' => [
'status' => ($this->status)->value(),
'payment_type' => $this->payment_type,
'payment_transaction_no' => $this->payment_transaction_no,
'subtotal' => $this->subtotal,
'taxes' => $this->taxes,
'total' => $this->total,
'items' => OrderItemsResource::collection($this->whenLoaded('orderItems')),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
];
}
}
class ProductsResource extends JsonResource
{
public function toArray($request) : array
{
return [
'id' => $this->id,
'type' => 'products',
'attributes' => [
'barcode' => $this->barcode,
'name' => $this->name,
'slug' => $this->slug,
'sku' => $this->sku,
'description' => $this->description,
'type' => $this->type,
// todo return category object?
'category' => new CategoriesResource($this->whenLoaded('category')),
'wholesale_price' => $this->wholesale_price,
'retail_price' => $this->retail_price,
'base_picture' => ($this->base_picture ? asset('images/products/' . $this->base_picture) : null),
'current_stock_level' => $this->current_stock_level,
'active' => $this->active,
]
];
}
}
class OrderItemsResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'order_id' => $this->order_id,
'product_id' => $this->product_id,
'qty' => $this->qty,
'price' => $this->price,
'total' => $this->total,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
When hitting my orders controller I return the data (in this case for displaying an order) like this:
public function show(Order $order): JsonResponse
{
return (new OrdersResource($order->loadMissing('orderItems')))
->response()
->setStatusCode(Response::HTTP_OK);
}
So far so go, the order is returned like this:
{
"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"
}
}
}
but now I'm in need of returning the actual product as part of OrderItem
instead of just the product ID, so I updated my resource to include:
'product' => new ProductsResource($this->whenLoaded('product')),
my resource ended up looking like this:
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,
];
}
yet the product is not visible in my response, what am I missing? Isn't that the right way to load it? do i need to load it from my controller?
Thanks
CodePudding user response:
In your controller:
$order->loadMissing('orderItems.product')
CodePudding user response:
Your Product
model is missing the relationship to OrderItem
. You defined:
// In OrderItem model
public function product(): HasOne
{
return $this->hasOne(Product::class, 'id');
}
And you are missing the required inverse relationship:
// In Product model
public function orderItem(): BelongsTo
{
return $this->belongsTo(OrderItem::class);
}