Membuat Fitur Bulk Import Data CSV Laravel 5.6

Membuat Fitur Bulk Import Data CSV Laravel 5.6

Pendahuluan

Seringkali yang menjadi kendala berikutnya yang mestinya dipikirkan bagi seorang programmer adalah membuat kemudahan bagi pengguna aplikasi yang telah dibuatnya, salah satunya adalah dalam mengelola data. Misalnya saja, kita asumsikan data produk dari aplikasi e-commerce yang perubahannya sangat dinamis, seperti produk hanya berlaku dalam 6 bulan dan terjadi pergantian data pada bulan berikutnya. Hal ini tentu saja sangat melelahkan bagi operator yang sedang bertugas untuk melakukan input data satu persatu melalui form yang telah disediakan dalam aplikasi tersebut.

Solusi yang ditawarkan salah satunya adalah dengan membuat fitur bulk import data yang telah disusun dengan format CSV, yang nantinya file tersebutlah yang akan di-upload untuk kemudian diolah dan disimpan oleh sistem secara otomatis.

Baca Juga: Membuat Aplikasi POS (Point of Sales Laravel 5.6 - Chart

Tahap Persiapan

Sebenarnya tidak mesti harus menggunakan Laravel 5.6, hanya saja sejak artikel ini dituliskan, Laravel yang terbaru adalah versi 5.6 (baca: meskipun dalam dokumentasi telah tersedia 5.7, namun saya belum menemukan official release-nya dalam artikel manapun).

Karena artikel ini tidak terkait dengan artikel lainnya, maka kita saya akan memulainya dengan Laravel fresh install:

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

Case yang akan kita angkat berbeda dari yang disampaikan pada pendahuluan, dimana analoginya menggunakan data produk, sedangkan yang akan dibahas adalah data user. Mau menggunakan case yang mana saja, sebab intinya adalah bagaimana kita bisa meng-upload file csv untuk kemudian disimpan ke dalam table yang terakait.

Daripada membuat table baru lagi, maka kita akan memanfaatkan table yang ada saja, yakni table users. Buat controller dengan command:

php artisan make:controller UserController

Buka file UserController.php, kemudian tambahkan method berikut:

public function index()
{
    $user = User::paginate(10);
    return view('welcome', compact('user'));
}

public function import(Request $request)
{
    //DIMODIFIKASI NANTI
}

Note: Kita juga memanfaatkan file view yang telah ada, yakni: welcome.blade.php.

Jangan lupa tambahkan use statement:

use App\User;

Buka file welcome.blade.php, kemudian modifikasi menjadi:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Import Data</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
​
</head>
<body>
    <div class="container">
        <div class="row" style="padding-top: 20px">
            
            <!-- FORM UNTUK MENG-UPLOAD FILE -->
            <div class="col-md-6">
                <form action="{{ url('/') }}" enctype="multipart/form-data" method="post">
                    @csrf
​
                    @if (session('success'))
                        <div class="alert alert-success">
                            {{ session('success') }}
                        </div>
                    @endif
​
                    @if (session('error'))
                        <div class="alert alert-danger">
                            {{ session('error') }}
                        </div>
                    @endif
                    <div class="form-group">
                        <label for="">Import Data</label>
                        <input type="file" accept=".csv" name="file" class="form-control {{ $errors->has('file') ? 'is-invalid':'' }}" required>
                        <p class="text-danger">{{ $errors->first('file') }}</p>
                    </div>
                    <div class="form-group">
                        <button class="btn btn-danger btn-sm">Import</button>
                    </div>
                </form>
            </div>
            
            <!-- TABLE UNTUK MELIHAT DATA YANG SUDAH DISIMPAN -->
            <div class="col-md-6">
                <div class="table-responsive">
                    <table class="table table-hover table-bordered">
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>Name</th>
                                <th>Email</th>
                                <th>Created</th>
                            </tr>
                        </thead>
                        <tbody>
                            @forelse ($user as $row)
                                <tr>
                                    <td></td>
                                    <td>{{ $row->name }}</td>
                                    <td>{{ $row->email }}</td>
                                    <td>{{ $row->created_at }}</td>
                                </tr>
                            @empty
                                <tr>
                                    <td colspan="4" class="text-center">No Records Found</td>
                                </tr>
                            @endforelse
                        </tbody>
                    </table>
                </div>
                <div class="float-right">
                    {!! $user->links() !!}
                </div>
            </div>
        </div>
    </div>
</body>
</html>

Note: Tidak ada yang perlu dijelaskan secara detail, sebab jika kamu telah membaca artikel ini, saya berasumsi kamu telah mengerti CRUD di Laravel.

Buka file routes/web.php, kemudian tambahkan code:

<?php
​
Route::get('/', 'UserController@index');
Route::post('/', 'UserController@import');

Baca Juga: Tips Menggunakan Eloquent Laravel

Bulk Import Data CSV

Bulk import berarti memasukkan data dalam jumlah yang banyak, maka hal ini tentu saja tidak dapat diproses secara langsung ketika file sedang di-upload karena prosesnya membutuhkan waktu yang 'lebih banyak' sesuai jumlah data yang akan diolah.

Hal lainnya, tentu saja akan memberikan kesan yang buruk kepada user karena harus menunggu proses tersebut secara langsung. Lalu bagaimana jika pada saat proses sedang berlangsung, tiba tiba koneksi internet terputus? Kejadian ini pasti akan sangat menyebalkan. Maka solusinya adalah sebaiknya menjalankan proses pengolahan datanya dari sisi server atau berjalan dibelakang layar.

Laravel telah menyediakan fitur yang bernama Queue atau biasa dikenal sebagai Laravel Job. Schema-nya adalah ketika user meng-upload file .csv, maka file tersebut akan disimpan kedalam storage, kemudian menginstruksikan pada Laravel untuk membuat Job untuk mengolah data yang ada didalam file tersebut. Queue membutuhkan table bantuan untuk menyimpan instruksi yang diberikan, table tersebut bernama jobs.

Untuk membuat table jobs , kamu dapat menggunakan command:

php artisan queue:table
php artisan migrate

Note: Line pertama berfungsi untuk men-generate migration yang akan meng-handle proses create table jobs, kemudian line kedua mengeksekusi migration yang telah dibuat sebelumnya.

Selanjutnya, buka file UserController.php, kemudian modifikasi method import() menjadi:

public function import(Request $request)
{
    $this->validate($request, [
        'file' => 'required'
    ]);
    
    //JIKA FILE ADA
    if ($request->hasFile('file')) {
        //GET FILE NYA
        $file = $request->file('file');
        //MEMBUAT FILENAME DENGAN MENGAMBIL EKSTENSI DARI FILE YANG DI-UPLOAD
        $filename = time() . '.' . $file->getClientOriginalExtension();
        
        //FILE TERSEBUT DISIMPAN KEDALAM FOLDER
        // STORAGE > APP > PUBLIC > IMPORT
        //DENGAN MENGGUNAKAN METHOD storeAs()
        $file->storeAs(
            'public/import', $filename
        );
    
        //MEMBUAT INSTRUKSI JOB QUEUE
        ImportJob::dispatch($filename);
        //REDIRECT DENGAN FLASH MESSAGE BERHASIL
        return redirect()->back()->with(['success' => 'Upload success']);
    }  
    //JIKA TIDAK ADA FILE, REDIRECT ERROR
    return redirect()->back()->with(['error' => 'Failed to upload file']);
}

Jangan lupa untuk menambahkan use statement:

use App\Jobs\ImportJob;

Untuk me-load data yang ada di dalam file .csv, maka kita akan menggunakan package league/csv, install package tersebut dengan command:

composer require league/csv:^9.0

ImportJob merupakan class yang akan mengolah job yang telah diberikan, didalam class inilah kita akan me-load file yang telah di-upload kemudian disimpan kedalam table yang terkait, dalam hal ini adalah table users. Untuk membuatnya gunakan command:

php artisan make:job ImportJob

Buka file app/Jobs/ImportJob.php, kemudian modifikasi menjadi:

<?php
​
namespace App\Jobs;
​
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
​
//IMPORT PACKAGE LEAGUE/CSV
use League\Csv\Reader;
use App\User;
use File;
​
class ImportJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    protected $filename;
​
    /**
     * Create a new job instance.
     *
     * @return void
     */
    
    //MEMINTA DATA NAMA FILE
    public function __construct($filename)
    {
        $this->filename = $filename;
    }
​
    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // READ DATA DARI FILE CSV YANG DISIMPAN DIDALAM FOLDER
        // STORAGE > APP > PUBLIC > IMPORT > NAMAFILE.CSV
        $csv = Reader::createFromPath(storage_path('app/public/import/' . $this->filename), 'r');
        //BARIS PERTAMA DI-SET SEBAGAI KEY DARI ARRAY YANG DIHASILKAN
        $csv->setHeaderOffset(0);
        
        //LOOPING DATA YANG TELAH DI-LOAD
        foreach ($csv as $row) {
            //SIMPAN KE DALAM TABLE USER
            User::create([
                'name' => $row['name'],
                'email' => $row['email'],
                'password' => $row['password'],
                'remember_token' => $row['remember_token'],
                'created_at' => $row['created_at'],
                'updated_at' => $row['updated_at']
            ]);
        }
        //APABILA PROSES TELAH SELESAI, FILE DIHAPUS.
        File::delete(storage_path('app/public/import/' . $this->filename));
    }
}

