Complete CRUD Application with Laravel and Vue js

In our previous Laravel Vue Js tutorial, we have explained how to build a to-do list app with Vue.js and Laravel. In this tutorial, we will explain how to make a CRUD(create, read, update, and delete) App with Laravel and Vue Js.

Vue JS is a popular and very user friendly JavaScript framework that helps to manage the application without page refresh page. We can easily use Vue with Laravel as it comes pre-packaged with Laravel. It used Laravel mix webpack tool to create Laravel application with Vue.

In this tutorial, we will cover following in our CRUD Application:

  1. Create Laravel Project
  2. Update MySQL Database Details
  3. Install Dependancies
  4. Create Migration, Model, and Controller
  5. Define Routes
  6. Create Vue App
  7. Create Vue App Component
  8. Define Route in Vue Router
  9. Add Vue.js dependencies to app.js
  10. Update Laravel Mix
  11. Compile Webpack
  12. Run Application

Now let’s get started!


1. Create Laravel Project

First we will create Laravel project and run below command to create Laravel proect laravel-vue-crud-app.

composer create-project laravel/laravel laravel-vue-crud-app

2. Update Database Details

After creating project, open .env file and update MySQL database details.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel-vue-crud
DB_USERNAME=root
DB_PASSWORD=

3. Install Dependancies

We will run the following command to install frontend dependencies:

npm install

We will also install vue, vue-router and vue-axiosmodules by running following command:

npm install vue vue-router vue-axios --save

4. Create Migration, Model, and Controller

We will run below command to create a Employee model, migration, and controller:


php artisan make:model Employee -mcr

We will open migration file of employee from database/migrations and replace code in up() function:

public function up() {
	Schema::create('employees', function (Blueprint $table) {
		$table->bigIncrements('id');
		$table->string('name');
		$table->text('address');
		$table->timestamps();
	});
}

we will run below command to migrate the database:

php artisan migrate

we will open Employee.php model from app/Models and update with following code:

<?php

namespace App\Models;

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

class Employee extends Model {
    use HasFactory;

    protected $fillable = ['name','address'];
}

we will open EmployeeController.php from app/Http/Controllers and update with following code to list, save, update, and delete methods:

<?php

namespace App\Http\Controllers;

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

class EmployeeController extends Controller {
    
    public function index() {
        $categories = Employee::all(['id','name','address']);
        return response()->json($categories);
    }

    
    public function create()  {
        //
    }

    
    public function store(Request $request)  {
        $employee = Employee::create($request->post());
        return response()->json([
            'message'=>'Employee Created Successfully!!',
            'employee'=>$employee
        ]);
    }

    
    public function show(Employee $employee) {
        return response()->json($employee);
    }

    
    public function edit(Employee $employee) {
        //
    }

    
    public function update(Request $request, Employee $employee) {
        $employee->fill($request->post())->save();
        return response()->json([
            'message'=>'Employee Updated Successfully!!',
            'employee'=>$employee
        ]);
    }

   public function destroy(Employee $employee) {
        $employee->delete();
        return response()->json([
            'message'=>'Employee Deleted Successfully!!'
        ]);
    }
}

5. Define Routes In web.php

We will define routes for our application. We will open routes/web.php and routes/api.php from routes directory.


We will open routes/web.php and update with following code:

<?php

use Illuminate\Support\Facades\Route;

Route::get('{any}', function () {
    return view('app');
})->where('any', '.*');

We will open routes/api.php and update with following code:

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

Route::resource('employee',App\Http\Controllers\EmployeeController::class)->only(['index','store','show','update','destroy']);

6. Create Views File for Vue App

Now we wil go to the resources/views direcgory and create app.blade.php file for our Vue app data load.

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" value="{{ csrf_token() }}"/>
        <title>Laravel Vue Crud Application</title>
        <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet" type="text/css">
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
        <link href="{{ mix('css/app.css') }}" type="text/css" rel="stylesheet"/>
    </head>
    <body>
        <div id="app">
        </div>
        <script src="{{ mix('js/app.js') }}" type="text/javascript"></script>
    </body>
