So today we are going to create a complete employee management application. For this we will use Laravel 8 along with Vuejs and Vuex.
you can follow along and create a project in laravel, vuejs and vuex. We are going to cover some advanced features of vuex. Also we are going to add search feature in this project.
Follow steps as given below to create this application. If face any issue then check our video guide.
Code for Video Laravel Vuejs Vuex Employee management Application.
Code for Video Laravel Vuejs Vuex Employee management Application.
---> Step 1
-----------
--------------
Getting started
1. create laravel project
https://laravel.com/docs/8.x#installation-via-composer 2. composer require laravel/ui 3. php artisan ui vue --auth 4.npm install
Additional NPM packages installation
5. npm install vue-template-compiler vue-loader vue-router vuex axios vue-axios --save-dev --legacy-peer-deps
Compile using
npm run dev or npm run watch
------------------------------------------------------------------------
---> Step 2
-----------
Setting up vuejs and Vuex for project and Creating model and controller
--------------
1. Connect database in env file.
2. php artisan make:migration create_employee_table
Table fields:
$table->id(); $table->string('name'); $table->string('department'); $table->string('section'); $table->string('email'); $table->integer('status')->default(0); $table->timestamps();
Run commands:
3.php artisan migrate 4. php artisan make:model Employee
5. in model
protected $fillable = [ 'name', 'department', 'section', 'email' ]
6. php artisan make:controller EmployeesController
7. public function create
$data= [ 'scope' =>'create' ]; view('employee.form')
8. create form.blade.php in employee folder.
9. create route
Route::get('add-employee', [EmployeesController::class, 'create']);
10. Create blade file form.blade.php
extends app.blade.php layout
create div#app inside blade file.
create component employee-form
pass props->
<div class="container"> <employee-form scope="{$scope}" :id="{$id ?? 0}" > </employee-form> </div>
create folder components/employees
create file EmployeeForm.vue
Package to use: element.eleme.io: installation
https://element.eleme.io/#/en-US/component/installation
npm i element-ui -S
----------------------------
In app.js
----------------------------
import Vue from 'vue'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI);
----------------------------
In EmployeeForm.vue
----------------------------
<template> <div> <el-card class="box-card"> <div slot="header" class="clearfix"> Form page </div> </el-card> </div> </template>
go in element.eleme.io sidebar components search for
page-header
<template> <div> <el-card class="box-card"> <div slot="header" class="clearfix"> <el-page-header @back="goBack" content="detail"> </el-page-header> </div> <div> <el-form ref="form" :model="form" ref="employeeForm"> <el-row :guuter="10"> <el-form-item label="Name" required prop="name"> <el-input v-model="form.name" aria-placeholder="Employee Name"> </el-form-item> <el-form-item label="Department" required prop="department"> <el-input v-model="form.department" aria-placeholder="Employee Department"> </el-form-item> <el-form-item label="Section" required prop="section"> <el-input v-model="form.section" aria-placeholder="Employee Section"> </el-form-item> <el-form-item label="Email" required prop="email"> <el-input v-model="form.email" aria-placeholder="Employee Email"> </el-form-item> <el-row> <el-row :guuter="10"> <el-form-item> <el-button type="success" @click="saveForm('employeeForm')">Save<el-button> </el-form-item> </el-row> </el-form> </div> </el-card> </div> </template> <script> export default{ name:'employee-form', data(){ return{ form:{ name:null, department:null, section:null, email:null, }, } }, methods:{ goBack(){ window.location.href = '/employees' }, saveForm(formName){ } } } </script>
-----------------------------------------------------------------------------
Vuex core concepts
Go in application structure
create store folder in js folder
create files
actions.js mutations.js getters.js index.js
----------------------------
index.js
----------------------------
import Vue from 'vue'; import Vuex from 'vuex'; import getters from './getters'; import mutations from './mutations'; import * as actions from './actions'; import { axios } from 'vue/types/umd'; Vue.use(Vuex); export const store = new Vuex.store({ modules:{ }, state:{ }, mutations, actions, getters, })
-----------------------------
App.js
-----------------------------
import { store } from './store/index.js' const app = new Vue({ store, el:'#app' })
-----------------------------
Employee form component
----------------------------
props:{ scope:String, } , saveForm(formName){ this.$refs[formName].validate((valid) => { if(valid){ this.$store.dispatch('saveEmployee', this.form); } }); }
----------------------------
Actions.js
----------------------------
import Vue from 'vue'; let loader = null; function displayLoader(loadingText='loading...'){ loader = Vue.prototype.$loading({ lock:true, text: loadingText, spinner: 'el-icon-loading', background: 'rgba(255,255,255,0.85', }) } function hideLoader(){ loader.close(); } export const saveEmployee = ({commit}, payload) => { displayLoader(); axios.post(`/save-employee`, pauyload).then(res => { Vue.prototype.$notify({ title:'Success', message: 'Employee added successfully', type: 'success', }); hideLoader(); setTimeout(()=>{ window.location.href = '/employees'; },1500); }); }
--------------------
web.php
--------------------
Route::post('save-employee', [EmployeesController::class, 'store'])
------------------
EmployeesController
-----------------
use Illuminate\Http\Request; use Exception; use App\Models\Employee; public function storeEmployee(Request $request) { try { $employee = new Employee; $employee->name = $request->name; $employee->department = $request->department; $employee->section = $request->section; $employee->email = $request->email; $employee->save(); return response()->json([ 'status' => 200, 'message' => 'Employee saved successfully' ]) } catch(Exception $e) { dd($e); } }
---------------------------------------------------------------------------
---> Step 3
-----------
--------------
--------------------
web.php
-------------------
Route::get('employees', [EmployeesController::class, 'index']);
-------------------
EmployeesController::class
-------------------
public function index(){ return view('employee.index'); }
copy content from
form.blade.php ------> index.blade.php
create component employee-list
EmployeeList.vue
<template> <div> <el-card class="box-card"> <div slot="header" class="clearfix"> Employee Data <el-button type="success" class="float-right" @click=""> Add Employee </el-button> </div> <div> <el-table :data="tableData" style="width:100%" :border="true" hieght="500" > <el-table-column v-for="column in tableColumns" :key="column.label" :label="column.label" :prop="column.prop" :column-key = "column.prop" :width="column.minWidth" :sortable="column.sortable" :align="column.align" :header-align="column.align" :format="column.format || null" > </el-table-column> <el-table-column align="right" <template slot="header" slot-scope="scope"> <el-input v-model="search" size="mini" placeholder="Type to search" /> </template> <template slot-scope="scope"> <el-button size="mini" type="success" @click=""></el-button> <el-button size="mini" type="danger" @click=""></el-button> </template> </el-table> </div> </el-card> </div> </template> <script> import { mapGetters } from 'vuex'; export default{ name:'employee-list', mounted(){ const loading = this.$loading({ lock:true, text: loadingText, spinner: 'el-icon-loading', background: 'rgba(255,255,255,0.85', }); this.$store.dispatch('getEmployees'); loading.close(); }, computed:{ ...mapGetters({ tableData: "tableData" }) }, data(){ return{ search:null, form:{ }, tableColumns: [ { prop: 'name', label: 'Name', width: 40, sortable: true, hidden: true, align: 'center', fixed:true }, { prop: 'department', label: 'Department', width: 40, sortable: false, hidden: true, align: 'center', fixed:true }, { prop: 'section', label: 'Section', width: 40, sortable: false, hidden: true, align: 'center', fixed:true }, { prop: 'email', label: 'Email', width: 40, sortable: false, hidden: true, align: 'center', fixed:true } ]; } }, methods:{ } } </script>
-------------------------------------
Index.js
-------------------------------------
state:{ tableData: [], }
------------------------------------
getters.js
------------------------------------
export default { tableData(state){ return state.tableData; } }
------------------------------------
actions.js
------------------------------------
export const getEmployees({commit}, payload) => { axios.get(`/get-employees-data`, payload).then(res => { commit('setTableData', res.data); }); }
----------------------------------
web.php
----------------------------------
Route::get('get-employees-data', [EmployeesController::class, 'getEmployeesData']);
----------------------------------
EmployeesController
----------------------------------
public function getEmployeesData(){ try { returen Employee::all(); } catch(Exception $e) { dd($e); } }
----------------------------------
mutations.js
---------------------------------
export default { setTableData(state, tableData){ state.tableData = tableData; } }
------------------------------------------------------------------------
---> Step 4
-----------
--------------
---------------------
EmployeeList.vue
---------------------
<el-button size="mini" type="success" @click="editEmployee(scope.$index, scope.row)"> Edit</el-button> <el-button size="mini" type="danger" >Delete</el-button> editEmployee(index,row){ window.location.href="/edit-employee/"+row.id+"/edit"; }
--------------------------
web.php
--------------------------
Route::get('edit-employee/{id}/edit',[EmployeesController::class, 'edit']);
-------------------------
EmployeesController
-------------------------
public function edit($id){ $data = [ 'scope' => 'edit', 'id' => $id ]; return view('employee.form')->with($data); }
---------------------------
EmployeeForm.vue
--------------------------
props:{ scope: String, id: Number } mounted(){ if(this.scope == 'edit') { axios.get(`/fetch-employee-data/${this.id}`).then(res => { this.$set(this, 'form', res.data.data); }); } }
--------------------------------
web.php
--------------------------------
Route::get('fetch-employee-data/{id}',[EmployeesController::class, 'getEmployeeDataById']);
-------------------------------
EmployeesController
-------------------------------
public function getEmployeeDataById($id) { $employee = Employee::find($id); return response()->json([ 'status' => 200, 'data' => $employee ]); }
-----------------------------------
EmployeeForm.vue
----------------------------------
saveForm(formName){ this.$refs[formName].validate((valid) => { if(valid){ if(this.scope == 'create'){ this.$store.dispatch('saveEmployee', this.form); }else{ this.$store.dispatch('updateEmployee', { id: this.id, form:this.form}); } } }); }
----------------------------------
action.js
----------------------------------
export const saveEmployee = ({ commit }, payload) => { displayLoader('Updating employee'); axios.put(`/update-employee/${payload.id}`, payload.form).then(res => { Vue.prototype.$notify({ title: 'Success', message: res.data.message, type: 'success', }) removeLoader(); setTimeout(() => { window.location.href = '/employees' }, 2000); }) }
-----------------------------------
web.php
----------------------------------
Route::put('update-employee/{id}', [EmployeesController::class, 'update']);
------------------------------------
EmployeesController
------------------------------------
public function update(Request $request, $id) { try { $employee = new Employee::find($id); $employee->name = $request->name; $employee->department = $request->department; $employee->section = $request->section; $employee->email = $request->email; $employee->update(); return response()->json([ 'status' => 200, 'message' => 'Employee update successfully', ]); } catch(Exception $e) { dd($e); } }
------------------------------------------------------------------------
---> Step 5
-----------
--------------
--------------------
Employeelist.vue
--------------------
<el-button size="mini" type="info" @click="showEmployeeData(scope.$index, scope.row)">Show</el-button> <el-button size="mini" type="success" @click="editEmployee(scope.$index, scope.row)"> Edit</el-button> <el-button size="mini" type="danger" >Delete</el-button> <el-table-column align=right min-width="90"> showEmployeeData(){ window.location.href="/show-employee/"+row.id; }
--------------------
web.php
--------------------
Route::get('show-employee/{id}', [EmployeesController::class, 'show']);
-------------------
EmployeesController
------------------
public function show($id) { $data = [ 'scope' => 'show', 'id' => $id ]; return view('employee.form')->with($data); }
--------------------
EmployeeForm.vue
--------------------
computed: { readOnlyField(){ return this.scope == 'show' ? true : false; } }
in form input we pass
:readonly="readOnlyField()"
for button we add
v-if="scope != 'show'"
--------------------------------------------------------------------------
---> Step 6
-----------
--------------
--------------------
Employeelist.vue
--------------------
<el-button size="mini" type="danger" @click="deleteEmployee(scope.$index, scope.row)">Delete</el-button> deleteEmployee(index, row){ this.$confirm('Are you sure, You want to delete this Employee', 'Warning', { confirmButtonText: 'OK', cancelButtonText: 'Cancel', type: 'warning' }).then(() => { this.$store.dispatch('deleteEmployee', { id: row.id, }); this.$store.dispatch('getEmployees'); this.$message({ type: 'success', message: 'Delete completed' }); }).catch(() => { this.$message({ type: 'info', message: 'Delete canceled' }); }); }
---------------------------------
action.js
--------------------------------
export const updateEmployee = ({ commit }, payload) => { displayLoader('Deleting employee'); axios.delete(`/delete-employee/${payload.id}`).then(res => { Vue.prototype.$notify({ title: 'Success', message: res.data.message, type: 'success', }) removeLoader(); }) }
--------------------------------
web.php
-------------------------------
Route::delete('delete-employee/{id}', [EmployeesController::class, 'destroy']);
--------------------------------
EmployeesController
--------------------------------
public function destroy($id) { $employee = Employee::find($id); $employee->delete(); return response()->json([ 'status' => 200, 'message' => 'Employee deleted successfully', ]); }
-------------------------------------------------------------------------
Search feature
----------------
EmployeeList.vue
-----------------
mounted(){ const loading = this.$loading({ lock: true, text: 'loading...', spinner: 'el-icon-loading', background: 'rgba(255,255,255,0.85)', }); this.$store.dispatch('getEmployeesData', {searchQuery: this.search}); loading.close(); }, watch:{ seach: function(keyword){ this.$store.dispatch('getEmployeesData', {searchQuery: this.search}); } }
---------------------------
action.js
--------------------------
//change to post export const getEmployeesData = ({ commit }, payload) => { axios.post(`/get-employee-data`, payload) .then(response => { commit('setTableData', response.data); }) }
--------------------------------
EmployeesController
--------------------------------
public function getEmployeeData(Request $request) { try { $query = Employee::select('name', 'department','section','email') if($request->searchQuery){ $query->where(function($q) use $request) { $q->orWhere('name', 'like', '%'.$request->searchQuery.'%') $q->orWhere('department', 'like', '%'.$request->searchQuery.'%') }) } $employees = $query->get(); return $employees; } catch(Exception $e) { dd($e); } }