Your cart
  • IMG
    {{cart_item.name}}
    {{cart_item.variation_attribute_name}}: {{cart_item.variation_attribute_label}}
    {{cart_item.item_unit}}: {{ setCurrency(cart_item.price)}}
    {{ setCurrency(cart_item.price*cart_item.quantity)}}
    Invalid quantity more than stock
Total :
{{setCurrency(cart.sub_total)}}

There is no item in the cart. If you want to buy, Please click here.

Step by step CRUD application using Laravel, Vue 3 and MySQL database with RESTful style

Created by :
CRUD, Laravel, Vue 3, MySQL, RESTful API
articles
Programming, Software and application
1857
2022-05-24 19:16:12

This tutorial will show you step by step on how to create basic data manipulation operations CRUD(Create, Read, Update and Delete)  using Laravel as backend programming, MySQL as Database, Vue 3 as frontend. Software architectural style is REST(Representational State Transfer) APIs. Before going to the main tutorial steps, we need to know the following basic terms to better understand. 

 

In the end of CRUD application, we will get a daily note management system by which one can manage daily note or dairy. So our target entity name is notes and basic fields for that entity are note number, title, datetime, note detail, involved person, location etc. 

RESTful API: 

A REST API (also known as RESTful API) is an application programming interface (API or web API) that conforms to the constraints of REST architectural style and allows for interaction with RESTful web services. REST stands for representational state transfer and was created by computer scientist Roy Fielding.

If entity name is notes, then RESTful API basic four methods and all url structures for CRUD operation are as like: 

api_base

http://localhost:4000

entity

notes

 

 API endpoints: 

 

REST API

URL

Detail

GET

notes

Request method is GET and this endpoint will get all records with json format

GET

notes/show/id

Request method is GET and this  endpoint will get single record for specific ID with json format

POST

notes/store

Request method is POST and this  endpoint is used to create a new record.

PUT

notes/update/id

Request method is POST and this endpoint is used to update an existing record for a specific ID. 

DELETE

notes/delete/id

Request method is DELETE and this endpoint is used to delete an existing record for a specific ID. 

 

Vue Js 3: 

Vue.js is an open-source model–view–viewmodel front end JavaScript framework for building user interfaces and single-page applications. Latest stable version of Vue JS is 3. 

Vue CLI is a full system for rapid Vue.js development which aims to be the standard tooling baseline for the Vue ecosystem.  

 

Laravel:
Laravel is a free, open-source PHP web framework, created by Taylor Otwell and intended for the development of web applications following the model–view–controller architectural pattern and based on Symfony.

 

MySQL: 

MySQL is an open-source relational database management system. Its name is a combination of "My", the name of co-founder Michael Widenius's daughter, and "SQL", the abbreviation for Structured Query Language.

 

CRUD Operation: 

CRUD stands for Create, Read, Update and Delete which are basic operations of persistent storage. CRUD operations are basic data manipulation for databases which is very important in database based applications. To understand this CRUD clearly is a very basic task for any application developer. 

 

Prerequisites

Before getting started with this tutorial, you must be aware of the fundamentals of 

  •  HTML and CSS

  • Mordan JavaScript, or ES6. 

  • Vue JS

  • PHP 

  • MySQL and 

  • API request  

 

Software Installation

 

This tutorial is mainly divided into two parts: 

Part 1: Build backend or API  using Laravel and MySQL Database 

Part 2: Build frontend or UI using Node, Vue JS, Bootstrap

 

Before starting steps, Go to PHP server root directory www or htdocs and create a folder named crud_app and open terminal 

 

Let's start now. 

Part 1: Build backend or API  using Laravel and MySQL Database 

Step 1: Install Laravel App

Open terminal further run below command to manifest a new laravel project. 

 

composer create-project laravel/laravel backend --prefer-dist

 

If you want to know, how create first project in laravel, please visit https://laravel.com/docs/4.2/quick

 

cd backend

php artisan serve

