Aplikasi Laundry (Laravel 5.8 - Vue.js - SPA) #5: Manage Laundry Products

Aplikasi Laundry (Laravel 5.8 - Vue.js - SPA) #5: Manage Laundry Products

Pendahuluan

Usaha laundry tak hanya mencakup satu bagian saja yakni laundry kiloan, akan tetapi telah memiliki banyak variant diantaranya, laundry jaket, selimut dan berbagai macam yang bersifat satu. Tidak cukup sampai disitu, ada juga variant dengan hanya cuci bersih, tapi tidak sepaket dengan setrika dan lain sebagainya.

Pada module ini kita akan membuat fitur yang akan menampung products item yang dimiliki oleh usaha laundry tersebut. Metode yang digunakan sama dengan module sebelumnya, hanya beberapa bagian saja yang mengalami perbedaan. Pada bagian ini kita akan bekerja dengan dua buah table sekaligus untuk menyimpan data.

Baca Juga:Tips Optimasi Meningkatkan Performance Laravel

Manage Product Lists

Bagian pertama yang akan dikerjakan adalah fitur untuk menampilkan data product yang ada, pada bagian ini tentu saja hanya bermain dengan fungsi untuk mengambil data dari table terkait. Pertama, kita selesaikan API-nya terlebih dahulu, pada command line jalankan perintah:

php artisan make:controller API/ProductController

Kemudian buka file controller yang telah dibuat dan tambahkan method index()

public function index()
{
    $products = LaundryPrice::with(['type', 'user'])->orderBy('created_at', 'DESC');
    if (request()->q != '') {
        $products = $products->where('name', 'LIKE', '%' . request()->q . '%');
    }
    $products = $products->paginate(10);
    return new ProductCollection($products);
}

Note: Penjelasannya sama dengan artikel sebelumnya, dimana terdapat dua buah relasi dari eager loading yang digunakan, yakni: type dan user.

Buat masing masing relasi diatas, buka file model app/LaundryPrice.php dan tambahkan method:

protected $guarded = []; //SEKALIAN UNTUK MODULE BERIKUTNYA

public function type()
{
    return $this->belongsTo(LaundryType::class, 'laundry_type_id', 'id');
}

public function user()
{
    return $this->belongsTo(User::class);
}

Terdapat sebuah API resource, untuk men-generate-nya, pada command line jalankan perintah:

php artisan make:resource ProductCollection

