Membuat Aplikasi Invoice Laravel 5.7 #2: Manajemen Produk

Membuat Aplikasi Invoice Laravel 5.7 #2: Manajemen Produk

Pendahuluan

Melanjutkan seri belajar Laravel dengan case Membuat aplikasi invoice, dimana pada seri sebelumnya kita telah membuat struktur database yang akan digunakan. Pada seri ini, target yang akan dicapai adalah bagaimana membuat module Manajemen Produk untuk mengelola data produk dengan 4 buah fungsi yang sudah umum kita kenal dengan istilah CRUD (Create Read Update Delete).

Karena ini merupakan serial belajar Laravel basic, maka kita akan membahasnya satu persatu secara detail bagaimana setiap fungsi tersebut bekerja, sehingga nantinya dapat kamu jadikan modal dasar untuk menyelesaikan sebuah aplikasi yang kamu inginkan.

Baca Juga: Membuat Aplikasi Invoice Laravel 5.7 #1: Generate Database

Read Data Produk

Fungsi pertama yang akan kita bahas adalah bagaimana menampilkan data dari database agar dapat dilihat oleh user yang sedang menggunakan aplikasi tersebut. Schema-nya adalah kumpulan data dari table products akan ditampilkan kedalam list table, sehingga ada 3 hal yang berperan, yakni: Controller, Model dan View.

Controller berperan untuk menghubunkan antara Model dan View, Model berperan untuk berinteraksi dengan database, sedangkan View berperan untuk melakukan delivery untuk menampilkan data yang ada. Langsung saja kita praktekkan dalam bentuk coding-an, buka terminal / command prompt (cmd), lalu jalankan command berikut untuk men-generate controller:

php artisan make:controller ProductController

Penjelasan: Command diatas akan menghasilkan sebuah file dengan nama ProductController.php dan terletak didalam folder app/Http/Controllers.

Buka file ProductController.php, kemudian modifikasi menjadi:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Product; // 1

class ProductController extends Controller
{
    public function index()
    {
        $products = Product::orderBy('created_at', 'DESC')->get(); // 2
        // CODE DIATAS SAMA DENGAN > select * from `products` order by `created_at` desc 
        return view('products.index', compact('products')); // 3
    }
}

Penjelasan: Line-1, berfungsi untuk me-load model Product sehingga kedepannya ketika model tersebut digunakan kita tidak perlu lagi menuliskan path lengkapnya, dalam hal ini App\Product, penggunaannya cukup dengan menuliskan Product. Line-2, adalah query ke-database dengan ketentuan data diurutkan descending berdasarkan created_at. Line-3, kita menggunakan helper view() dimana file yang di-load dari helper tersebut adalah file index.blade.php yang terletak didalam folder resources/views/products, helper view() secara default sudah berada didalam folder resources/views sehingga kita hanya perlu menuliskan path selanjutnya yakni dalam hal ini adalah products. Sedangkan untuk nama file cukup nama depannya saja dan abaikan .blade.php, dalam case ini maka cukup menuliskan index.