</html>

7. Create Vue App Components

Now we will create componenets for Vue App. We will go to the resource/js and create components directory.

We will create main file of our Vue app App.vue in resource/js/components directory. We will define router-view in this file. We will update App.vue with following code:


<template>
    <main>
        <nav class="navbar navbar-expand-lg navbar-dark bg-success">
            <div class="container-fluid">
                <router-link to="/" class="navbar-brand" href="#">Laravel Vue Crud Application</router-link>
                <div class="collapse navbar-collapse">
                    <div class="navbar-nav">
                        <router-link exact-active-class="active" to="/" class="nav-item nav-link">Home</router-link>
                        <router-link exact-active-class="active" to="/employee" class="nav-item nav-link">Employee</router-link>
                    </div>
                </div>
            </div>
        </nav>
        <div class="container mt-5">
            <router-view></router-view>
        </div>
    </main>
</template>
 
<script>
    export default {}
</script>

We will create a directory employee in resource/js/components and create employee componenets vue files.

We will create Add.vue file in resource/js/components/employee and update with following code:

<template>
    <div class="row">
        <div class="col-12">
            <div class="card">
                <div class="card-header">
                    <h4>Add Employee</h4>
                </div>
                <div class="card-body">
                    <form @submit.prevent="create">
                        <div class="row">
                            <div class="col-12 mb-2">
                                <div class="form-group">
                                    <label>name</label>
                                    <input type="text" class="form-control" v-model="employee.name">
                                </div>
                            </div>
                            <div class="col-12 mb-2">
                                <div class="form-group">
                                    <label>Address</label>
                                    <input type="text" class="form-control" v-model="employee.address">
                                </div>
                            </div>
                            <div class="col-12">
                                <button type="submit" class="btn btn-success">Save</button>
                            </div>
                        </div>                        
                    </form>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name:"add-employee",
    data(){
        return {
            employee:{
                name:"",
                address:""
            }
        }
    },
    methods:{
        async create(){
            await this.axios.post('/api/employee',this.employee).then(response=>{
                this.$router.push({name:"employeeList"})
            }).catch(error=>{
                console.log(error)
            })
        }
    }
}
</script>

We will create Edit.vue file in resource/js/components/employee and update with following code:

<template>
    <div class="row">
        <div class="col-12">
            <div class="card">
                <div class="card-header">
                    <h4>Update Employee</h4>
                </div>
                <div class="card-body">
                    <form @submit.prevent="update">
                        <div class="row">
                            <div class="col-12 mb-2">
                                <div class="form-group">
                                    <label>Name</label>
                                    <input type="text" class="form-control" v-model="em.name">
                                </div>
                            </div>
                            <div class="col-12 mb-2">
                                <div class="form-group">
                                    <label>Address</label>
                                    <input type="text" class="form-control" v-model="employee.address">
                                </div>
                            </div>
                            <div class="col-12">
                                <button type="submit" class="btn btn-success">Update</button>
                            </div>
                        </div>                        
                    </form>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name:"update-employee",
    data(){
        return {
            employee:{
                name:"",
                address:"",
                _method:"patch"
            }
        }
    },
    mounted(){
        this.showEmployee()
    },
    methods:{
        async showEmployee(){
            await this.axios.get(`/api/employee/${this.$route.params.id}`).then(response=>{
                const { name, address } = response.data
                this.employee.name = name
                this.employee.address = address
            }).catch(error=>{
                console.log(error)
            })
        },
        async update(){
            await this.axios.post(`/api/employee/${this.$route.params.id}`,this.employee).then(response=>{
                this.$router.push({name:"employeeList"})
            }).catch(error=>{
                console.log(error)
            })
        }
    }
}
</script>

We will create List.vue file in resource/js/components/employee and update with following code:

