Laravel & Vue.js 3 Product Update Tutorial (API Based)

Laravel & Vue.js 3 Product Update Tutorial (API Based)

 After creating and fetching products, the next step in Laravel with Vue js CRUD is updating product values.
 This tutorial explains how to update product data using a Laravel REST API and Vue.js 3 frontend, following the same API-based pattern used in professional projects. 

Backend: Laravel Product Update API

 Product Controller

use Illuminate\Http\Request;
use App\Models\Product;
use Illuminate\Support\Facades\Storage;

public function update(Request $request, $id)
{
    $product = Product::findOrFail($id);

    $validated = $request->validate([
        'name'        => 'required|string|max:255',
        'sku'         => 'required|unique:products,sku,' . $product->id,
        'price'       => 'required|numeric',
        'stock'       => 'required|integer',
        'description' => 'nullable|string',
        'image'       => 'nullable|image|mimes:jpg,png,jpeg|max:2048'
    ]);

    if ($request->hasFile('image')) {
        if ($product->image) {
            Storage::disk('public')->delete($product->image);
        }

        $validated['image'] = $request->file('image')
            ->store('products', 'public');
    }

    $product->update($validated);

    return response()->json([
        'message' => 'Product updated successfully',
        'data' => $product
    ], 200);
}

API Route

Route::put('/products/{id}', [ProductController::class, 'update']);

Frontend: Vue.js 3 Product Update Form

Vue.js Edit Component

<template>
  <div class="p-6 max-w-xl mx-auto">
    <h2 class="text-xl font-bold mb-4">Edit Product</h2>

    <form @submit.prevent="updateProduct">
      <input v-model="form.name" class="input" placeholder="Name" />
      <input v-model="form.sku" class="input" placeholder="SKU" />
      <input type="number" v-model="form.price" class="input" placeholder="Price" />
      <input type="number" v-model="form.stock" class="input" placeholder="Stock" />
      <textarea v-model="form.description" class="input"></textarea>

      <input type="file" @change="handleFile" />

      <button class="btn">Update Product</button>
    </form>
  </div>
</template>

<script setup>
import { reactive, onMounted } from 'vue'
import api from '@/services/api'
import { useRoute } from 'vue-router'

const route = useRoute()

const form = reactive({
  name: '',
  sku: '',
  price: '',
  stock: '',
  description: '',
  image: null
})

const handleFile = (e) => {
  form.image = e.target.files[0]
}

const fetchProduct = async () => {
  const res = await api.get(`/products/${route.params.id}`)
  Object.assign(form, res.data.data)
}

const updateProduct = async () => {
  const data = new FormData()
  Object.keys(form).forEach(key => {
    data.append(key, form[key])
  })

  await api.post(`/products/${route.params.id}?_method=PUT`, data)
  alert('Product Updated Successfully')
}

onMounted(fetchProduct)
</script>