Kemudian buat file index.blade.php didalam folder resources/views/products, kemudian tambahkan code berikut:

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                <div class="card">
                    <div class="card-header">
                        <div class="row">
                            <div class="col-md-6">
                                <h3 class="card-title">Manajemen Produk</h3>
                            </div>
                            <div class="col-md-6">
                                <a href="{{ url('/product/new') }}" class="btn btn-primary btn-sm float-right">Tambah Data</a>
                            </div>
                        </div>
                    </div>
                    <div class="card-body">
                        @if (session('success'))
                            <div class="alert alert-success">
                                {!! session('success') !!}
                            </div>
                        @endif
                        <table class="table table-hover table-bordered">
                            <thead>
                                <tr>
                                    <th>Nama Produk</th>
                                    <th>Deskripsi</th>
                                    <th>Harga</th>
                                    <th>Stok</th>
                                    <th>Tanggal</th>
                                    <th>Aksi</th>
                                </tr>
                            </thead>
                            <tbody>
                                <!-- DIRECTIVE FORELSE SAMA DENGAN FOREACH -->
                                <!-- HANYA SAJA SUDAH FORELSE SUDAH DILENGKAPI FUNGSI UNTUK MENGECEK DATA ADA ATAU TIDAK SEHINGGA KITA TIDAK PERLU LAGI MENGGUNAKAN IF CONDITION -->
                                <!-- JIKA DATA KOSONG MAKA FUNGSI YANG BERJALAN ADALAH CODE BERADA PADA BLOCK CODE @EMPTY -->
                                @forelse($products as $product)
                                <tr>
                                    <!-- MENAMPILKAN VALUE DARI TITLE -->
                                    <td>{{ $product->title }}</td>
                                    <td>{{ str_limit($product->description, 50) }}</td>
                                    <td>Rp {{ number_format($product->price) }}</td>
                                    <td>{{ $product->stock }}</td>
                                    <td>{{ $product->created_at->format('d-m-Y') }}</td>
                                    <!-- TOMBOL DELETE MENGGUNAKAN METHOD DELETE DALAM ROUTING SEHINGGA KITA MEMASUKKAN TOMBOL TERSEBUT KEDALAM TAG <FORM></FORM> -->
                                    <td>
                                        <form action="{{ url('/product/' . $product->id) }}" method="POST">
                                            <!-- @csrf ADALAH DIRECTIVE UNTUK MEN-GENERATE TOKEN CSRF -->
                                            @csrf
                                            <input type="hidden" name="_method" value="DELETE" class="form-control">
                                            <a href="{{ url('/product/' . $product->id) }}" class="btn btn-warning btn-sm">Edit</a>
                                            <button class="btn btn-danger btn-sm">Hapus</button>
                                        </form>
                                    </td>
                                </tr>
                                @empty
                                <tr>
                                    <td class="text-center" colspan="6">Tidak ada data</td>
                                </tr>
                                @endforelse
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Penjelasan: Salah satu hal yang menarik dari blade templating Laravel adalah, kita dapat memecah bagian-bagian dari code HTML sehingga dapat meminimalisir penulisan code yang sama dengan tujuan yang sama secara berulang. Dalam hal ini, code yang sifatnya statis atau tidak berubah-ubah dapat kita pecah kedalam file tersendiri, kemudian kedepannya cukup me-load saja file tersebut. Jika mengacu pada potongan code diatas, maka code HTML yang statis kita tempatkan ke dalam file app.blade.php yang terletak didalam folder resources/views/layouts. Lebih lengkap persoalan blade templating dapat kamu baca pada artikel Berkenalan dengan blade template Laravel.

File app.blade.php untuk sementara kita gunakan dari file yang telah disediakan oleh Laravel. Sehingga style yang akan didapatkan adalah standar view dari Bootstrap, untuk men-generate-nya gunakan command:

php artisan make:auth

Terakhir buat routing-nya agar dapat melihat hasil yang telah dibuat, buka file routes/web.php, kemudian tambahkan code berikut:

Route::group(['prefix' => 'product'], function() {
    Route::get('/', 'ProductController@index');
});

Penjelasan: Agar lebih rapi, kita masukkan kedalam group sehingga apapun routing yang berada dalam group tersebut akan memiliki prefix atau awalan /product.

Untuk melihat hasilnya, buka browser dengan path http://localhost/laravel-invoicer/public/product atau apabila kamu menggunakan command php artisan serve maka path-nya http://localhost:8000/product sehingga hasil yang akan diperoleh akan tampak seperti berikut

laravel invoice

Karena kita belum membuat fungsi untuk menyimpan data, maka kita akan membuat data dummy dengan menggunakan seeder terlebih dahulu untuk melihat bagaimana jika tampilannya jika terdapat data di-database. Untuk membuatnya gunakan command:

php artisan make:seeder ProductTableSeeders

