Source code: https://github.com/sunnygreentea/laravel-vue-crud-contact
The combination of Laravel and Vue.js becomes more popular these days. Laravel plus Vue.js provides a clear and efficient way to build event-driven web applications, it gives users a wonderful seamless front-end experience. With many Vue.js features great features such as model binding, reactive components, we only need to request API data from Laravel and make UI changes by switching components without reloading the whole page.
This demo shows how to build a Laravel + Vue CRUD project, while Laravel plays a role as API provider and Vue.js as front end player.
Taking contact list as an example for this project:
Database:
The database only contains one table to store all contacts:
php artisan make:migration create_contacts_table
database/migrations/create_contacts_table.php
public function up() { Schema::create('contacts', function (Blueprint $table) { $table->id(); $table->string('first_name'); $table->string('last_name'); $table->string('email'); $table->string('phone'); $table->timestamps(); }); }
Resource
Eloquent resources can be used as a transformation layer that sits between models and the JSON responses for Restful APIs. Here we create a resource class so in the api controller we can return the resource or a collection of resources or the paginated response.
php artisan make:resource Contact
app/Http/Resources/Contact.php
public function toArray($request) { //return parent::toArray($request); return [ 'id' => $this->id, 'first_name' => $this->first_name, 'last_name' => $this->last_name, 'email' => $this->email, 'phone' => $this->phone, ]; }
Controller
php artisan make:controller ContactController
app/Http/Controllers/ContactController.php
namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Contact; use App\Http\Resources\Contact as ContactResource; class ContactController extends Controller { public function index() { // Get articles $contacts = Contact::orderBy('created_at', 'desc')->paginate(10); // Return collection of contacts as a resource return ContactResource::collection($contacts); } public function store(Request $request) { $contact = $request->isMethod('put') ? Contact::findOrFail($request->contact_id) : new Contact; $contact->id = $request->input('contact_id'); $contact->first_name = $request->input('first_name'); $contact->last_name = $request->input('last_name'); $contact->email = $request->input('email'); $contact->phone = $request->input('phone'); if($contact->save()) { return new ContactResource($contact); } } public function show($id) { // Get contact $contact = Contact::findOrFail($id); // Return single contacte as a resource return new ContactResource($contact); } public function destroy($id) { // Get contact $contact = Contact::findOrFail($id); if($contact->delete()) { return new ContactResource($contact); } }
Route
// List contacts Route::get('contacts', '[email protected]'); // List single contact Route::get('contact/{id}', '[email protected]'); // Create new contact Route::post('contact', '[email protected]'); // Update contact Route::put('contact', '[email protected]'); // Delete contact Route::delete('contact/{id}', '[email protected]');
Templates
This demo uses a blade file as the global template, with two vue components included
resources/views/welcome.blade.php
<div id="app"> <div class="container"> <navbar></navbar> <h1>Contact List</h1> <contacts></contacts> </div>
Vue Components
resources/js/app.js
Vue.component('navbar', require('./components/Navbar.vue').default); Vue.component('contacts', require('./components/Contacts.vue').default);
resources/js/components/Navbar.vue
<template> <nav class="navbar navbar-expand-sm navbar-dark bg-info mb-2"> <div class="container"> <a href="#" class="navbar-brand">Laravel + Vue <b> CRUD</b></a> </div> </nav> </template>
resources/js/components/Contact.vue
<template> <div> <button class="btn btn-lg btn-success my-4 px-4" data-toggle="modal" data-target="#formModal" @click="clearForm()">Add New Contact</button> <nav aria-label="Page navigation example"> <ul class="pagination"> <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item"><a class="page-link" href="#" @click="fetchContacts(pagination.prev_page_url)">Previous</a></li> <li class="page-item disabled"><a class="page-link text-dark" href="#">Page {{ pagination.current_page }} of {{ pagination.last_page }}</a></li> <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item"><a class="page-link" href="#" @click="fetchContacts(pagination.next_page_url)">Next</a></li> </ul> </nav> <table class="table table-striped table-dark"> <thead> <tr> <th scope="col">First Name</th> <th scope="col">Last Name</th> <th scope="col">Email</th> <th scope="col">Phone</th> <th scope="col"></th> <th scope="col"></th> </tr> </thead> <tbody> <tr v-for="contact in contacts" v-bind:key="contact.id"> <td>{{ contact.first_name }}</td> <td>{{ contact.last_name }}</td> <td>{{ contact.email }}</td> <td>{{ contact.phone }}</td> <td><button @click="showEditContact(contact)" class="btn btn-warning mb-2" data-toggle="modal" data-target="#formModal">Edit</button></td> <td><button @click="deleteContact(contact.id)" class="btn btn-danger">Delete</button></td> </tr> </tbody> </table> <div class="modal fade" id="formModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog" > <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="formModalLabel">{{form_title}}</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <form @submit.prevent="editContact(contact)" class="mb-3"> <div class="modal-body"> <div class="form-group"> <input type="text" class="form-control" placeholder="First name" v-model="contact.first_name"> </div> <div class="form-group"> <input type="text" class="form-control" placeholder="Last name" v-model="contact.last_name"> </div> <div class="form-group"> <input type="email" class="form-control" placeholder="Email" v-model="contact.email"> </div> <div class="form-group"> <input type="text" class="form-control" placeholder="Phone" v-model="contact.phone"> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> <button type="submit" class="btn btn-primary" >Save</button> </div> </form> </div> </div> </div> </div> </div> </template> <script> export default { data() { return { contacts: [], contact: { id: '', firstname: '', last_name: '', email: '', phone: '', }, contact_id: '', pagination: {}, edit: false, form_title: 'Add New Contact', } }, created() { this.fetchContacts(); }, methods: { fetchContacts(page_url) { let vm = this; page_url = page_url || 'api/contacts'; axios.get(page_url) .then(res => { console.log(res); this.contacts = res.data.data; vm.makePagination(res.data.meta, res.data.links); }) .catch(err => console.log(err)); }, makePagination(meta, links) { let pagination = { current_page: meta.current_page, last_page: meta.last_page, next_page_url: links.next, prev_page_url: links.prev }; this.pagination = pagination; }, editContact() { // Add if(this.edit === false){ if(this.contact.first_name && this.contact.last_name && this.contact.email && this.contact.phone) { axios.post('api/contact', this.contact) .then((res) => { this.clearForm(); this.fetchContacts(); }).catch((err) => { console.log(err) }); } else { confirm('Please enter the data!'); } } // Update else { axios.put ('api/contact', this.contact) .then( res =>{ this.clearForm(); this.fetchContacts(); }) .catch( err => { console.log(err); }); } }, showEditContact (contact) { this.edit = true; this.form_title="Edit Contact"; this.contact.id = contact.id; this.contact.contact_id = contact.id; this.contact.first_name = contact.first_name; this.contact.last_name = contact.last_name; this.contact.email = contact.email; this.contact.phone = contact.phone; this.showModal = true; }, deleteContact(id) { if (confirm('Are You Sure?' + id)) { let data = new FormData(); data.append('_method', 'delete'); console.log(data); axios.post('api/contact/' + id, data) .then((res) => { this.fetchContacts(); }) .catch((err) => { console.log(err) }) } }, clearForm() { this.edit = false; this.form_title="Add New Contact"; this.contact.id = null; this.contact.contact_id = null; this.contact.first_name = ''; this.contact.last_name = ''; this.contact.email = ''; this.contact.phone = ''; } } }; </script>