Laravel:将含子对象数组的JSON解析为带OrderItem实例的Order模型
问题描述
我在Laravel应用中收到以下格式的JSON数据:
{ "name": "order 1", "customer": "cus123", "orderItems": [ { "amount": 1, "name": "cola", "price": "2.10" }, { "amount": 3, "name": "fanta", "price": "2.00" } ] }
我已经创建了Order和OrderItem两个模型,希望将该JSON解析为Order实例$order。目前在OrderController中通过以下代码实现基础解析:
class OrderController extends Controller { public function store(Request $request) { $order = new Order(); $order->forceFill($request->toArray()); } }
现在可以访问$order->name、$order->customer等属性,但$order->orderItems返回的是普通数组而非OrderItem实例数组。尝试在Order模型中配置$casts未成功,请问如何实现$order->orderItems返回OrderItem实例数组?
解决方案
方法一:利用Eloquent关联关系+批量创建
这是Laravel中处理关联模型的标准方式,适合需要将数据持久化到数据库的场景:
1. 配置模型关联
在Order模型中定义一对多关联:
class Order extends Model { // 其他模型代码... public function orderItems() { return $this->hasMany(OrderItem::class); } }
在OrderItem模型中定义反向关联(可选,但推荐):
class OrderItem extends Model { // 其他模型代码... public function order() { return $this->belongsTo(Order::class); } }
2. 修改控制器逻辑
先保存主订单,再批量创建关联的OrderItem实例:
class OrderController extends Controller { public function store(Request $request) { // 创建并保存主订单,排除orderItems字段 $order = Order::create($request->except('orderItems')); // 将请求中的orderItems数组转换为OrderItem实例集合 $orderItems = collect($request->input('orderItems'))->map(function ($item) { // 转换price为数值类型(适配数据库decimal字段) $item['price'] = (float)$item['price']; return new OrderItem($item); }); // 批量保存关联的订单商品 $order->orderItems()->saveMany($orderItems); // 加载关联后,$order->orderItems即为OrderItem实例数组 return $order->load('orderItems'); } }
方法二:自定义模型转换(Cast)
如果不需要立即持久化数据,只是想将数组转为OrderItem实例数组,可以自定义Cast类:
1. 创建自定义Cast类
在app/Casts目录下创建OrderItemsCast.php(目录不存在则手动创建):
namespace App\Casts; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use App\Models\OrderItem; class OrderItemsCast implements CastsAttributes { public function get($model, $key, $value, $attributes) { if (is_null($value)) { return []; } // 将JSON字符串或数组转换为OrderItem实例数组 $items = is_string($value) ? json_decode($value, true) : $value; return collect($items)->map(function ($item) { if (isset($item['price'])) { $item['price'] = (float)$item['price']; } return new OrderItem($item); })->all(); } public function set($model, $key, $value, $attributes) { // 保存时将OrderItem实例数组转为JSON字符串 if (is_array($value) && !empty($value) && $value[0] instanceof OrderItem) { $value = collect($value)->map(fn($item) => $item->toArray())->all(); } return json_encode($value); } }
2. 在Order模型中配置Cast
class Order extends Model { protected $casts = [ 'orderItems' => \App\Casts\OrderItemsCast::class, ]; // 其他模型代码... }
3. 控制器中直接使用
此时forceFill后$order->orderItems就是OrderItem实例数组:
class OrderController extends Controller { public function store(Request $request) { $order = new Order(); $order->forceFill($request->toArray()); // 此时$order->orderItems为OrderItem实例数组 dd($order->orderItems); } }
注意:使用自定义Cast时,若要保存orderItems到数据库,需确保orders表中有order_items字段(类型为JSON或JSONB)。
内容的提问来源于stack exchange,提问作者CodeNinja
相关产品推荐
相关产品推荐