Buka file database/seeds/ProductTableSeeders.php kemudian tambahkan code berikut:

<?php

use Illuminate\Database\Seeder;
use App\Product;

class ProductTableSeeders extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        Product::create([
            'title' => 'Macbook Pro 13 2017',
            'description' => 'Seri komputer jinjing Macintosh yang diproduksi oleh Apple',
            'price' => 18500000,
            'stock' => 5
        ]);

        Product::create([
            'title' => 'Asus Rog Slim',
            'description' => 'Sebuah brand perangkat keras notebook khusus gaming dari ASUS',
            'price' => 10500000,
            'stock' => 15
        ]);
    }
}

Note: Code diatas untuk membuat data dummy, apabila kamu ingin menggunakan Model Factory referensinya dapat mengikuti artikel Berkenalan dengan model factory.

Buka file database/seeds/DatabaseSeeder.php kemudian tambahkan code berikut:

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(ProductTableSeeders::class); //Load seeder class yang telah dibuat
    }
}

Kemudian jalankan command: php artisan db:seed, reload halaman dari module product, maka hasil yang akan kamu peroleh akan tampak seperti ini

laravel invoice manajemen produk

Baca Juga: FItur Upload Progress Bar Indokator Vue Laravel

Create Data Produk

Fungsi selanjutnya adalah untuk menambahkan data kedalam table products. Adapun schema-nya tentu saja adalah sebuah form untuk meng-input data yang kemudian akan diteruskan ke Controller untuk diolah dan disimpan kedalam table products.

Buka file ProductController.php kemudian tambahkan method berikut:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Product;

class ProductController extends Controller
{
    // [... CODE SEBELUMNYA ...]

    public function create()
    {
        return view('products.create');
    }
}

Sama seperti sebelumnya, terdapat file yang di-load yakni create.blade.php. Buat file tersebut dan tempatkan didalam folder resources/views/products, kemudian tambahkan code berikut:

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                <div class="card">
                    <div class="card-header">
                        <h3 class="card-title">Tambah Data Produk</h3>
                    </div>
                    <div class="card-body">
                        <!-- MENAMPILKAN ERROR APABILA TERDAPAT FLASH MESSAGE ERROR -->
                        @if (session('error'))
                            <div class="alert alert-danger">
                                {{ session('error') }}
                            </div>
                        @endif

                        <form action="{{ url('/product') }}" method="post">
                            @csrf
                            <div class="form-group">
                                <label for="">Nama Produk</label>
                                <input type="text" name="title" class="form-control" placeholder="Masukkan nama produk">
                            </div>
                            <div class="form-group">
                                <label for="">Deskripsi</label>
                                <textarea name="description" cols="10" rows="10" class="form-control"></textarea>
                            </div>
                            <div class="form-group">
                                <label for="">Harga</label>
                                <input type="number" name="price" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">Stok</label>
                                <input type="number" name="stock" class="form-control">
                            </div>
                            <div class="form-group">
                                <button class="btn btn-danger btn-sm">Simpan</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Penjelasan: Potongan code diatas hanya sebuah code HTML untuk membuat form input dengan beberapa bagian yakni: title, description, price dan stock. Adapun route dari action yang dituju adalah /product dengan method POST.

Selanjutnya kembali ke controller ProductController.php, tambahkan method berikut untuk meng-handle data yang telah dikirim oleh user:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Product;

class ProductController extends Controller
{
    // [... CODE SEBELUMNYA ...]

    public function save(Request $request)
    {
        //MELAKUKAN VALIDASI DATA YANG DIKIRIM DARI FORM INPUTAN
        $this->validate($request, [
            'title' => 'required|string|max:100',
            'description' => 'required|string',
            'price' => 'required|integer',
            'stock' => 'required|integer'
        ]);

        try {
            //MENYIMPAN DATA KEDALAM DATABASE
            $product = Product::create([
                'title' => $request->title,
                'description' => $request->description,
                'price' => $request->price,
                'stock' => $request->stock
            ]);
            
            //REDIRECT KEMBALI KE HALAMAN /PRODUCT DENGAN FLASH MESSAGE SUCCESS
            return redirect('/product')->with(['success' => '<strong>' . $product->title . '</strong> Telah disimpan']);
        } catch(\Exception $e) {
            //APABILA TERDAPAT ERROR MAKA REDIRECT KE FORM INPUT
            //DAN MENAMPILKAN FLASH MESSAGE ERROR
            return redirect('/product/new')->with(['error' => $e->getMessage()]);
        }
    }
}