Note: Karena baris pertama digunakan sebagai key untuk array yang dihasilkan, maka bari kedua dan selanjutnya berisi data yang akan disimpan. Sedangkan pada baris pertama sebaiknya diisi dengan nama field dari table terkait saja.

Terakhir, pada file .env bagian QUEUE_DRIVER di ubah menjadi:

QUEUE_DRIVER=database

Note: Ganti sync menjadi database agar instruksinya tidak dijalankan secara langsung, melainkan disimpan kedalam database terlebih dahulu.

Testing Bulk Import

Sebagai pelengkap fitur bulk import csv yang telah kita buat, berikut adalah file .csv yang dapat teman-teman gunakan untuk proses testing. Di dalamnya terdapat 7000 data user yang telah disusun dengan ketentuan, pada baris pertama berisi nama field yang akan menjadi key dari array, sedangkan baris selanjutnya adalah data yang ingin disimpan kedalam database.

Upload file .csv tersebut pada form yang telah disediakan

cara import csv laravel 5.6

Setelah proses upload file selesai, kamu dapat menjalankan command berikut agar instruksi yang ada di dalam table jobs segera dikerjakan.

php artisan queue:work

Prosesnya akan tampak seperti ini:

cara import csv laravel 5.6

Jadi setiap kali meng-upload file, harus menjalankan command queue:work dong? Tentu saja iya atau kamu bisa menjalankannya secara terus menerus dengan menggunakan service dari Supervisor. Untuk cara install dan konfigurasinya dapat kamu lihat pada official documentation.

Kesimpulan

Queue atau Job dapat menjalankan berbagai macam peran dibelakang layar sehingga mengurangi load time yang akan dialami oleh user untuk setiap proses yang membutuhkan waktu dalam menyelesaikannya, seperti: Mengirim email, mengolah data yang sifatnya besar, dan lain sebagainya.

Untuk dokumentasi dari project diatas dapat kamu lihat di Github.

Category:
Share:

Comments