Sebelum mulai praktek membuat SPA (Single Page Application), pastikan Anda telah membaca semua materi yang telah saya tulis di Belajar Laravel dan Belajar Vue.js. Contoh SPA dibuat dengan Lumen (micro-framework-nya Laravel) di sisi backend dan Vue.js di sisi frontend. Goal dari materi ini adalah memberi dasar pemahaman yang cukup tentang cara menambahkan data ke database lalu menampilkan hasilnya pada SPA. Agar lebih menarik, penggunaan query SQL saya ganti dengan Eloquent ORM.

Lumen di Sisi Backend Untuk REST API

Project Lumen (beserta database-nya) menggunakan instalasi dari contoh materi sebelumnya Menggunakan Eloquent ORM Lumen Laravel – source code project Lumen untuk latihan ini tersedia di Google Drive.

Langkah 1 – Siapkan Router

Buang semua router sebelumnya karena kita hanya akan membutuhkan dua router yaitu get('/') dan post('/store). Rubah isi route pada file web.php menjadi seperti berikut :

$router->group(['prefix' => 'sepakbola', 'middleware' => 'cors'], function () use ($router) {

	$router->get('/', 'SepakBolaController@index');
	$router->post('/store', 'SepakBolaController@store');

});

Langkah 2 – Tambahkan / Aktifkan CORS

CORS atau Cross-Origin Resource Sharing adalah sebuah mekanisme yang membuat browser dapat mengirim dan menerima data dari satu domain ke domain lainnya. Data yang dipertukarkan dapat berupa gambar, font, file dan lain-lain. Sederhananya, jika CORS ini tidak diaktifkan, maka REST API yang kita bangun tidak dapat diakses dari domain atau aplikasi lain.

CORS pada Lumen dapat diaktifkan melalui custom Middleware. Buat file bernama CorsMiddleware.php letakkan di dalam folder app/Http/Middleware/.

<?php 

namespace App\Http\Middleware;

use Closure;

class CorsMiddleware {

    public function handle($request, Closure $next) {
        
        $response = $next($request);

        $response->header('Access-Control-Allow-Origin', '*');

        return $response;
    }
}

Langkah 3 – Daftarkan CORS Middleware ke Bootstrap Lumen

Agar CORS dapat diakses dari router 'middleware' => 'cors', Middleware harus didaftarkan ke bootstrap Lumen, caranya buka file app.php di folder bootstrap/, tambahkan baris kode berikut :

$app->routeMiddleware([
    'cors' => App\Http\Middleware\CorsMiddleware::class
]);

Langkah 4 – Buat Controller Untuk Membaca dan Menambahkan Data

Buang semua method sebelumnya yang tidak butuhkan, kita hanya akan menggunakan dua method yaitu index dan store. Rubah isi file SepakBolaController.php menjadi seperti di bawah :

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Models\SepakBola;

class SepakBolaController extends Controller {

    public function index() {

        $results = SepakBola::orderBy('id', 'DESC')->get();

        $json = json_encode($results);

        return $json;

    }

    public function store(Request $request) {

        $klub = $request->input('klub');
        $pemain = ucwords($request->input('pemain'));

        $createSepakBola = new SepakBola();

        $createSepakBola->klub = $klub;
        $createSepakBola->pemain = $pemain;
        
        $createSepakBola->save();

        $data = ['pesan' => $pemain . ' telah ditambahkan.'];

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

    }

}

Perhatikan, pada method index, perintah Eloquent :

SepakBola::orderBy('id', 'DESC')->get();

Akan memberikan hasil yang sama jika menggunakan query SQL :

SELECT * FROM variancode.sepak_bola ORDER BY `id` DESC;

Pada method store, penyimpanan data ke tabel database sepak_bola juga dilakukan dengan Eloquent ORM alias tanpa query SQL konvensional :

$createSepakBola = new SepakBola();

$createSepakBola->klub = $klub;
$createSepakBola->pemain = $pemain;

$createSepakBola->save();

Perintah Eloquent di atas ekivalen dengan query SQL :

INSERT INTO sepak_bola (klub, pemain) VALUES ($klub, $pemain);

Nilai yang akan disimpan ke kolom klub dan pemain tabel database sepak_bola diperoleh dari request :

$klub = $request->input('klub');
$pemain = ucwords($request->input('pemain'));

Vue.js di Sisi Frontend Untuk Tampilan

Silahkan buat project Vue.js baru terlebih dahulu di sembarang direktori dengan menjalankan perintah Vue-CLI berikut (pemilihan nama project bebas) – source code project Vue.js untuk latihan ini tersedia di Google Drive.

vue create belajarspa

Langkah 1 – Install Plugin Axios

Axios adalah plugin berbasis HTTP Client yang digunakan oleh Vue untuk mengambil data dari sisi backend Lumen. Instalasi Axios dilakukan pada root project Vue dengan perintah npm di bawah :

npm install axios --save
membuat single page application dengan vue.js dan lumen

Langkah 2 – Import CSS Bootstrap 4

Unduh file bootstrap.min.css dari CDN https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css, lalu letakkan di dalam folder src/assets/.

membuat single page application dengan vue.js dan lumen

Agar CSS Bootstrap 4 dapat digunakan, file bootstrap.min.css harus di-import dari file main.js.

import Vue from "vue";
import App from "./App.vue";
import router from "./router";

import "@/assets/bootstrap.min.css";

Vue.config.productionTip = true;

new Vue({
  router,
  render: h => h(App)
}).$mount("#app");

Langkah 3 – Siapkan Router

Buang router bawaan instalasi Vue-CLI yang tidak dibutuhkan pada file index.js – selain path: "/".

import Vue from "vue";
import VueRouter from "vue-router";
import Home from "@/views/Home.vue";

Vue.use(VueRouter);

const routes = [
  {
    path: "/",
    name: "Home",
    component: Home
  }
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes
});