Penjelasan: Flash message bekerja dengan membuat session sekali pakai, sehingga pada load selanjutnya session tersebut sudah dihapus. Untuk lebih lengkap mengenai flash message dapat kamu baca pada artikel Sweet Alert dan flash message Laravel 5.

Terakhir, tambahkan route berikut didalam group route product:

Route::group(['prefix' => 'product'], function() {
    //[... CODE SEBELUMYA ...]
    
    Route::post('/', 'ProductController@save');
});

Apabila dijalankan dan mengisi form yang tersedia, maka kita akan mendapatkan feedback error kurang lebih seperti berikut

laravel invoice error fillable

Hal ini terjadi karena penggunakan mass assignment di Laravel mengharuskan untuk mendefinisikan $fillable yang berarti field apa saja yang diizinkan untuk dapat menerima data kedalam database. Buka file model app/Product.php kemudian tambahkan code berikut:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $guarded = [];
    // protected $fillable = ['title', 'description', 'price', 'stock'];
}

Penjelasan: Terdapat dua pilihan yang dapat digunakan, $fillable dan $guarded. Fillable berarti mengizinkan field apa saja yang boleh menerima data. Sedangkan guarded sebaliknya, field apa saja yang tidak boleh (black list) atau tidak diizinkan. Untuk jumlah field yang sedikit kamu bisa menggunakan fillable, sedangkan jika field nya banyak saya sarankan untuk menggunakan guarded sehingga kamu tidak perlu menuliskan semua field tersebut.

Update Data Produk

Perubahan data adalah sesuatu yang pasti terjadi pada sebuah aplikasi karena kesalahan dalam entry data sering kali terjadi meskipun hanya sebuah kesalahan kecil seperti typo, sehingga sebagai developer kita perlu menyediakan fitur untuk mengubah atau mengedit data.

Dalam fitur ini terdapat dua step yang berperan, step pertama adalah menampilkan data apa yang akan di ubah dan step selanjutnya adalah melakukan perubahan terhadap data itu sendiri kedalam database. Buka file ProductController.php, kemudian tambahkan method berikut:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Product;

class ProductController extends Controller
{
	// [.. CODE SEBELUMNYA ..]
    public function edit($id)
    {
        $product = Product::find($id); // Query ke database untuk mengambil data dengan id yang diterima
        return view('products.edit', compact('product'));
    }
}

Selanjutnya buat file edit.blade.php didalam folder resources/views/products kemudian tambahkan code berikut:

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                <div class="card">
                    <div class="card-header">
                        <h3 class="card-title">Edit Data Produk</h3>
                    </div>
                    <div class="card-body">
                        @if (session('error'))
                            <div class="alert alert-danger">
                                {{ session('error') }}
                            </div>
                        @endif
						
                        <!-- ACTION MENGARAH KE /product/id -->
                        <form action="{{ url('/product/' . $product->id) }}" method="post">
                            @csrf
                            <!-- KARENA METHOD YANG AKAN DIGUNAKAN ADALAH PUT -->
                            <!-- MAKA KITA PERLU MENGIRIMKAN PARAMETER DENGAN NAME _method -->
                            <!-- DAN VALUE PUT -->
                            <input type="hidden" name="_method" value="PUT" class="form-control">
                            <div class="form-group">
                                <label for="">Nama Produk</label>
                                <input type="text" name="title" class="form-control" value="{{ $product->title }}" placeholder="Masukkan nama produk">
                            </div>
                            <div class="form-group">
                                <label for="">Deskripsi</label>
                                <textarea name="description" cols="10" rows="10" class="form-control">{{ $product->description }}</textarea>
                            </div>
                            <div class="form-group">
                                <label for="">Harga</label>
                                <input type="number" name="price" class="form-control" value="{{ $product->price }}">
                            </div>
                            <div class="form-group">
                                <label for="">Stok</label>
                                <input type="number" name="stock" class="form-control" value="{{ $product->stock }}">
                            </div>
                            <div class="form-group">
                                <button class="btn btn-primary btn-sm">Update</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Untuk meng-handle data yang dikirimkan oleh user untuk diubah, maka kembali ke controller ProductController.php, kemudian tambahkan method berikut:

