Create a Laravel Vue.js CRUD Application: A Simple Example

Building modern web applications often requires creating Create, Read, Update, and Delete (CRUD) functionalities. This article provides a simple and practical example of building a Laravel Vue.js CRUD example application, combining the power of Laravel's backend with Vue.js's reactive frontend. Whether you are a beginner or an experienced developer, this step-by-step guide will help you understand the core concepts and implementation details.

Why Choose Laravel and Vue.js for Your CRUD Application?

Laravel, a PHP framework known for its elegant syntax and robust features, is an excellent choice for handling backend operations. Its features like Eloquent ORM, routing, and security make it a great foundation for any web application. Vue.js, a progressive JavaScript framework, simplifies frontend development with its component-based architecture and reactive data binding. Combining these two technologies can result in a scalable, maintainable, and efficient application.

Setting Up Your Laravel Project for the Vue.js CRUD Example

First, you need to set up a new Laravel project. Open your terminal and run the following command:

composer create-project --prefer-dist laravel/laravel laravel-vue-crud
cd laravel-vue-crud

Next, configure your database connection in the .env file. Update the DB_DATABASE, DB_USERNAME, and DB_PASSWORD variables to match your database credentials.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_database_name
DB_USERNAME=your_database_username
DB_PASSWORD=your_database_password

After configuring the database, run the migrations to create the necessary tables. For this Laravel Vue.js CRUD example, we'll create a simple products table.

php artisan migrate

Creating the Product Model and Migration

To interact with the products table, create a model and migration using Artisan commands:

php artisan make:model Product -m

