In this post we will add the users module to manage the website users, so i will start by adding the backend CRUD in lumen then i will connect this to display them in the Nuxtjs admin panel.
Users CRUD
Let’s open the lumen project and update User model, update the $fillable property and add the $appends property like this:
app/Models/User.php
protected $fillable = [
'name', 'email', 'is_super_admin', 'password'
];
protected $appends = array('create_date');
public function getCreateDateAttribute()
{
return $this->created_at ? date('Y-m-d', strtotime($this->created_at)) : "";
}
The $appends property used to add custom properties to the response of the Eloquent models, so i added getCreateDateAttribute() method to return the create date of the user then i added it’s name to the $appends property in this case when you retrieve users you will see the create_date in the response.
Users Controller
Create UsersController.php in app/Http/Controllers directory, with the following code:
app/Http/Controllers/UsersController.php
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class UsersController extends Controller
{
public function __construct()
{
$this->middleware('super_admin_check:store-update-destroy');
}
public function index(Request $request)
{
$users = $this->filterAndResponse($request);
return response()->json(['users' => $users], 200);
}
public function store(Request $request)
{
$validator = $this->getValidator($request);
if ($validator->fails()) {
return response()->json(['success' => 0, 'message' => 'Please fix these errors', 'errors' => $validator->errors()], 500);
}
$bundle = $request->except('password_confirmation');
$bundle['password'] = app('hash')->make($request->input('password'));
$user = User::create($bundle);
return response()->json(['success' => 1, 'message' => 'Created successfully', 'user' => $user], 201);
}
public function show($id)
{
return response()->json(['user' => User::findOrFail($id)], 200);
}
public function update(Request $request, $id)
{
$user = User::findOrFail($id);
$validator = $this->getValidator($request, $id);
if ($validator->fails()) {
return response()->json(['success' => 0, 'message' => 'Please fix these errors', 'errors' => $validator->errors()], 500);
}
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->is_super_admin = $request->input('is_super_admin');
if($request->input('password') != "") {
$user->password = app('hash')->make($request->input('password'));
}
$user->save();
return response()->json(['success' => 1, 'message' => 'Updated successfully', 'user' => $user], 200);
}
public function destroy($id)
{
User::findOrFail($id)->delete();
return response()->json(['success' => 1, 'message' => 'Deleted successfully'], 200);
}
/**
* @param Request $request
*/
protected function filterAndResponse(Request $request)
{
$query = User::with("products", "orders")->orderBy('id', 'DESC');
if($request->has('all')) {
return $query->get();
}
if ($request->has('id')) {
$query->where('id', $request->id);
}
if ($request->has('name')) {
$query->where('name', 'like', "%" . $request->name . "%");
}
if ($request->has('email')) {
$query->where('email', 'like', "%" . $request->email . "%");
}
$users = $query->paginate(10);
return $users;
}
private function getValidator($request, $id = null)
{
$validate_rules = [
'name' => 'required|min:3',
'email' => 'required|email|' . ($id == null ? 'unique:users,email' : 'unique:users,email,' . $id),
];
if($id == null || $request->input('password') != "") {
$validate_rules += ['password' => 'required|min:4|confirmed'];
}
$validator = Validator::make($request->all(), $validate_rules);
return $validator;
}
}
The above controller is a simple CRUD controller so i will not go into the details of this controller as you already saw similar controllers in previous posts.
Now update routes/web.php
<?php
$router->get('/', function () use ($router) {
return $router->app->version();
});
$router->group(['prefix' => 'api'], function () use ($router) {
$router->post('/login', 'Auth\\LoginController@login');
$router->post('/register', 'Auth\\RegisterController@register');
$router->group(['prefix' => 'category'], function () use ($router) {
$router->get('/', 'CategoriesController@index');
$router->get('/htmltree', 'CategoriesController@getCategoryHtmlTree');
$router->get('/{id}', 'CategoriesController@show');
});
$router->group(['prefix' => 'brand'], function () use ($router) {
$router->get('/', 'BrandsController@index');
$router->get('/{id}', 'BrandsController@show');
});
$router->group(['prefix' => 'product'], function () use ($router) {
$router->get('/', 'ProductsController@index');
$router->get('/{id}', 'ProductsController@show');
});
$router->group(['prefix' => 'user'], function () use ($router) {
$router->get('/', 'UsersController@index');
$router->get('/{id}', 'UsersController@show');
});
$router->group(['middleware' => 'auth:api'], function () use ($router) {
$router->get('/me', 'Auth\\LoginController@userDetails');
$router->get('/logout', 'Auth\\LoginController@logout');
$router->get('/check-login', 'Auth\\LoginController@checkLogin');
$router->group(['prefix' => 'category'], function () use ($router) {
$router->post('/', 'CategoriesController@store');
$router->put('/{id}', 'CategoriesController@update');
$router->delete('/{id}', 'CategoriesController@destroy');
});
$router->group(['prefix' => 'brand'], function () use ($router) {
$router->post('/', 'BrandsController@store');
$router->put('/{id}', 'BrandsController@update');
$router->delete('/{id}', 'BrandsController@destroy');
});
$router->group(['prefix' => 'product'], function () use ($router) {
$router->post('/', 'ProductsController@store');
$router->put('/{id}', 'ProductsController@update');
$router->delete('/delete-image/{id}', 'ProductsController@destroyImage');
$router->delete('/{id}', 'ProductsController@destroy');
});
$router->group(['prefix' => 'user'], function () use ($router) {
$router->post('/', 'UsersController@store');
$router->put('/{id}', 'UsersController@update');
$router->delete('/{id}', 'UsersController@destroy');
});
});
});
Manipulating Users In Nuxtjs
The second thing we need to do in the Nuxt project in the /admin/ directory is to create the pages responsible for manipulating users.
Users Api
As usual we will start with implementing the apis or services for users so go to /admin/api/ directory and create user.js with following code:
/admin/api/user.js
const UserApi = {
create: (axios, payload) => {
return axios.$post('/api/user', payload);
},
list: (axios, payload = null) => {
let payload_arr = [];
if(payload) {
for(let key in payload) {
payload_arr.push(key +"=" + payload[key]);
}
}
return axios.$get('/api/user?' + payload_arr.join("&"));
},
getAll: (axios) => {
return axios.$get('/api/user?all=1');
},
delete: (axios, id) => {
return axios.$delete('/api/user/' + id);
},
show: (axios, id) => {
return axios.$get('/api/user/' + id);
},
update(axios, payload, id) {
return axios.$put('/api/user/' + id, payload);
}
};
export {UserApi};
Next let’s create the store for users module.
Users Vuex Store
As we seen in previous tutorials how the store work in Nuxtjs i prepared the user store, in the same way as we done in previous lesson. So in /admin/store directory create user.js with the below code:
/admin/store/user.js
import { UserApi } from '../api/user';
export const state = () => ({
user: {
name: '',
email: '',
password: '',
password_confirmation: '',
is_super_admin: 0
},
filterData: {
id: '',
name: '',
email: ''
},
page: 1,
userList: {},
allUsers: []
});
export const mutations = {
setUser(state, payload) {
state.user[payload.key] = payload.value;
},
resetUser(state) {
state.user = {
name: '',
email: '',
password: '',
password_confirmation: '',
is_super_admin: 0
};
},
setFilterData(state, payload) {
state.filterData[payload.key] = payload.val;
},
setPage(state, page) {
state.page = page;
},
setUserList(state, userList) {
state.userList = userList;
},
setAllUsers(state, value) {
state.allUsers = value;
}
};
export const actions = {
create({commit, dispatch, state}, payload) {
commit('shared/resetStatusMessagesParameters', null, {root: true});
commit('shared/setStatusMessageParameter', {key: 'showLoading', val: true}, {root: true});
const dataToSend = {...state.user};
UserApi.create(this.$axios, dataToSend).then(response => {
commit('shared/setStatusMessageParameter', {key: 'showLoading', val: false}, {root: true});
if(response.success) {
commit('shared/setStatusMessageParameter', {key: 'success_message', val: response.message}, {root: true});
}
setTimeout(() => {
payload.router.push('/user');
}, 2000);
}).catch(err => {
dispatch('showValidationErrors', err);
}).finally(() => {
setTimeout(() => {
commit('shared/resetStatusMessagesParameters', null, {root: true});
}, 10000);
});
},
list({commit}, payload = null) {
UserApi.list(this.$axios, payload).then(response => {
commit('setUserList', response.users);
});
},
getAllUsers({commit}) {
UserApi.getAll(this.$axios).then(response => {
commit('setAllUsers', response.users);
}).catch(err => {
console.log(err);
});
},
show({commit}, id) {
commit('shared/resetStatusMessagesParameters', null, {root: true});
commit('shared/setStatusMessageParameter', {key: 'showLoading', val: true}, {root: true});
commit('resetUser');
UserApi.show(this.$axios, id).then(response => {
commit('shared/setStatusMessageParameter', {key: 'showLoading', val: false}, {root: true});
if(response.user) {
commit('setUser', {key: 'name', value: response.user.name});
commit('setUser', {key: 'email', value: response.user.email});
commit('setUser', {key: 'is_super_admin', value: response.user.is_super_admin});
}
}).catch(err => {
console.log(err);
});
},
update({commit, state, dispatch}, payload) {
commit('shared/resetStatusMessagesParameters', null, {root: true});
commit('shared/setStatusMessageParameter', {key: 'showLoading', val: true}, {root: true});
const dataToSend = {...state.user};
UserApi.update(this.$axios, dataToSend, payload.id).then(response => {
commit('shared/setStatusMessageParameter', {key: 'showLoading', val: false}, {root: true});
if(response.success) {
commit('shared/setStatusMessageParameter', {key: 'success_message', val: response.message}, {root: true});
}
setTimeout(() => {
payload.router.push('/user');
}, 2000);
}).catch(err => {
dispatch('showValidationErrors', err);
}).finally(() => {
setTimeout(() => {
commit('shared/resetStatusMessagesParameters', null, {root: true});
}, 10000);
});
},
delete({commit, state, dispatch}, id) {
commit('shared/resetStatusMessagesParameters', null, {root: true});
commit('shared/setStatusMessageParameter', {key: 'showLoading', val: true}, {root: true});
UserApi.delete(this.$axios, id).then(response => {
commit('shared/setStatusMessageParameter', {key: 'showLoading', val: false}, {root: true});
if(response.success) {
let userList = {...state.userList};
userList.data = userList.data.filter(item => item.id !== id);
commit('setUserList', userList);
commit('shared/setStatusMessageParameter', {key: 'success_message', val: response.message}, {root: true});
}
}).catch(err => {
dispatch('showValidationErrors', err);
}).finally(() => {
setTimeout(() => {
commit('shared/resetStatusMessagesParameters', null, {root: true});
}, 10000);
});
},
showValidationErrors({commit}, err) {
commit('shared/setStatusMessageParameter', {key: 'showLoading', val: false}, {root: true});
if(err.response.data) {
commit('shared/setStatusMessageParameter', {key: 'error_message', val: err.response.data.message}, {root: true});
if(err.response.data.errors) {
let errors = [];
for(let key in err.response.data.errors) {
errors.push(err.response.data.errors[key][0]);
}
commit('shared/setStatusMessageParameter', {key: 'validation_errors', val: errors}, {root: true});
}
}
}
}
Users Pages
At this point we are ready to create the pages needed for users display and management so go to /admin/pages and create user/ directory and inside it add these pages:
- pages/user/index.vue
- pages/user/create.vue
- pages/user/_edit.vue
Also in the /admin/components directory create user-components/ directory and add these partial components inside it:
- components/user-components/UserFilter.vue
- components/user-components/UserForm.vue
- components/user-components/UserRow.vue
Displaying All Users
Let’s display all users in index.vue page
index.vue
<template>
<div class="main-content">
<div class="section__content section__content--p30">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<loader></loader>
<status-messages></status-messages>
<UserFilter v-on:Filtering="handleFiltering"></UserFilter>
</div>
<div class="col-md-12">
<!-- DATA TABLE-->
<div class="table-responsive m-b-40">
<table class="table table-borderless table-data3">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email</th>
<th>Admin</th>
<th>Create Date</th>
<th>Count Products</th>
<th>Count Order</th>
<th>Options</th>
</tr>
</thead>
<tbody>
<UserRow v-if="userList.data" v-for="user of userList.data" v-bind:user="user" v-bind:key="user.id" v-on:removeUser="removeUser"></UserRow>
</tbody>
</table>
</div>
<Pagination :data="userList" v-if="userList.data" v-on:handlePagination="handlePagination"></Pagination>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Loader from "../../components/helpers/loader";
import statusMessages from "../../components/helpers/statusMessages";
import UserRow from "../../components/user-components/UserRow";
import Pagination from "../../components/helpers/Pagination";
import UserFilter from "../../components/user-components/UserFilter";
export default {
name: "index",
middleware: "auth",
components: {
UserFilter,
Pagination,
UserRow,
Loader,
statusMessages
},
fetch() {
this.$store.dispatch('user/list');
},
computed: {
userList() {
return this.$store.state.user.userList;
}
},
methods: {
handlePagination(page_number) {
this.$store.commit('user/setPage', page_number);
this.$store.dispatch('user/list', this.getPayload());
},
handleFiltering(field, value) {
this.$store.commit('user/setFilterData', {key: field, val: value});
this.$store.commit('user/setPage', 1);
this.$store.dispatch('user/list', this.getPayload());
},
getPayload() {
let payload = {};
for(let field in this.$store.state.user.filterData) {
if(this.$store.state.user.filterData.hasOwnProperty(field) && this.$store.state.user.filterData[field] !== '')
payload[field] = this.$store.state.user.filterData[field];
}
payload.page = this.$store.state.user.page;
return payload;
},
removeUser(id) {
if(confirm("Are you sure?")) {
this.$store.dispatch('user/delete', id);
}
}
}
}
</script>
<style scoped>
</style>
/admin/components/user-components/UserFilter.vue
<template>
<form method="get" action="#" style="margin-bottom: 10px">
<h4>Filter</h4>
<div class="row">
<div class="col-1">
<input type="text" class="form-control" placeholder="Id" name="id" @change="handleFiltering($event)" :value="this.$store.state.user.filterData.id">
</div>
<div class="col-3">
<input type="text" class="form-control" placeholder="Name" name="name" @change="handleFiltering($event)" :value="this.$store.state.user.filterData.name">
</div>
<div class="col-3">
<input type="text" class="form-control" placeholder="Email" name="email" @change="handleFiltering($event)" :value="this.$store.state.user.filterData.email">
</div>
<div class="col-5">
<nuxt-link to="/user/create" class="btn btn-success pull-right"><i class="fa fa-plus-square"></i> Create</nuxt-link>
</div>
</div>
</form>
</template>
<script>
export default {
name: "UserFilter",
methods: {
handleFiltering(event) {
this.$emit('Filtering', event.target.name, event.target.value);
}
}
}
</script>
<style scoped>
</style>
/admin/components/user-components/UserRow.vue
<template>
<tr>
<td>{{ this.user.id }}</td>
<td>{{ this.user.name }}</td>
<td>{{ this.user.email }}</td>
<td>
<span class="badge badge-info" v-if="this.user.is_super_admin == 1">Yes</span>
<span class="badge badge-warning" v-if="this.user.is_super_admin == 0">No</span>
</td>
<td>{{ this.user.create_date }}</td>
<td>{{ this.user.products.length > 0 ? this.user.products.length : 'none' }}</td>
<td>{{ this.user.orders.length > 0 ? this.user.orders.length : 'none' }}</td>
<td>
<nuxt-link :to="'/user/' + this.user.id" class="btn btn-info btn-sm"><i class="fa fa-edit"></i></nuxt-link>
<a href="#" class="btn btn-danger btn-sm" @click.prevent="removeUser(user.id)"><i class="fa fa-remove"></i></a>
</td>
</tr>
</template>
<script>
export default {
name: "UserRow",
props: ['user'],
methods: {
removeUser(userId) {
this.$emit('removeUser', userId);
}
}
}
</script>
<style scoped>
</style>
Creating Users
Open pages/user/create.vue and add the code below
pages/user/create.vue
<template>
<div class="main-content">
<div class="section__content section__content--p30">
<div class="container-fluid">
<loader></loader>
<status-messages></status-messages>
<form method="post" action="#" @submit.prevent="save()">
<UserForm></UserForm>
<div class="row">
<button type="submit" class="btn btn-lg btn-info btn-block">
<i class="fa fa-save fa-lg"></i> Save
</button>
</div>
</form>
</div>
</div>
</div>
</template>
<script>
import Loader from '../../components/helpers/loader';
import statusMessages from '../../components/helpers/statusMessages';
import UserForm from '../../components/user-components/UserForm';
export default {
name: "CreateUser",
middleware: "auth",
components: {
Loader,
statusMessages,
UserForm
},
methods: {
save() {
this.$store.dispatch('user/create', {router: this.$router});
}
},
mounted() {
this.$store.commit('user/resetUser');
}
}
</script>
<style scoped>
</style>
As you see in this code we extracted the form into a separate component <UserForm /> like we did in the previous part with products to make things simple.
components/user-components/UserForm.vue
<template>
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<strong>{{ this.$route.params.edit?'Edit User #' + this.$route.params.edit : 'Create New User' }}</strong>
</div>
<div class="card-body card-block">
<div class="form-group">
<label for="name" class=" form-control-label">Name <span class="required-in">*</span></label>
<input type="text" id="name" name="name" placeholder="Name" class="form-control" v-model="name" />
</div>
<div class="form-group">
<label for="email" class=" form-control-label">Email <span class="required-in">*</span></label>
<input type="text" id="email" name="email" placeholder="Email" class="form-control" v-model="email" />
</div>
<button type="button" class="btn btn-info" v-if="this.$route.params.edit" @click="togglePasswordBlock()">Change Password</button>
<div v-if="!this.$route.params.edit || showEditPassword">
<div class="form-group">
<label for="password" class=" form-control-label">Password <span class="required-in">*</span></label>
<input type="password" id="password" name="password" placeholder="Password" class="form-control" v-model="password" />
</div>
<div class="form-group">
<label for="password_confirmation" class=" form-control-label">Password Confirmation <span class="required-in">*</span></label>
<input type="password" id="password_confirmation" name="password_confirmation" placeholder="Password Confirmation" class="form-control" v-model="password_confirmation" />
</div>
</div><br/>
<div class="form-group">
<label for="is_super_admin" class=" form-control-label">Is Super Admin</label>
<label class="switch switch-default switch-primary mr-2" size="lg">
<input type="checkbox" class="switch-input" name="is_super_admin" id="is_super_admin" value="1" v-model="is_super_admin">
<span class="switch-label"></span>
<span class="switch-handle"></span>
</label>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "UserForm",
data() {
return {
showEditPassword: false
}
},
computed: {
name: {
get() {
return this.$store.state.user.user.name;
},
set(value) {
this.$store.commit('user/setUser', {key: 'name', value});
}
},
email: {
get() {
return this.$store.state.user.user.email;
},
set(value) {
this.$store.commit('user/setUser', {key: 'email', value});
}
},
password: {
get() {
return this.$store.state.user.user.password;
},
set(value) {
this.$store.commit('user/setUser', {key: 'password', value});
}
},
password_confirmation: {
get() {
return this.$store.state.user.user.password_confirmation;
},
set(value) {
this.$store.commit('user/setUser', {key: 'password_confirmation', value});
}
},
is_super_admin: {
get() {
return this.$store.state.user.user.is_super_admin;
},
set(value) {
value = value == true ? 1 : 0;
this.$store.commit('user/setUser', {key: 'is_super_admin', value});
}
}
},
methods: {
togglePasswordBlock() {
this.showEditPassword = !this.showEditPassword;
}
}
}
</script>
<style scoped>
</style>
Updating Users
The last page to update is the _edit.vue page which enable us to update users.
pages/user/_edit.vue
<template>
<div class="main-content">
<div class="section__content section__content--p30">
<div class="container-fluid">
<loader></loader>
<status-messages></status-messages>
<form method="post" action="#" @submit.prevent="save()">
<UserForm></UserForm>
<div class="row">
<button type="submit" class="btn btn-lg btn-info btn-block">
<i class="fa fa-save fa-lg"></i> Save
</button>
</div>
</form>
</div>
</div>
</div>
</template>
<script>
import Loader from '../../components/helpers/loader';
import statusMessages from '../../components/helpers/statusMessages';
import UserForm from '../../components/user-components/UserForm';
export default {
name: "EditUser",
middleware: "auth",
components: {
Loader,
statusMessages,
UserForm
},
fetch() {
if(this.$route.params.edit) {
this.$store.dispatch('user/show', this.$route.params.edit);
}
},
methods: {
save() {
this.$store.dispatch('user/update', {router: this.$router, id: this.$route.params.edit});
}
},
mounted() {
}
}
</script>
<style scoped>
</style>
Updating Navbar
Let’s update the navbar to display the link for user module so go to components/partials/NavMenu.vue and update as shown below:
components/partials/NavMenu.vue
<template>
<ul :class="ulclass">
<li :class="{active: this.$route.path === '/'}">
<nuxt-link to="/">
<i class="fas fa-tachometer-alt"></i>Dashboard
</nuxt-link>
</li>
<li :class="{active: this.$route.path.indexOf('category') !== -1}">
<nuxt-link to="/category">
<i class="fas fa-list"></i>Categories
</nuxt-link>
</li>
<li :class="{active: this.$route.path.indexOf('brand') !== -1}">
<nuxt-link to="/brand">
<i class="fas fa-television"></i>Brands
</nuxt-link>
</li>
<li :class="{active: this.$route.path.indexOf('product') !== -1}">
<nuxt-link to="/product">
<i class="fas fa-shopping-cart"></i>Products
</nuxt-link>
</li>
<li>
<a href="#">
<i class="fas fa-gears"></i>Orders</a>
</li>
<li>
<a href="#">
<i class="fas fa-gears"></i>Pending Orders</a>
</li>
<li :class="{active: this.$route.path.indexOf('user') !== -1}">
<nuxt-link to="/user">
<i class="fas fa-users"></i>Users
</nuxt-link>
</li>
<li>
<a href="#">
<i class="fas fa-sign-out-alt"></i>Logout</a>
</li>
</ul>
</template>
<script>
export default {
name: "nav-menu",
props: ['ulclass'],
mounted() {
}
}
</script>
<style scoped>
</style>
Now give this a try by runing “npm run dev” and go to http://localhost:4000/user.
In the next part we will move on by starting to prepare the html template we will be using on the website.
Continue to Part10: Preparing Website Html Template


