Laravel: How to use foreach loop to decrease stock based on order quantity

Viewed 455

I'm having trouble getting the quantity of the ordered products to update the stock quantity based on pivot table order_items.

Order Model

public function items()
{
    return $this->belongsToMany(Product::class,'order_items', 'order_id', 'product_id')->withPivot('quantity', 'price')->withTimestamps();
}

OrderItem Model

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

Product Model

public function orders()
{
    return $this->belongsToMany(Order::class,'order_items', 'order_id', 'product_id')->withPivot('quantity', 'price')->withTimestamps();
}

OrderController (Updated)

public function store(Request $request)
{
    $order = new Order();
    $order->order_number = uniqid('Order #');
    ...
    $order->save();
    
    $cartItems = \Cart::session(auth()->id())->getContent();
    foreach ($cartItems as $item)
    {
        $order->items()->attach($item->id, [
            'price'=> $item->price,
            'quantity'=> $item->quantity
        ]);
    }

    // Update stock quantity
    foreach ($order->items as $prod) {
    {
        dd($order->items);
        $newStock = $prod->stock - $item->quantity;
        $prod->update(['stock' => $newStock]);
    }
    
    ...

}
Illuminate\Database\Eloquent\Collection {#598 ▼
  #items: array:2 [▼
    0 => App\Models\Product {#1159 ▶}
    1 => App\Models\Product {#754 ▼
      #table: "products"
      #translatable: array:17 [▶]
      #fillable: array:6 [▶]
      #connection: "mysql"
      #primaryKey: "id"
      #keyType: "int"
      +incrementing: true
      #with: []
      #withCount: []
      #perPage: 15
      +exists: true
      +wasRecentlyCreated: false
      #attributes: array:16 [▼
        "id" => 24
        "shop_id" => 1
        "cat_id" => 19
        "child_cat_id" => 5
        "name" => "Facilis sit saepe."
        "slug" => "voluptatum-rem-doloribus-eligendi-porro-molestiae-sapiente-ut-aut"
        "summary" => "Quidem repudiandae voluptate explicabo sed voluptates impedit explicabo vel quis."
        "description" => "Sint corporis sed velit velit. Qui qui quibusdam sint. Commodi facere similique voluptas commodi rerum omnis incidunt qui. Est exercitationem odit id dignissimo ▶"
        "image" => "https://via.placeholder.com/400x200.png/007799?text=corrupti"
        "images" => "https://via.placeholder.com/400x200.png/00ccaa?text=nihil"
        "price" => 67.24
        "stock" => 4  // Product stock
        "condition" => "popular"
        "publish" => 1
        "created_at" => "2021-04-26 20:21:42"
        "updated_at" => "2021-04-26 20:21:42"
      ]
      #original: array:22 [▶]
      #changes: []
      #casts: []
      #classCastCache: []
      #dates: []
      #dateFormat: null
      #appends: []
      #dispatchesEvents: []
      #observables: []
      #relations: array:1 [▶]
      #touches: []
      +timestamps: true
      #hidden: []
      #visible: []
      #guarded: array:1 [▶]
    }
  ]
}
2 Answers

You don't need to query your database inside the foreach to obtain the current Product as you already have access to it on your items.

You can use a classic foreach or you can use the each method on the items collection.

public function store(Request $request)
{
    // ...

    // iterate (loop) over each item in the order items collection
    $order->items->each(function ($item) {
        // deduct the quantity of the item for this order
        // from the stock of the product and update the new stock
        $item->update(['stock' => ($item->stock - $item->pivot->quantity)]);
    });

    // ...
}

On a side note, try to be consistent in your naming and use framework conventions where possible. You have a model named Product but you refer to instances of Product as $item. I know this seems like something very minor, however, consider a new developer or someone who has never seen your code or you returning to the code in 6 months and seeing $item then expecting to find an Item model.

A couple of things that might be causing an issue inside your controller.

Try the below code to see if that works for you.

  • Removed the parentheses from $order->items()
  • Changed the $prod query from get to first
    • Get returns a collection of results, first will return a single one.

Updated my answer

public function store(Request $request)
{
    ....

    // Update stock quantity
    foreach ($order->items as $prod)
    {
        $newStock = $item->stock - $item->pivot->quantity;
        $prod->update(['stock' => $newStock]);
    }
    
    ...

}
Related