Home > database >  Laravel: Parse JSON object with array of "sub-objects" to model instance
Laravel: Parse JSON object with array of "sub-objects" to model instance

Time:02-04

In my (Laravel) application receive a JSON which looks like:

{
  "name": "order 1",
  "customer": "cus123",
  "orderItems": [
    {
      "amount": 1,
      "name": "cola",
      "price": "2.10"
    },
    {
      "amount": 3,
      "name": "fanta",
      "price": "2.00"
    },
  ]
}

I have create 2 models in Laravel, one Order and one OrderItem. I want to parse the received JSON to one Order instance $order. I can get this done so by doing this in my OrderController:

class OrderController extends Controller
{
  public function store(Request $request) {
    $order = new Order();
    $order->forceFill($request->toArray());
  }
}

It's possible to access properties now like $order->name and $order->customer in the store function of the controller. When i access the $order->orderItems i receive an array with "orderItemsbut as array, not as instance ofOrderItem`.

I want that $order->orderItems returns an array of OrderItem instances. I tried the following in Order but this does not work as 'orderItems' is not a OrderItem::class but is an array with multiple "OrderItems".

  protected $casts = [
    'orderItems' => OrderItem::class,
  ];

How can i achieve that $order->orderItems returns an array of OrderItem instances? Thanks for any help in advance!

CodePudding user response:

Try to add the following to your controller

  • validation
  • manual storing your Order
  • manual storing each of your order items

.

class OrderController extends Controller
{
  public function store(Request $request) 
  {
    $your_rules = [
       'name' => 'required|string',
       'customer' => 'required|string', // related to customer id ?
    ];

    $validated = $request->validate($your_rules);
    $order = Order::create([
       'name' => $validated['name'],
       'customer' => $validated['customer'], // is this customer id or name ?
    ]);

    // I assume you already declare relationship to OrderItem inside your Order model
    foreach ($validated['orderItems'] as $orderItem) {
        // this array only is optional
        $orderItem = Arr::only($orderItem, ['name', 'amount', 'price');

        $order->orderItems()->save($orderItem);
    }

    // reload saved order items
    $order->load('orderItems');

    dd($order);
  }
}

You can also create multiple children in single command.

$order->orderItems()->saveMany([
    new OrderItem(['name' => '...', ... ]),
    new OrderItem(['name' => '...', ... ]),
]);

Read here for more info https://laravel.com/docs/9.x/eloquent-relationships#the-save-method

You can move this into your model as extra custom method. For example:

public function saveOrderItems(array $orderItems): void
{
   $this->orderItems()->saveMany($orderItems);
}

And you call it as $order->saveOrderItems($orderItems);

P.S.

Dont forget to declare relationship in Order model.

public function orderItems()
{
    return $this->hasMany(OrderItem::class);
}

CodePudding user response:

I think you are confuse with the whole Model relationship. Checkout the documentation here, you need to define proper relationship and foreign key between your Order and OrderItem model.

Then your model should be like this;

//Order.php
class Order extends Model {

    protected $fillable = [
        'name',
        'customer',
    ];

    public function items() {
        return $this->hasMany(OrderItem::class);
    }
    
}

//OrderItem.php
class OrderItem extends Model {

    protected $fillable = [
        'amount',
        'name',
        'price'
    ];

    public function order() {
        return $this->belongsTo(Order::class);
    }
    
}

Then your store method

public function store( Request $request ) {

    $request->validate([
        'name' => 'required',
        'customer' => 'required|exists:customers_table,id',
        'orderItems' => 'required|array'
    ]);

    $order = Order::create( $request->except('orderItems') );

    $items = $order->items()->createMany( $request->input('orderItems') );
}
  • Related