Buka file app/Http/Resources/ProductCollection dan modifikasi menjadi:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class ProductCollection extends ResourceCollection
{
    /**
     * Transform the resource collection into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'data' => $this->collection
        ];
    }
}

Jangan lupa tambahkan use statement pada ProductController

use App\LaundryPrice;
use App\Http\Resources\ProductCollection;

Definisikan route API-nya agar dapat diakses, buka file routes/api.php dan tambahkan:

Route::resource('product', 'API\ProductController')->except(['create', 'show']);

Dari sisi server sudah selesai, saatnya berpindah pada sisi client, pertama buat store-nya terlebih dahulu. Buat file product.js di dalam folder resources/js/stores dan tambahkan code berikut:

import $axios from '../api.js'

const state = () => ({
    products: [], //STATE YANG AKAN MENYIMPAN DATA PRODUCT
    page: 1
})

const mutations = {
    //MUTATION UNTUK MEMASUKKAN DATA KE DALAM STATE PRODUCTS
    ASSIGN_DATA(state, payload) {
        state.products = payload
    },
    //MUTATION UNTUK MENGUBAH VALUE DARI STATE PAGE
    SET_PAGE(state, payload) {
        state.page = payload
    }
}

const actions = {
    //FUNGSI UNTUK MENGAMBIL DATA KE SERVER
    getProducts({ commit, state }, payload) {
        let search = typeof payload != 'undefined' ? payload:''
        return new Promise((resolve, reject) => {
            //MENGGUNAKAN AXIOS PADA URL /PRODUCT
            $axios.get(`/product?page=${state.page}&q=${search}`)
            .then((response) => {
                //APABILA MENDAPATKAN RESPONSE, DATA AKAN DICOMMIT KE MUTATION ASSIGN_DATA
                commit('ASSIGN_DATA', response.data)
                resolve(response.data)
            })
        })
    }
}

export default {
    namespaced: true,
    state,
    actions,
    mutations
}

Setelah itu, daftarkan module store diatas pada store.js, buka file store.js dan tambahkan code berikut:

import product from './stores/product.js'

Pada bagian modules, tambahkan product

//[.. CODE SEBELUMNYA ..]

modules: {
    auth,
    outlet,
    courier,
    product //TAMBAHKAN LINE INI
},
  
//[.. CODE SETELAHNYA ..]

Tugas selanjutnya adalah membuat routing dari sisi Vue.js, buka file router.js dan tambahkan code ini pada bagian import:

import IndexProduct from './pages/products/Index.vue'
import DataProduct from './pages/products/Product.vue'

Masih dengan file yang sama, tambahkan line code berikut ini pada bagian routes

//[.. CODE SEBELUMNYA ..]
mode: 'history',
routes: [
	{
      path: '/product',
      component: IndexProduct,
      meta: { requiresAuth: true },
      children: [
          {
              path: '',
              name: 'products.data',
              component: DataProduct,
              meta: { title: 'Manage Products' }
          }
      ]
  }
]

//[.. CODE SETELAHNYA ..]

Routing diatas membutuhkan dua buah file, yakni Index.vue dan Product.vue yang diletakkan di dalam folder resources/js/pages/products. Buat file Index.vue terlebih dahulu dan tambahkan code:

<template>
    <div class="container">
        <section class="content-header">
            <h1>
                Manage Products
            </h1>
            <breadcrumb></breadcrumb>
        </section>

        <section class="content">
            <div class="row">
                <router-view></router-view>
            </div>
        </section>
    </div>
</template>
<script>
    import Breadcrumb from '../../components/Breadcrumb.vue'
    export default {
        name: 'IndexProduct',
        components: {
            'breadcrumb': Breadcrumb
        }
    }
</script>

Note: Pastikan file ini berada di dalam folder /pages/products.

Kemudian buat file Product.vue dengan folder yang sama dan tambahkan code:

<template>
    <div class="col-md-12">
        <div class="panel">
            <div class="panel-heading">
                <router-link :to="{ name: 'products.add' }" class="btn btn-primary btn-sm btn-flat">Tambah</router-link>
                <div class="pull-right">
                    <input type="text" class="form-control" placeholder="Cari..." v-model="search">
                </div>
            </div>
            <div class="panel-body">
                <b-table striped hover bordered :items="products.data" :fields="fields" show-empty>
                    <template slot="laundry_type" slot-scope="row">
                        {{ row.item.type.name }}
                    </template>
                    <template slot="user_id" slot-scope="row">
                        {{ row.item.user.name }}
                    </template>
                    <template slot="actions" slot-scope="row">
                        <router-link :to="{ name: 'products.edit', params: {id: row.item.id} }" class="btn btn-warning btn-sm"><i class="fa fa-pencil"></i></router-link>
                        <button class="btn btn-danger btn-sm" @click="deleteProduct(row.item.id)"><i class="fa fa-trash"></i></button>
                    </template>
                </b-table>

                <div class="row">
                    <div class="col-md-6">
                        <p v-if="products.data"><i class="fa fa-bars"></i> {{ products.data.length }} item dari {{ products.meta.total }} total data</p>
                    </div>
                    <div class="col-md-6">
                        <div class="pull-right">
                            <b-pagination
                                v-model="page"
                                :total-rows="products.meta.total"
                                :per-page="products.meta.per_page"
                                aria-controls="products"
                                v-if="products.data && products.data.length > 0"
                                ></b-pagination>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import { mapActions, mapState } from 'vuex'

export default {
    name: 'DataCourier',
    created() {
        this.getProducts() //MELAKUKAN REQUEST KETIKA COMPONENT DI-LOAD
    },
    data() {
        return {
            //FIELDS UNTUK MENGISI HEADER TABLE YANG AKAN DITAMPILKAN
            fields: [
                { key: 'name', label: 'Nama Item' },
                { key: 'unit_type', label: 'Tipe' },
                { key: 'laundry_type', label: 'Jenis Jasa' },
                { key: 'price', label: 'Harga' },
                { key: 'user_id', label: 'Admin' },
                { key: 'actions', label: 'Aksi' }
            ],
            //VARIABLE UNTUK FORM SEARCH
            search: ''
        }
    },
    computed: {
        //ME-LOAD STATE DARI MODULE PRODUCTS
        ...mapState('product', {
            products: state => state.products, //STATE PRODUCTS
        }),
        page: {
            get() {
                return this.$store.state.product.page //LOAD STATE PAGE
            },
            set(val) {
                this.$store.commit('product/SET_PAGE', val) //SET STATE PAGE KETIKA VALUE BERUBAH
            }
        }
    },
    watch: {
        //KETIKA TERJADI PERUBAHAN VALUE DARI PAGE
        page() {
            this.getProducts() //AMBIL DATA TERBARU
        },
        //KETIKA TERJADI PERUBAHAN VALUE DARI SEARCH
        search() {
            this.getProducts(this.search) //AMBIL DATA TERBARU BERDASARKAN VALUE SEARC
        }
    },
    methods: {
      ...mapActions('product', ['getProducts', 'removeProduct']), //LOAD ACTIONS DARI MODULE PRODUCT
        //FUNGSI UNTUK MENG-HANDLE TOMBOL HAPUS PRODUCT
        deleteProduct(id) {
            this.$swal({
                title: 'Kamu Yakin?',
                text: "Tindakan ini akan menghapus secara permanent!",
                type: 'warning',
                showCancelButton: true,
                confirmButtonColor: '#3085d6',
                cancelButtonColor: '#d33',
                confirmButtonText: 'Iya, Lanjutkan!'
            }).then((result) => {
                if (result.value) {
                    this.removeProduct(id) //KETIKA YES MAKA FUNGSI INI AKAN DIJALANKAN
                }
            })
        }
    }
}
</script>

Agar halaman diatas bisa di akses melalui menu navigasi, buka file Header.vue yang berada di dalam folder resources/js/components tambahkan tag:

<li><router-link :to="{ name: 'products.data' }">Products</router-link></li>

Adapun hasil yang akan kita peroleh akan tampak seperti dibawah ini

aplikasi laundry laravel 5.8

Fitur Add Product

Melangkah pada fitur selanjutnya adalah untuk menambahkan data product ke dalam database, pertama buat dulu routing-nya, maka buka file router.js dan tambahkan line berikut pada bagian import statement:

import AddProduct from './pages/products/Add.vue'

Kemudian pada bagian routes, tambahkan line code berikut tepat di dalam children untuk routing products

{
    path: 'add',
    name: 'products.add',
    component: AddProduct,
    meta: { title: 'Add New Product' }
},

Selanjutnya, buat file Add.vue di dalam folder /pages/products dan tambahkan code:

<template>
    <div class="col-md-12">
        <div class="panel">
            <div class="panel-heading">
                <h3 class="panel-title">Add New Product</h3>
            </div>
            <div class="panel-body">
                <product-form ref="formProduct"></product-form>
                <div class="form-group">
                    <button class="btn btn-primary btn-sm btn-flat" @click.prevent="submit">
                        <i class="fa fa-save"></i> Add New
                    </button>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
    import { mapActions, mapState, mapMutations } from 'vuex'
    import FormProduct from './Form.vue' //MENGIMPORT FILE Form.vue
    export default {
        name: 'AddProduct',
        methods: {
            //KETIKA TOMBOL ADD NEW DITEKAN, MAKA FUNGSI INI AKAN BERJALAN
            submit() {
                //KEMUDIAN AKAN MENJALANKAN FUNGSI SUBMIT() PADA FILE FORM.VUE MENGGUNAKAN $REFS
                this.$refs.formProduct.submit() 
            }
        },
        components: {
            'product-form': FormProduct
        }
    }
</script>

Karena file di atas membutuhkan file Form.vue, maka buat file tersebut di dalam folder yang sama dan tambahkan code:

<template>
    <div>
        <div class="form-group" :class="{ 'has-error': errors.name }">
            <label for="">Nama Item</label>
            <input type="text" class="form-control" v-model="product.name" placeholder="Kemeja">
            <p class="text-danger" v-if="errors.name">{{ errors.name[0] }}</p>
        </div>
        <div class="form-group" :class="{ 'has-error': errors.unit_type }">
            <label for="">Tipe</label>
            <select v-model="product.unit_type" class="form-control">
                <option value="">Pilih</option>
                <option value="Kilogram">Kilogram</option>
                <option value="Potong">Potong</option>
            </select>
            <p class="text-danger" v-if="errors.unit_type">{{ errors.unit_type[0] }}</p>
        </div>
        <div class="row">
            <div class="col-md-6">
              
              	<!-- KETIKA TOMBOL ADD NEW DITEKAN -->
                <div class="form-group" :class="{ 'has-error': errors.laundry_type }">
                    <label for="">Jenis Jasa <sup><a @click="showForm = true" href="javascript:void(0)" v-if="!showForm">Add New</a></sup></label>
                    <select v-model="product.laundry_type" class="form-control">
                        <option value="">Pilih</option>
                        <option v-for="(row, index) in laundry_types" :key="index" :value="row.id">{{ row.name }}</option>
                    </select>
                    <p class="text-danger" v-if="errors.laundry_type">{{ errors.laundry_type[0] }}</p>
                </div>
            </div>
          
          	<!-- MAKA FORM UNTUK MENAMBAHKAN JENIS LAUNDRY AKAN DITAMPILKAN -->
            <div class="col-md-6" v-if="showForm">
                <div class="form-group" :class="{ 'has-error': errors.name_laundry_type }">
                    <label for="">&nbsp;</label>
                    <div class="input-group">
                        <input type="text" placeholder="Cuci Kering + Setrika" v-model="laundry_type" class="form-control">
                        <a href="javascript:void(0)" class="input-group-addon btn btn-warning btn-sm" id="basic-addon2" @click="addNewLaundryType">Save</a>
                    </div>
                    <p class="text-danger" v-if="errors.name_laundry_type">{{ errors.name_laundry_type[0] }}</p>
                </div>
            </div>
          	<!-- END FORM ADD JENIS LAUNDRY -->
          
        </div>
        
        <div class="form-group" :class="{ 'has-error': errors.price }">
            <label for="">Harga</label>
            <input type="number" class="form-control" v-model="product.price">
            <p class="text-danger" v-if="errors.price">{{ errors.price[0] }}</p>
        </div>
    </div>
</template>

<script>
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
    name: 'FormCourier',
    created() {
        this.getLaundryType() //KETIKA COMPONENT DI-LOAD MAKA METHOD INI AKAN DIJALANKAN
        //APABILA MENGAKSESNYA DARI ROUTE EDIT
        if (this.$route.name == 'products.edit') {
            //MAKA FUNGSI UNTUK MENGAMBIL DATA PRODUCT BERDASARKAN ID AKAN DIJALANKAN
            this.editProduct(this.$route.params.id).then((res) => {
                //KEMUDIAN VARIABLE YANG ADA DI ISI DENGAN DATA DARI SERVER
                this.product = {
                    name: res.data.name,
                    unit_type: res.data.unit_type,
                    price: res.data.price,
                    laundry_type: res.data.laundry_type_id
                }
            })
        }
    },
    data() {
        return {
            //DEFINISIKAM VARIABLE
            product: {
                name: '',
                unit_type: '',
                price: '',
                laundry_type: ''
            },
            laundry_type: '',
            showForm: false //DEFAULT FORM UNTUK MENAMBAHKAN JENIS LAUNDRY ADALAH FALSE, YANG BERARTI FORM TIDAK DITAMPILKAN
        }
    },
    computed: {
        ...mapState(['errors']), //MEGAMBIL STATE ERROS
        ...mapState('product', {
            laundry_types: state => state.laundry_types //MENGAMBIL STATE LAUNDRY_TYPES
        })
    },
    methods: {
        ...mapActions('product', ['getLaundryType', 'addLaundryType', 'addProductLaundry', 'editProduct', 'updateCourier']), //ME-LOAD SEMUA FUNGSI YANG ADA DI MODULE PRODUCT
        
        //FUNGSI YANG AKAN BERJALAN KETIKA TOMBOL SAVE DARI ADD JENIS LAUNDRY DITEKAN
        addNewLaundryType() {
            //MENGIRIMKAN PERMINTAAN KE SERVER UNTUK DISIMPAN
            this.addLaundryType({ name_laundry_type: this.laundry_type }).then(() => {
                //MENGAMBIL DATA TERBARU DARI SERVER
                this.getLaundryType().then(() => {
                    //FORM DI SET FALSE KEMBALI
                    this.showForm = false
                    this.laundry_type = '' //DAN LAUNDRY_TYPE DI KOSONGKAN
                })
            })
        },
        //KETIKA TOMBOL ADD NEW DARI FILE ADD.VUE DI TEKAN, MAKA FUNGSI INI AKAN DIJALANKAN
        submit() {
            //APABILA DIAKSESNYA DARI ROUTE DENGAN NAME PRODUCTS.ADD
            if (this.$route.name == 'products.add') {
                //MAKA FUNGSI INI AKAN DIJALANKAN UNTUK MENAMBAHKAN PRODUCT BARU
                this.addProductLaundry(this.product).then(() => {
                    //KOSONGKAN VARIABLE KETIKA BERHASIL MENYIMPAN DATA KE SERVER
                    this.product = {
                        name: '',
                        unit_type: '',
                        price: '',
                        laundry_type: ''
                    }
                    //REDIRECT KEMBALI KE HALAMAN LIST PRODUCT
                    this.$router.push({ name: 'products.data' })
                })
            //KETIKA DIAKSES MELALUI ROUTE DENGAN NAME PRODUCTS.EDIT
            } else if (this.$route.name == 'products.edit') {
                //MAKA ID AKAN DI TAMBAHKAN KE DALAM OBJECT VARIABLE PRODUCT
                Object.assign(this.product, { id: this.$route.params.id })
                //KIRIM PERMINTAAN KE SERVER UNTUK MENGUBAH DATA
                this.updateCourier(this.product).then(() => {
                    //KOSONGKAN VARIABLE
                    this.product = {
                        name: '',
                        unit_type: '',
                        price: '',
                        laundry_type: ''
                    }
                    //REDIRECT KEMBALI
                    this.$router.push({ name: 'products.data' })
                })
            }
        }
    }
}
</script>

Note: Karena file Form.vue di gunakan oleh Edit dan Add, maka fungsi diatas dibuat sekaligus agar reusable.

Hal selanjutnya yang akan dilakukan adalah melengkapi store dari module product. Buka file product.js dan tambahkan code dibawah ini pada bagian actions.

//[.. CODE SEBELUMNYA ..]

getLaundryType({ commit }) {
    return new Promise((resolve, reject) => {
        //MENGIRIM REQUEST UNTUK MENGAMBIL DATA LAUNDRY TYPE
        $axios.get(`/product/laundry-type`)
        .then((response) => {
            commit('ASSIGN_LAUNDRY_TYPE', response.data.data) //COMMIT DATA KE MUTATIONS ASSIGN_LAUNDRY_TYPE
            resolve(response.data)
        })
    })
},
addLaundryType({ commit }, payload) {
    return new Promise((resolve, reject) => {
        //MENGIRIM REQUEST UNTUK MENYIMPAN LAUNDRY_TYPE YANG BARU
        $axios.post(`/product/laundry-type`, payload)
        .then((response) => {
            resolve(response.data)
        })
        .catch((error) => {
            //JIKA TERJADI ERROR VALIDASI, MAKA ERRORNYA DISIMPAN KE DALAM VARIABLE ERRORS
            if (error.response.status == 422) {
                commit('SET_ERRORS', error.response.data.errors, { root: true })
            }
        })
    })
},
addProductLaundry({ commit }, payload) {
    return new Promise((resolve, reject) => {
        //MENGIRIM REQUEST UNTUK MENAMBAHKAN DATA PRODUCT BARU
        $axios.post(`/product`, payload)
        .then((response) => {
            resolve(response.data)
        })
        .catch((error) => {
            //SET ERROR VALIDASI
            if (error.response.status == 422) {
                commit('SET_ERRORS', error.response.data.errors, { root: true })
            }
        })
    })
},
editProduct({ commit }, payload) {
    return new Promise((resolve, reject) => {
        //MENGIRIM REQUEST UNTUK MENGAMBIL DATA PRODUCT BERDASARKAN ID
        $axios.get(`/product/${payload}/edit`)
        .then((response) => {
            resolve(response.data)
        })
    })
},
updateCourier({ commit }, payload) {
    return new Promise((resolve, reject) => {
        //MENGIRIM REQUEST UNTUK MENGUBAH DATA PRODUCT BERDASARKAN ID
        $axios.put(`/product/${payload.id}`, payload)
        .then((response) => {
            resolve(response.data)
        })
        .catch((error) => {
            //SET ERROR VALIDASI
            if (error.response.status == 422) {
                commit('SET_ERRORS', error.response.data.errors, { root: true })
            }
        })
    })
},
removeProduct({ dispatch }, payload) {
    return new Promise((resolve, reject) => {
        //MENGIRIM PERMINTAAN UNTUK MENGHAPUS DATA PRODUCT BERDASARKAN ID
        $axios.delete(`/product/${payload}`)
        .then((response) => {
            dispatch('getProducts').then(() => resolve(response.data))
        })
    })
}

Masih di dalam file yang sama, tambahkan code dibawah ini ke dalam bagian mutations

ASSIGN_LAUNDRY_TYPE(state, payload) {
    state.laundry_types = payload
},

Terakhir, pada file yang sama tambahkan code berikut pada bagian state

laundry_types: [],

Berpindah pada sisi server, buat API untuk meng-handle beberapa permintaan yang dilakukan diatas. Buka file ProductController.php dan tambahkan code:

public function getLaundryType()
{
    $type = LaundryType::orderBy('name', 'ASC')->get(); //GET DATA LAUNDRY TYPE DENGAN DI URUTKAN BERDASARKAN NAMA
    return response()->json(['status' => 'success', 'data' => $type]);
}

public function storeLaundryType(Request $request)
{
    //VALIDASI DATA YANG DIKIRIM
    $this->validate($request, [
        'name_laundry_type' => 'required|unique:laundry_types,name'
    ]);

    //SIMPAN DATA BARU KE DALAM TABLE LAUNDRY_TYPES
    LaundryType::create(['name' => $request->name_laundry_type]);
    return response()->json(['status' => 'success']);
}

public function store(Request $request)
{
    //VALIDASI DATA YANG DIKIRIM
    $this->validate($request, [
        'name' => 'required|string|max:100',
        'unit_type' => 'required',
        'price' => 'required|integer',
        'laundry_type' => 'required'
    ]);

    try {
        //SIMPAN DATA PRODUCT KE DALAM TABLE laundry_prices
        LaundryPrice::create([
            'name' => $request->name,
            'unit_type' => $request->unit_type,
            'laundry_type_id' => $request->laundry_type,
            'price' => $request->price,
            'user_id' => auth()->user()->id
        ]);
        return response()->json(['status' => 'success']);
    } catch (\Exception $e) {
        return response()->json(['status' => 'failed']);
    }
}

Jangan lupa tambahkan use statement:

use App\LaundryType;

Juga jangan lupa untuk menambahkan code dibawah ini di dalam file app/LaundryType.php agar eloquent mengizinkan untuk melakukan mass assignment:

protected $guarded = [];

Terakhir adalah membuat routing untuk laundry_types, buka file routes/api.php dan tambahkan code:

Route::get('/product/laundry-type', 'API\ProductController@getLaundryType');
Route::post('/product/laundry-type', 'API\ProductController@storeLaundryType');

Fitur Edit Product

Pada fitur ini pekerjaan kita menjadi lebih mudah karena hanya mendefinisikan routing dan membuat API-nya saja. Hal ini disebabkan karena fungsi pada sisi client sudah dibuat sebelumnya pada module diatas. Kembali buka file router.js dan tambahkan code ini pada bagian import statement:

import EditProduct from './pages/products/Edit.vue'

Masih dengan file yang sama, tambahkan routing ini pada bagian children products:

{
    path: 'edit/:id',
    name: 'products.edit',
    component: EditProduct,
    meta: { title: 'Edit Product' }
},

Selanjutnya, buat file Edit.vue di dalam folder resources/js/pages/products dan tambahkan code:

<template>
    <div class="col-md-12">
        <div class="panel">
            <div class="panel-heading">
                <h3 class="panel-title">Edit Product</h3>
            </div>
            <div class="panel-body">
                <product-form ref="formProduct"></product-form>
                <div class="form-group">
                    <button class="btn btn-primary btn-sm btn-flat" @click.prevent="submit">
                        <i class="fa fa-save"></i> Update
                    </button>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
    import { mapActions, mapState } from 'vuex'
    import FormProduct from './Form.vue'
    export default {
        name: 'EditProduct',
        methods: {
            //KETIKA TOMBOL UPDATE DITEKAN
            submit() {
                //MAKA AKAN MENJALAN FUNGSI SUBMIT YANG BERADA DI DALAM FILE FORM.VUE MENGGUNAKAN $REFS
                this.$refs.formProduct.submit()
            }
        },
        components: {
            'product-form': FormProduct
        },
    }
</script>

Pada sisi server, buat API untuk mengambil data tunggal dan meng-update-nya. Buka file ProductController.php dan tambahkan method:

public function edit($id)
{
    $laundry = LaundryPrice::find($id); //MENGAMBIL DATA BERDASARKAN ID
    return response()->json(['status' => 'success', 'data' => $laundry]);
}

public function update(Request $request, $id)
{
    $laundry = LaundryPrice::find($id); //MENGAMBILD ATA BERDASARKAN ID
    //KEMUDIAN MENG-UPDATE DATA TERSEBUT
    $laundry->update([
        'name' => $request->name,
        'unit_type' => $request->unit_type,
        'laundry_type_id' => $request->laundry_type,
        'price' => $request->price,
    ]);
    return response()->json(['status' => 'success']);
}

Fitur Delete Product

Hapus data product juga menjadi lebih mudah karena fungsinya sudah dibuat pada bagian "Manage Product List" dan dilengkapi store-nya pada bagian "Fitur Add Product". Tugas kita hanyalah membuat API-nya saja untuk mengerjakan request yang diterima. Buka file ProductController.php dan tambahkan code:

public function destroy($id)
{
    $laundry = LaundryPrice::find($id);
    $laundry->delete();
    return response()->json(['status' => 'success']);
}

Baca Juga:Aplikas Laundry (Laravel 5.8 - Vue.js - SPA) #4: Management Couriers

Kesimpulan

Melengkapi data master yang dimiliki pada serial Membuat Aplikasi Laundry menggunakan Laravel 5.8 dan Vue.js, dimana kita telah membuat fitur untuk mengelola data product. Teknik yang digunakan serupa dengan serial sebelumnya karena hanya bermain dengan CRUD. Dengan banyaknya fungsi CRUD seharusnya sudah sangat membantu teman-teman dalam memahami Laravel dan menghubungkannya dengan Vue.js karena kegiatan yang sama telah dilakukan berulang kali.

Adapun dokumentasi code dari serial ini dapat ditemukan pada halaman Github.

Category:
Share:

Comments