Open the generated migration file (database/migrations/*_create_products_table.php) and define the table schema:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateProductsTable extends Migration
{
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('description')->nullable();
            $table->decimal('price', 8, 2);
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('products');
    }
}

Run the migration again to apply the changes to the database:

php artisan migrate

Now, open the Product model (app/Models/Product.php) and define the fillable attributes:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
        'description',
        'price',
    ];
}

Building the Laravel API for CRUD Operations

Next, create a controller to handle the CRUD operations. Use the following Artisan command:

php artisan make:controller ProductController --resource

Open the ProductController (app/Http/Controllers/ProductController.php) and implement the CRUD methods:

<?php

namespace App\Http\Controllers;

use App\Models\Product;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    public function index()
    {
        $products = Product::all();
        return response()->json($products);
    }

    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required',
            'price' => 'required|numeric',
        ]);

        $product = Product::create($request->all());
        return response()->json($product, 201);
    }

    public function show(Product $product)
    {
        return response()->json($product);
    }

    public function update(Request $request, Product $product)
    {
        $request->validate([
            'name' => 'required',
            'price' => 'required|numeric',
        ]);

        $product->update($request->all());
        return response()->json($product, 200);
    }

    public function destroy(Product $product)
    {
        $product->delete();
        return response()->json(null, 204);
    }
}

Define the API routes in routes/api.php:

<?php

use App\Http\Controllers\ProductController;
use Illuminate\Support\Facades\Route;

Route::resource('products', ProductController::class);

Setting Up Vue.js Frontend

Install Vue.js using npm or yarn. If you don't have Node.js and npm installed, download them from the official Node.js website.

npm install vue@next

Or, using yarn:

yarn add vue@next

Create a resources/js/app.js file and import Vue.js:

import { createApp } from 'vue';

const app = createApp({});

// Register components here

app.mount('#app');

Create a main component, for example, resources/js/components/ProductList.vue:

<template>
    <div>
        <h2>Products</h2>
        <ul>
            <li v-for="product in products" :key="product.id">
                {{ product.name }} - ${{ product.price }}
            </li>
        </ul>
    </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';

export default {
    setup() {
        const products = ref([]);

        onMounted(async () => {
            const response = await axios.get('/api/products');
            products.value = response.data;
        });

        return {
            products
        };
    }
}
</script>

Register the component in resources/js/app.js:

import { createApp } from 'vue';
import ProductList from './components/ProductList.vue';

const app = createApp({});

app.component('product-list', ProductList);

app.mount('#app');

Integrating Vue.js with Laravel Views

Create a blade template where the Vue.js application will be mounted. Create resources/views/welcome.blade.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Laravel Vue.js CRUD Example</title>
    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
</head>
<body>
    <div id="app">
        <product-list></product-list>
    </div>
    <script src="{{ asset('js/app.js') }}"></script>
</body>
</html>

Update your webpack.mix.js file to compile your assets:

const mix = require('laravel-mix');

mix.js('resources/js/app.js', 'public/js')
   .vue()
   .sass('resources/sass/app.scss', 'public/css');

Run the compilation command:

npm run dev

Or, using yarn:

yarn dev

Displaying Data: Reading Products

In the ProductList.vue component, you've already seen how to fetch and display data. Use axios to make a GET request to your API endpoint and display the products in a list. Enhance the component to include more details like description and price.

<template>
    <div>
        <h2>Products</h2>
        <ul>
            <li v-for="product in products" :key="product.id">
                {{ product.name }} - ${{ product.description }} - ${{ product.price }}
            </li>
        </ul>
    </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';

export default {
    setup() {
        const products = ref([]);

        onMounted(async () => {
            const response = await axios.get('/api/products');
            products.value = response.data;
        });

        return {
            products
        };
    }
}
</script>

Creating New Products: Implementing the Create Operation

Add a form to your ProductList.vue component to create new products. Use Vue.js's data binding to bind the form inputs to data properties and make a POST request to your API endpoint.

<template>
    <div>
        <h2>Products</h2>
        <ul>
            <li v-for="product in products" :key="product.id">
                {{ product.name }} - ${{ product.description }} - ${{ product.price }}
            </li>
        </ul>

        <h2>Create New Product</h2>
        <form @submit.prevent="createProduct">
            <input type="text" v-model="newProduct.name" placeholder="Name" required>
            <textarea v-model="newProduct.description" placeholder="Description"></textarea>
            <input type="number" v-model="newProduct.price" placeholder="Price" required>
            <button type="submit">Create</button>
        </form>
    </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';

export default {
    setup() {
        const products = ref([]);
        const newProduct = ref({
            name: '',
            description: '',
            price: ''
        });

        onMounted(async () => {
            await fetchProducts();
        });

        const fetchProducts = async () => {
            const response = await axios.get('/api/products');
            products.value = response.data;
        };

        const createProduct = async () => {
            await axios.post('/api/products', newProduct.value);
            newProduct.value = {
                name: '',
                description: '',
                price: ''
            };
            await fetchProducts(); // Refresh the product list
        };

        return {
            products,
            newProduct,
            createProduct
        };
    }
}
</script>

Updating Existing Products: Implementing the Update Operation

Add functionality to edit existing products. This involves adding an edit button for each product, displaying a form populated with the product's data, and making a PUT request to your API endpoint.

<template>
    <div>
        <h2>Products</h2>
        <ul>
            <li v-for="product in products" :key="product.id">
                {{ product.name }} - {{ product.description }} - ${{ product.price }}
                <button @click="editProduct(product)">Edit</button>
            </li>
        </ul>

        <h2>Create New Product</h2>
        <form @submit.prevent="createProduct">
            <input type="text" v-model="newProduct.name" placeholder="Name" required>
            <textarea v-model="newProduct.description" placeholder="Description"></textarea>
            <input type="number" v-model="newProduct.price" placeholder="Price" required>
            <button type="submit">Create</button>
        </form>

        <div v-if="editingProduct">
            <h2>Edit Product</h2>
            <form @submit.prevent="updateProduct">
                <input type="text" v-model="editingProduct.name" placeholder="Name" required>
                <textarea v-model="editingProduct.description" placeholder="Description"></textarea>
                <input type="number" v-model="editingProduct.price" placeholder="Price" required>
                <button type="submit">Update</button>
            </form>
        </div>
    </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';

export default {
    setup() {
        const products = ref([]);
        const newProduct = ref({
            name: '',
            description: '',
            price: ''
        });
        const editingProduct = ref(null);

        onMounted(async () => {
            await fetchProducts();
        });

        const fetchProducts = async () => {
            const response = await axios.get('/api/products');
            products.value = response.data;
        };

        const createProduct = async () => {
            await axios.post('/api/products', newProduct.value);
            newProduct.value = {
                name: '',
                description: '',
                price: ''
            };
            await fetchProducts(); // Refresh the product list
        };

        const editProduct = (product) => {
            editingProduct.value = { ...product }; // Create a copy to avoid direct mutation
        };

        const updateProduct = async () => {
            if (editingProduct.value) {
                await axios.put(`/api/products/${editingProduct.value.id}`, editingProduct.value);
                editingProduct.value = null;
                await fetchProducts(); // Refresh the product list
            }
        };

        return {
            products,
            newProduct,
            createProduct,
            editingProduct,
            editProduct,
            updateProduct
        };
    }
}
</script>

Deleting Products: Implementing the Delete Operation

Implement the delete functionality by adding a delete button for each product and making a DELETE request to your API endpoint.

<template>
    <div>
        <h2>Products</h2>
        <ul>
            <li v-for="product in products" :key="product.id">
                {{ product.name }} - {{ product.description }} - ${{ product.price }}
                <button @click="editProduct(product)">Edit</button>
                <button @click="deleteProduct(product)">Delete</button>
            </li>
        </ul>

        <h2>Create New Product</h2>
        <form @submit.prevent="createProduct">
            <input type="text" v-model="newProduct.name" placeholder="Name" required>
            <textarea v-model="newProduct.description" placeholder="Description"></textarea>
            <input type="number" v-model="newProduct.price" placeholder="Price" required>
            <button type="submit">Create</button>
        </form>

        <div v-if="editingProduct">
            <h2>Edit Product</h2>
            <form @submit.prevent="updateProduct">
                <input type="text" v-model="editingProduct.name" placeholder="Name" required>
                <textarea v-model="editingProduct.description" placeholder="Description"></textarea>
                <input type="number" v-model="editingProduct.price" placeholder="Price" required>
                <button type="submit">Update</button>
            </form>
        </div>
    </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';

export default {
    setup() {
        const products = ref([]);
        const newProduct = ref({
            name: '',
            description: '',
            price: ''
        });
        const editingProduct = ref(null);

        onMounted(async () => {
            await fetchProducts();
        });

        const fetchProducts = async () => {
            const response = await axios.get('/api/products');
            products.value = response.data;
        };

        const createProduct = async () => {
            await axios.post('/api/products', newProduct.value);
            newProduct.value = {
                name: '',
                description: '',
                price: ''
            };
            await fetchProducts(); // Refresh the product list
        };

        const editProduct = (product) => {
            editingProduct.value = { ...product }; // Create a copy to avoid direct mutation
        };

        const updateProduct = async () => {
            if (editingProduct.value) {
                await axios.put(`/api/products/${editingProduct.value.id}`, editingProduct.value);
                editingProduct.value = null;
                await fetchProducts(); // Refresh the product list
            }
        };

        const deleteProduct = async (product) => {
            if (confirm(`Are you sure you want to delete ${product.name}?`)) {
                await axios.delete(`/api/products/${product.id}`);
                await fetchProducts(); // Refresh the product list
            }
        };

        return {
            products,
            newProduct,
            createProduct,
            editingProduct,
            editProduct,
            updateProduct,
            deleteProduct
        };
    }
}
</script>

Enhancing User Experience and Application Security

To provide a better user experience, consider adding features like pagination, search, and sorting. Additionally, implement proper validation and authentication to secure your application.

Best Practices for a Laravel Vue.js CRUD Example

Follow best practices such as using environment variables for configuration, implementing proper error handling, and writing clean, maintainable code. Regular code reviews and testing are also crucial.

Conclusion: Your Laravel Vue.js CRUD Application

By following this guide, you've created a Laravel Vue.js CRUD example application that demonstrates the fundamental CRUD operations. You can extend this example by adding more features and complexity to meet your specific requirements. Combining Laravel and Vue.js provides a powerful and efficient way to build modern web applications. Remember to always prioritize security, maintainability, and user experience in your development process.

Leave a Reply

Your email address will not be published. Required fields are marked *

MakeupGuide

Our media platform offers reliable news and insightful articles. Stay informed with our comprehensive coverage and in-depth analysis on various topics.

Recent Posts

Categories

Resource

© 2025 MakeupGuide