export default router;

Langkah 4 – Modifikasi File Home.vue

Copy paste kode berikut untuk menggantikan isi file Home.vue bawaan instalasi Vue-CLI.

<template>
  <div class="home">
    <div>
      <div class="jumbotron text-center">
        <h1>Membuat SPA Sederhana</h1>
        <p>Menggunakan Lumen (sisi Backend) dan Axios Vue.js (sisi Frontend)</p>
      </div>
      <div class="container">
        <div class="row">
          <div class="col-md-6">
            <form @submit.prevent="onSubmit()">
              <div class="form-group">
                <label for="klub">Klub</label>
                <select class="form-control" v-model="klub">
                  <option
                    v-for="(klubBola, index) in arrayKlubBola"
                    :key="index"
                    >{{ klubBola }}</option
                  >
                </select>
              </div>
              <div class="form-group">
                <label for="pemain">Pemain</label>
                <input type="text" class="form-control" v-model="pemain" />
              </div>
              <button type="submit" class="btn btn-primary">
                Tambah Pemain
              </button>
            </form>
          </div>
          <div class="col-md-6">
            <list-pemain-bola :arrayPemainBola="arrayPemainBola" />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import axios from "axios";

import ListPemainBola from "@/components/ListPemainBola.vue";

export default {
  name: "Home",
  components: {
    ListPemainBola
  },
  data() {
    return {
      klub: "",
      pemain: "",
      arrayKlubBola: [],
      arrayPemainBola: []
    };
  },
  created() {
    this.getArrayPemainBola();
  },
  methods: {
    distinctArray(value, index, self) {
      return self.indexOf(value) === index;
    },
    getArrayKlubBola() {
      this.arrayPemainBola.forEach(element => {
        this.arrayKlubBola.push(element.klub);
      });
      this.arrayKlubBola = this.arrayKlubBola.filter(this.distinctArray);
      this.arrayKlubBola.sort();
      this.klub = this.arrayKlubBola[0];
    },
    getArrayPemainBola() {
      const options = {
        url: "http://localhost/sepakbola",
        method: "get"
      };
      axios(options).then(response => {
        this.arrayPemainBola = response.data;
        this.getArrayKlubBola();
      });
    },
    onSubmit() {
      if (this.pemain.trim()) {
        let formData = new FormData();
        formData.append("klub", this.klub);
        formData.append("pemain", this.pemain.trim());

        const options = {
          url: "http://localhost/sepakbola/store",
          method: "post",
          headers: {
            "Content-Type": "multipart/form-data"
          },
          data: formData
        };

        axios(options)
          .then(() => {
            this.getArrayPemainBola();
          })
          .catch(errors => {
            alert(errors);
          });

        this.pemain = "";
      }
    }
  }
};
</script>

Tahapan membaca alur kode file Home.vue di atas :

  1. Ketika Vue memasuki lifecycle created(), method this.getArrayPemainBola() adalah yang paling pertama dieksekusi.
  2. Di dalam method getArrayPemainBola, Axios mengambil data dari URL http://localhost/sepakbola dengan HTTP Request GET. Data json dari backend kemudian di-set sebagai nilai untuk arrayPemainBola melalui baris this.arrayPemainBola = response.data.
  3. Masih di method getArrayPemainBola, pemanggilan method lainnya this.getArrayKlubBola() dilakukan untuk mem-filter nilai kolom klub yang ada di arrayPemainBola. Method distinctArray digunakan untuk menyingkirkan kolom klub dengan nilai yang sama pada arrayKlubBola, agar AC Milan, Juventus dan Inter Milan masing-masing hanya mengisi satu index array saja.
  4. arrayPemainBola akan di-render oleh komponen ListPemainBola melalui tag <list-pemain-bola>, sedangkan arrayKlubBola di-render dengan directive v-for di dalam elemen HTML Select Option.
  5. Method onSubmit dipanggil setiap kali button Tambah Pemain di-klik. Di dalam method ini, Axios akan mengirim data ke URL http://localhost/sepakbola/store menggunakan HTTP Request POST. Sebelum dikirim, property klub dan pemain dibungkus / append ke dalam objek FormData. Option headers pada HTTP Request POST tidak boleh kosong, isi dengan nilai "Content-Type": "multipart/form-data"
  6. Jika penambahan data pemain gagal dilakukan (karena duplikasi data / ada pemain yang sama), Axios akan masuk ke blok .catch(errors), pesan error ditampilkan melalui alert(errors).

Langkah 5 – Jalankan Aplikasi Vue

Pada root project Vue, ketik perintah berikut untuk menjalankan aplikasi Vue :

npm run serve
membuat single page application dengan vue.js dan lumen

Terakhir, ketikkan URL http://localhost:8080/ pada browser dan akan diperoleh hasil seperti gambar di bawah :

membuat single page application dengan vue.js dan lumen