Once you have started the Artisan development server, you may access your application at http://localhost:8000.

 

 

Step 2: Database Connection

This step explains how to make database connection by adding database name, username and password in .env config file of your project:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=crud_db
DB_USERNAME=root
DB_PASSWORD=

 

Step 3: Run migration (Optional): 

Add following code in database/migrations/create_notes_table.php:

<?php

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

class CreateNotesTable extends Migration {

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up() {

        Schema::create('notes', function (Blueprint $table) {
            $table->id();
            $table->string('note_number');
            $table->string('title');
            $table->dateTime('date_time');
            $table->text('description');
            $table->string('involved_person');
            $table->string('location');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down() {
        Schema::dropIfExists('notes');
    }

}

 

Run following command to run migrate: 

php artisan make:migration create_notes_table

Step 4: Create Model  

Run the below process to respectively create the Model (database table) and migration file:

php artisan make:model note -m

Update model with below code: 

<?php

namespace App\Models;

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

class Note extends Model
{
    const UID = "123213";
    
      //'note_number', 'title', 'date_time', 'description', 'involved_person', 'location'
    use HasFactory;
    protected $fillable = [
        'uid',
        'note_number', 
        'title', 
        'date_time', 
        'description', 
        'involved_person', 
        'location' 
    ];    
    
    public $timestamps = false;
}

Step 5: Create Notes Controller

You need to create the notes controller and define the CRUD operations methods:

php artisan make:controller NotesController

Open and update the below code in app\Http\Controllers\NotesController.php:

<?php

namespace App\Http\Controllers;

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

class NotesController extends Controller {

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index() {
        $notes = Note::all()->toArray();
        return array_reverse($notes);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request) {
        $note = new Note([
            'note_number' => $request->input('note_number'),
            'uid' => uniqid(),
            'title' => $request->input('title'),
            'date_time' => $request->input('date_time'),
            'description' => $request->input('description'),
            'involved_person' => $request->input('involved_person'),
            'location' => $request->input('location')
        ]);
        $note->save();
        $response = [];
        $response["code"] = 200;
        $response["message"] = 'A Daily Note has been created!';

        return response()->json($response);
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id) {
        $note = Note::where('id', $id)->first();
        return $note;
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update($id, Request $request) {
        $note = Note::find($id);
        $note->update($request->all());
        $response["code"] = 200;
        $response["message"] = 'A Daily Note has been updated!';

        return response()->json($response);
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id) {
        $note = Note::find($id);
        $note->delete();
         $response["code"] = 200;
        $response["message"] = 'A Daily Note has been deleted!';
        return response()->json($response);
    }

}

Step 6 Create CRUD Routes

Open routes/web.php file, in here; you have to define the following route:

<?php

use Illuminate\Support\Facades\Route;

/*
  |--------------------------------------------------------------------------
  | Web Routes
  |--------------------------------------------------------------------------
  |
  | Here is where you can register web routes for your application. These
  | routes are loaded by the RouteServiceProvider within a group which
  | contains the "web" middleware group. Now create something great!
  |
 */

Route::get('/', function () {
    return view('welcome');
});
 
use App\Http\Controllers\NotesController;
Route::middleware(['cors'])->group(function () {
    Route::get('/notes', [NotesController::class, 'index']);
    Route::get('/notes/show/{id}', [NotesController::class, 'show']);
    Route::post('/notes/store', [NotesController::class, 'store']);
    Route::post('/notes/update/{id}', [NotesController::class, 'update']);
    Route::post('/notes/delete/{id}', [NotesController::class, 'destroy']);
});

Step 7: Enable CORS on Laravel and  Exclude Route From Verify CSRF Token

Enable CORS:

You can use a middleware that adds Access-Control-Allow-Origin to an http response header.

First, create an middleware

 

php artisan make:middleware Cors

 

Secondly, append  'cors'          => \App\Http\Middleware\Cors::class in the middleware section to Kernel.php files

 

protected $routeMiddleware = [

        …..

        'cors'          => \App\Http\Middleware\Cors::class, 

    ];

 

Exclude Route From Verify CSRF Token 
Navigate to the app/Http/Middleware folder in your Laravel application. You will find VerifyCsrfToken.php and update $except variable for notes all API endpoints.  

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array<int, string>
     */
    protected $except = [
        "notes",
        "notes/*"
    ];
}

Step 8: Check API using postman

Open postman and create the following collection request which will help to check if the API request is working or not. 

 

Endpoint 1 - to get all notes: http://127.0.0.1:8000/notes

Endpoint 2- to get single note: http://127.0.0.1:8000/notes/show/2

Endpoint 3- to add new note: http://127.0.0.1:8000/notes/store

Endpoint 4 - to update record: http://127.0.0.1:8000/notes/update/1

Endpoint 5 - to delete record: http://127.0.0.1:8000/notes/delete/1

Part 2 Build frontend or UI using Node, ReactJS, Bootstrap

Step 1: VueCli Installation

To install the new package, use the following commands. 

npm install -g @vue/cli

Step 2: Create Vue Project

To create a new project, run:

vue create frontend

This will give us a screen where we’re going to choose to use “Vue 3 Preview”:

This will create a base Vue 3 application, and we can ensure it works by going into the directory and running the server:

  

cd frontend
npm run serve

Output: 

DONE  Compiled successfully in 2963ms       10:30:22 PM

  App running at:
  - Local:   http://localhost:8080/ 
  - Network: http://192.168.0.108:8080/

  Note that the development build is not optimized.
  To create a production build, run npm run build.

Step 3 Install Vue Router, Axios, and vue-axios.

Okay, now go to the terminal and type the following command to install the vue-router, axios, and vue-axios dependencies.

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

Now that we have that, we can have a look at setting up Bootstrap for our project.

We first install the dependencies:

npm install bootstrap bootstrap-vue 

After those have been installed, we need to open up our project in our IDE. 

 

 Step 4: Create required files

Create views folder under src and create following files and update respective content. 

NoteList.vue: 

Create views/notes/NoteList.vue and update following code

<template>
    <router-link class="btn btn-primary my-3"  to="/notes/add">Add Note</router-link> 
    <div class="card">
        <div class="card-header">
            Notes 
        </div>
        <div class="card-body">
            <table class="table table-hover">
                <thead>
                    <tr>
                        <th scope="col">Date</th>
                        <th scope="col">Note Number</th>
                        <th scope="col">Title</th>
                        <th scope="col">Action</th>
                    </tr>
                </thead>
               <tbody>
                   Item Loop... 
                </tbody>
            </table>

        </div>
    </div>
</template>

<script>

    export default {
        data() {
            return{
                items: []
            }
        },
        inject: ['apiBasePath'],
        created: function ()
        {
            this.fetchItems();
        },

        methods: {
            fetchItems()
            {
                this.axios.get(this.apiBasePath + 'notes', {
                }).then(res => {
                    console.log(res);
                    this.items = res.data;
                }).catch(err => {
                    console.log(err.response);
                });
            },
            deleteNote(id) {
                if (confirm("Do you confirm to delete this item")) {
                    this.axios.post(this.apiBasePath + 'notes/delete/' + id)
                            .then(res => {
                                if (res.status === 200 && res.data.code === 200) {
                                    let i = this.items.map(data => data.id).indexOf(id);
                                    this.items.splice(i, 1)
                                }
                            });
                }

            }
        }
    }
</script>

NoteForm.vue: 

Create views/notes/NoteForm.vue and update following code 

<template>
    <div>

        <div class="mb-3">
            <label for="note_number" class="form-label">Note number</label>
            <input v-model="formItem.note_number" type="text" class="form-control" id="note_number" placeholder="">

        </div>
        <div class="mb-3">
            <label for="title" class="form-label">Note Title</label>
            <input v-model="formItem.title" type="text" class="form-control" id="title" placeholder="">
        </div>

        <div class="mb-3">
            <label for="date_time" class="form-label">Date time</label>
            <input v-model="formItem.date_time" type="datetime-local" class="form-control" id="date_time" placeholder="">
        </div>

        <div class="mb-3">
            <label for="description" class="form-label">Description</label>
            <textarea v-model="formItem.description"   class="form-control" id="description" placeholder=""></textarea>
        </div>
        <div class="mb-3">
            <label for="involved_person" class="form-label">Involved person</label>
            <input v-model="formItem.involved_person" type="text" class="form-control" id="involved_person" placeholder="">
        </div>
        <div class="mb-3">
            <label for="location" class="form-label">Location</label>
            <input v-model="formItem.location" type="text" class="form-control" id="location" placeholder="">
        </div>
    </div>
</template>
<script>
    export default {
        name: "NoteForm",
        props: ["item"], // declare the props
        computed: {
            formItem() {
                return this.item;
            }
        }

    }
</script>

 

CreateNote.vue: 

Create views/notes/CreateNote.vue and update following code

<template>
    <router-link class="btn btn-primary my-3"  to="/notes">Back To All Notes</router-link> 
    <div class="card">
        <form @submit.prevent="addNote">
            <div class="card-header">
                Add Note
            </div>
            <div class="card-body">
                <NoteForm :item="item_data" />
            </div>

            <div class="card-footer">
                <button type="submit"  class="btn btn-info mx-1 text-white"    >Save</button>   or 
                <router-link class="btn btn-danger text-white"  to="/notes">Cancel</router-link>  
            </div>
        </form>
    </div>
</template>
<script>
    import NoteForm from '@/views/notes/NoteForm.vue';

    export default {
        name: 'App',
        data() {
            return{
                item_data: {},
                successMessage: "",
                errorMessage: ""

            };
        },
        inject: ['apiBasePath'],
        methods: {
            addNote() {
                this.axios.post(this.apiBasePath + 'notes/store/', this.item_data)
                        .then((res) => {

                            if (res.status === 200 && res.data.code === 200) {
                                this.successMessage = res.data.message;
                                this.$router.push("/notes");
                            } else {
                                this.errorMessage = res.data.message;
                            }
                        });
            },
        },

        components: {
            NoteForm
        }
    }
</script>

EditNote.vue: 

Create views/notes/EditNote.vue and update following code 

<template>
    <router-link class="btn btn-primary my-3"  to="/notes">Back To All Notes</router-link> 
    <div class="card">
        <form @submit.prevent="updateNote"> 
            <div class="card-header">
                Edit Note
            </div>
            <div class="card-body">
                <NoteForm :item="item_data"  />
            </div>
            <div class="card-footer">
                <button type="submit"  class="btn btn-info mx-1 text-white">Update</button>   or 
                <router-link class="btn btn-danger text-white"  to="/notes">Cancel</router-link>  
            </div>
        </form>
    </div>
</template>
<script>

    import NoteForm from '@/views/notes/NoteForm.vue';

    export default {
        name: 'App',
        data() {
            return{
                item_data: {},
                successMessage: "",
                errorMessage: ""
            }
        },
        inject: ['apiBasePath'],
        created: function ()
        {
            this.fetchItem();
        },

        methods: {
            fetchItem()
            {
                this.axios.get(this.apiBasePath + 'notes/show/' + this.$route.params.id, {
                }).then(res => {
                    // console.log(res);
                    this.item_data = res.data;
                }).catch(err => {
                    console.log(err.response);
                });
            },
            updateNote() {
                this.axios.post(this.apiBasePath + 'notes/update/' + this.$route.params.id, this.item_data)
                        .then((res) => {
                            if (res.status === 200 && res.data.code === 200) {
                                console.log(res);
                                this.successMessage = res.data.message;
                                this.$router.push('/notes');
                            } else {
                                this.errorMessage = res.data.message;
                            }
                        });
            }
        },
        components: {
            NoteForm
        }
    }
</script>

PageHome.vue: 

Create views/pages/PageHome.vue and update following code 

<template>
    <h1 class="mt-5"> CRUD application Home </h1>
    <div class="card">
        <div class="card-body p-3">
            <p> Step by step CRUD application using Laravel, Vue 3(CLI) and MySQL database with RESTful style.</p> 
            <p>
            <router-link class="nav-link" to="/notes">Go To CRUD Application</router-link>
            </p>
        </div>
    </div>
</template>

 

PageAbout.vue: 

Create views/pages/PageAbout.vue and update following code 

<template>
    <h1 class="mt-5"> About Me</h1>
    <div class="card">
        <div class="card-body p-3">
            <p> I am Md Saidul Haque. For details, visit <a href="https://saidulhaque.com/" target="_blank">https://saidulhaque.com/</a></p> 

        </div>
    </div>
</template>

 

Step 5: Create router files

Create src/router/index.js and update following code

import { createWebHistory, createRouter } from "vue-router";
import Home from "@/views/pages/PageHome.vue";
import About from "@/views/pages/PageAbout.vue";
import NoteList from "@/views/notes/NoteList.vue";
import CreateNote from '@/views/notes/CreateNote.vue';
import EditNote from '@/views/notes/EditNote.vue';
 
const routes = [
    {
        path: "/",
        name: "Home",
        component: Home,
    },
    {
        path: "/about",
        name: "About",
        component: About,
    },
    {
        path: "/notes",
        name: "Notes",
        component: NoteList,
    },
    {
        path: "/notes/add",
        name: "CreateNote",
        component: CreateNote,
    },
    {
        path: "/notes/edit/:id",
        name: "EditNote",
        component: EditNote,
    },
];

const router = createRouter({
    history: createWebHistory(),
    routes,
});

export default router;

 

Step 6: Update app.vue files

Update app.vue file with following code 

<template>
    <div class="">
        <header>
            <div class="d-flex flex-column flex-md-row align-items-center  py-2 px-5 border-bottom">
                <a href="/" class="d-flex align-items-center text-dark text-decoration-none">
                    <span class="fs-4">CRUD Application (Laravel + Vue 3(CLI) + MySQL)</span>
                </a>
                <nav class="d-inline-flex mt-2 mt-md-0 ms-md-auto">
                    <router-link class="nav-link active"  to="/">Home</router-link> 
                    <router-link class="nav-link" to="/about">About</router-link>
                    <router-link class="nav-link" to="/notes">Notes</router-link>
                </nav>
            </div>
        </header>

        <main>
            <div class="container">
                <router-view />
            </div>
        </main>
        <footer class="footer">
            <div class="container">
                <span class="text-muted">CRUD Application is basic and initial program for a developer who want to work with database.</span>
            </div>
        </footer>  


    </div>
</template>

<style>
    .top-bar{
        position: absolute;
    }

    .border-top {
        border-top: 1px solid #e5e5e5;
    }
    .border-bottom {
        border-bottom: 1px solid #e5e5e5;
    }

    .box-shadow {
        box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
    }

    .footer {
        position: absolute;
        bottom: 0;
        width: 100%;
        height: 60px;
        line-height: 60px;
        background-color: #f5f5f5;
    }
</style>

 

Step 7: Update main.js files 

Update main.js file with following code

import { createApp } from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'

import App from './App.vue'
import router from './router'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'jquery/src/jquery.js'
import 'bootstrap/dist/js/bootstrap.min.js'

const app = createApp(App)
app.use(VueAxios, axios) 
app.use(router).mount('#app')

//set base path as global variable 
app.provide('apiBasePath', "http://127.0.0.1:8000/")
 
 

Step 8: Run and Test Application 

 
npm run serve

And browse this URL: http://localhost:8080/ 

 

Enjoy!