<template>
    <div class="row">
        <div class="col-12 mb-2 text-end">
            <router-link :to='{name:"employeeAdd"}' class="btn btn-success">Create</router-link>
        </div>
        <div class="col-12">
            <div class="card">
                <div class="card-header">
                    <h4>Employee</h4>
                </div>
                <div class="card-body">
                    <div class="table-responsive">
                        <table class="table table-bordered">
                            <thead>
                                <tr>
                                    <th>ID</th>
                                    <th>Name</th>
                                    <th>Address</th>
                                    <th>Actions</th>
                                </tr>
                            </thead>
                            <tbody v-if="employees.length > 0">
                                <tr v-for="(employee,key) in employees" :key="key">
                                    <td>{{ employee.id }}</td>
                                    <td>{{ employee.name }}</td>
                                    <td>{{ employee.address }}</td>
                                    <td>
                                        <router-link :to='{name:"employeeEdit",params:{id:employee.id}}' class="btn btn-success">Edit</router-link>
                                        <button type="button" @click="deleteEmployee(employee.id)" class="btn btn-danger">Delete</button>
                                    </td>
                                </tr>
                            </tbody>
                            <tbody v-else>
                                <tr>
                                    <td colspan="4" align="center">No employees Found.</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name:"employees",
    data(){
        return {
            employees:[]
        }
    },
    mounted(){
        this.getEmployees()
    },
    methods:{
        async getEmployees(){
            await this.axios.get('/api/employees').then(response=>{
                this.employees = response.data
            }).catch(error=>{
                console.log(error)
                this.employees = []
            })
        },
        deleteEmployee(id){
            if(confirm("Are you sure to delete this employee ?")){
                this.axios.delete(`/api/employee/${id}`).then(response=>{
                    this.getEmployees()
                }).catch(error=>{
                    console.log(error)
                })
            }
        }
    }
}
</script>

8. Add Routes in Vue Router

We will add routes for components in Vue Router for our app. The componenets will be loaded with lazy loading using Vue JS. We will open resources/js/routes.js and update with following code:


const EmployeeList = () => import('./components/employee/List.vue')
const EmployeeCreate = () => import('./components/employee/Add.vue')
const EmployeeEdit = () => import('./components/employee/Edit.vue')

export const routes = [
    {
        name: 'home',
        path: '/',
        component: EmployeeList
    },
    {
        name: 'employeeList',
        path: '/employee',
        component: EmployeeList
    },
    {
        name: 'employeeEdit',
        path: '/employee/:id/edit',
        component: EmployeeEdit
    },
    {
        name: 'employeeAdd',
        path: '/employee/add',
        component: EmployeeCreate
    }
]

9. Import Vue.js Dependencies

We will import all required dependencies in our app. We will open resource/js/app.js file and import vue, routes, vue-axios and Bootstrap.

require('./bootstrap');
import vue from 'vue'
window.Vue = vue;

import App from './components/App.vue';
import VueRouter from 'vue-router';
import VueAxios from 'vue-axios';
import axios from 'axios';
import {routes} from './routes';
 
Vue.use(VueRouter);
Vue.use(VueAxios, axios);
 
const router = new VueRouter({
    mode: 'history',
    routes: routes
});
 
const app = new Vue({
    el: '#app',
    router: router,
    render: h => h(App),
});

10. Update Laravel Mix

We will also need to update webpack.mix.js with resources/js/app.js to compile js and css into public directory.

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

mix.js('resources/js/app.js', 'public/js')
    .postCss('resources/css/app.css', 'public/css', [
        
    ]).vue();
	

11. Compile Webpack

After updating webpack.mix.js file, we will run below command to compile all JavaScript and CSS into application’s public directory:

npm run dev

We will also run below command to watch all relevant JavaScript and CSS files changes. It will automatically recompile when it detects a change to one of these files:

npm run watch

12. Run Application

Now, finally we will run our application by using below command:

php artisan serve

The application will be available on http://127.0.0.1:8000/

Leave a Reply

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