public function update(Request $request, $id)
{
    $product = Product::find($id); // QUERY UNTUK MENGAMBIL DATA BERDASARKAN ID
    //KEMUDIAN MENGUPDATE DATA TERSEBUT
    $product->update([
        'title' => $request->title,
        'description' => $request->description,
        'price' => $request->price,
        'stock' => $request->stock
    ]);
    //LALU DIARAHKAN KE HALAMAN /product DENGAN FLASH MESSAGE SUCCESS
    return redirect('/product')->with(['success' => '<strong>' . $product->title . '</strong> Diperbaharui']);
}

Terakhir buat route-nya dengan menambahkan code berikut pada routes/web.php:

Route::group(['prefix' => 'product'], function() {
    //[.. CODE SEBELUMNYA ..]
    Route::get('/{id}', 'ProductController@edit');
    Route::put('/{id}', 'ProductController@update');
});

Apabila dijalankan maka kita akan memperoleh hasil seperti berikut

laravel invoice edit data

Dan ketika ditekan update maka akan diarahkan kembali kehalaman /product dengan flash message

laravel invoice update

Delete Data Produk

Mengapus data yang telah usang atau memang data tersebut tidak seharusnya di-input kedalam aplikasi, maka fitur selanjutnya adalah Delete. Fitur ini bekerja sangat sederhana karena ketika tombol ditekan maka akan langsung di-handle oleh controller kemudian dikembalikan kehalaman /product, sehingga kita tidak memerlukan view baru lagi.

Buka file ProductController.php kemudian tambahkan method berikut:

public function destroy($id)
{
    $product = Product::find($id); //QUERY KEDATABASE UNTUK MENGAMBIL DATA BERDASARKAN ID
    $product->delete(); // MENGHAPUS DATA YANG ADA DIDATABASE
    return redirect('/product')->with(['success' => '</strong>' . $product->title . '</strong> Dihapus']); // DIARAHKAN KEMBALI KEHALAMAN /product
}

Terakhir tambakan route berikut kedalam file routes/web.php

Route::group(['prefix' => 'product'], function() {
    // [.. CODE SEBELUMNYA ..]
    Route::delete('/{id}', 'ProductController@destroy');
});

Apabila dijalankan dan tombol hapus ditekan maka kamu akan mendapatkan flash message berikut

laravel invoice delete

Menampilkan error validasi

Sepanjang step yang telah kita lalui, belum ada code untuk menampilkan error message yang dikirimkan oleh validasi yang gagal. Sengaja saya buat demikian agar kita memahami artikel ini dari bagian per bagian. Analisanya, error tersebut pastinya akan ditampilkan di view dan tentu saja validasi hanya ada pada form input-an. Jadi hanya ada dua form input-an, yakni create dan edit.

Buka file create.blade.php yang terletak didalam folder resources/views/products, kemudian modifikasi code-nya menjadi:

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                <div class="card">
                    <div class="card-header">
                        <h3 class="card-title">Tambah Data Produk</h3>
                    </div>
                    <div class="card-body">
                        @if (session('error'))
                            <div class="alert alert-danger">
                                {{ session('error') }}
                            </div>
                        @endif

                        <form action="{{ url('/product') }}" method="post">
                            @csrf
                            <div class="form-group">
                                <label for="">Nama Produk</label>
                                <input type="text" name="title" class="form-control {{ $errors->has('title') ? 'is-invalid':'' }}" placeholder="Masukkan nama produk">
                                <p class="text-danger">{{ $errors->first('title') }}</p>
                            </div>
                            <div class="form-group">
                                <label for="">Deskripsi</label>
                                <textarea name="description" cols="10" rows="10" class="form-control {{ $errors->has('description') ? 'is-invalid':'' }}"></textarea>
                                <p class="text-danger">{{ $errors->first('description') }}</p>
                            </div>
                            <div class="form-group">
                                <label for="">Harga</label>
                                <input type="number" name="price" class="form-control {{ $errors->has('price') ? 'is-invalid':'' }}">
                                <p class="text-danger">{{ $errors->first('price') }}</p>
                            </div>
                            <div class="form-group">
                                <label for="">Stok</label>
                                <input type="number" name="stock" class="form-control {{ $errors->has('stock') ? 'is-invalid':'' }}">
                                <p class="text-danger">{{ $errors->first('stock') }}</p>
                            </div>
                            <div class="form-group">
                                <button class="btn btn-danger btn-sm">Simpan</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Penjelasan: Validasi mengembalikan nilainya kedalam variable $errors. Sehingga ada dua bagian yang perlu diperhatikan, pertama: {{ $errors->has('title') ? 'is-invalid':'' }} , code tersebut hanya ternary operator untuk mengecek jika title errors maka class 'is-invalid' akan ditambahkan, selain itu tidak akan terjadi apa-apa. Selanjutnya {{ $errors->first('title') }}, berfungsi untuk mengambil value dari title yang terdapat didalam variable $errors untuk ditampilkan sebagai error message.

Cara menampilkan validation message sama saja, hanya berbeda parameter apa yang akan ditampilkan. So, untuk file edit.blade.php silahkan dibuat sendiri untuk melihat apa kamu sudah paham atau belum, jika belum coba di-baca kembali.

Adapun tampilannya apabila terdapat error validasi adalah seperti berikut

laravel invoice error validation

Ohya, agar halaman manajemen produk dapat diakses dengan hanya meng-klik link pada navigasi, kita modifikasi file resources/views/layouts/app.blade.php dan perhatian bagian code dibawah saja:

<!-- [.. CODE SEBELUMNYA ..] -->

<!-- Right Side Of Navbar -->
<ul class="navbar-nav ml-auto">
    <!-- Authentication Links -->
    
    <!-- TAMBAHKAN CODE BERIKUT -->
    <li class="nav-item">
        <a href="{{ url('/product') }}" class="nav-link">Manajemen Produk</a>
    </li>
    
    @guest
        <li class="nav-item">
            <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
        </li>
        <li class="nav-item">
            @if (Route::has('register'))
                <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
            @endif
        </li>
    @else
        <li class="nav-item dropdown">
            <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                {{ Auth::user()->name }} <span class="caret"></span>
            </a>

            <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                <a class="dropdown-item" href="{{ route('logout') }}"
                   onclick="event.preventDefault();
                                 document.getElementById('logout-form').submit();">
                    {{ __('Logout') }}
                </a>

                <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                    @csrf
                </form>
            </div>
        </li>
    @endguest
</ul>

<!-- [.. CODE SETELAHNYA ..] -->

Kesimpulan

Aplikasi tidak hanya sebatas CRUD karena masih banyak hal lain yang akan terlibat, namun setidaknya ketika kamu telah paham dasar dan bagaimana crud bekerja maka kamu sudah dapat merangkai aplikasi sederhana, dan seiring berjalannya waktu modal dasar ini akan melatih kamu untuk merangkai aplikasi yang lebih kompleks.

Ohya untuk kamu yang kesulitan merangkai code diatas, kamu dapat lihat dokumentasi code-nya di Github.

Category:
Share:

Comments