Import data dari Excel ke database adalah solusi paling efisien untuk mengelola banyak data sekaligus. Dengan Laravel dan PhpSpreadsheet, proses ini bisa dibuat otomatis, rapi, dan minim error.
Artikel ini langsung membahas praktiknya, mulai dari contoh sederhana sampai studi kasus nyata.
Kenapa Perlu Import Excel?
Beberapa alasan kenapa fitur ini penting:
- Menghemat waktu input data
- Mengurangi human error
- Cocok untuk data massal (undangan, produk, user, dll)
- Bisa dibuat template agar user tidak salah format
Persiapan Install Library
Gunakan PhpSpreadsheet:
composer require phpoffice/phpspreadsheet
Contoh Dasar Import Excel (Tabel Barang)
Struktur Tabel
Schema::create('barangs', function (Blueprint $table) {
$table->id();
$table->string('nama_barang');
$table->integer('qty');
$table->integer('harga');
$table->timestamps();
});
Format Excel
Pastikan urutan kolom seperti ini:
| nama_barang | qty | harga |
|---|---|---|
| Pensil | 10 | 2000 |
| Buku | 5 | 5000 |
Controller Import
use PhpOffice\PhpSpreadsheet\IOFactory;
use App\Models\Barang;public function import(Request $request)
{
$request->validate([
'file' => 'required|mimes:xlsx,xls'
]); $file = $request->file('file')->getPathname();
$spreadsheet = IOFactory::load($file);
$rows = $spreadsheet->getActiveSheet()->toArray(); unset($rows[0]); // hapus header foreach ($rows as $row) {
Barang::create([
'nama_barang' => $row[0],
'qty' => $row[1],
'harga' => $row[2],
]);
} return back()->with('success', 'Import berhasil');
}
Membuat Template Excel Agar Tidak Salah Format
Supaya user tidak salah isi, buat template:
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;public function downloadTemplate()
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet(); $sheet->fromArray([
['nama_barang', 'qty', 'harga']
], NULL, 'A1'); $writer = new Xlsx($spreadsheet);
$fileName = 'template_barang.xlsx'; header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header("Content-Disposition: attachment; filename=\"$fileName\"");
$writer->save('php://output');
exit;
}
Studi kasus : Dalam banyak kasus, kita punya kebutuhan contoh seperti ini:
Kita ingin input banyak data perusahaan sekaligus, tapi tidak mau input manual satu per satu di form. Selain itu, setiap perusahaan juga harus:
- punya akun user otomatis
- punya kode unik
- punya token
- dan mengikuti aturan tertentu (misalnya kuota & meja)
Jadi alurnya yang diinginkan adalah:
- Admin download template Excel
- Admin isi data perusahaan di Excel
- Upload ke sistem
- Sistem otomatis:
- membaca file Excel
- membuat user baru
- generate kode perusahaan (ext50-0001, dst)
- menentukan kuota (company vs personal)
- menyimpan ke database

Dengan konsep ini, input ratusan data bisa selesai dalam hitungan detik.
Struktur Data yang Digunakan
Tabel perusahaans:
- kode_perusahaan
- nama_perusahaan
- jenis (personal / company)
- pic
- meja
- kuota
- token
Relasi ke tabel users (otomatis dibuat saat import)
Route
Tambahkan di web.php:
use App\Http\Controllers\Admin\PerusahaanTemplateExport;Route::prefix('admin/undangan')->group(function () {
Route::get('/download-template', [PerusahaanTemplateExport::class, 'downloadTemplate'])
->name('undangan.template'); Route::post('/import', [PerusahaanTemplateExport::class, 'import'])
->name('undangan.import');
});
Buat Controller
php artisan make:controller Admin/PerusahaanTemplateExport
Controller Lengkap
<?phpnamespace App\Http\Controllers\Admin;use App\Http\Controllers\Controller;
use Illuminate\Http\Request;use App\Models\User;
use App\Models\Perusahaan;
use Carbon\Carbon;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Hash;use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;class PerusahaanTemplateExport extends Controller
{
// DOWNLOAD TEMPLATE
public function downloadTemplate()
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet(); // header
$sheet->fromArray([
['nama_perusahaan', 'jenis', 'pic', 'meja', 'kuota']
], NULL, 'A1'); // dropdown jenis
$validationJenis = $sheet->getCell('B2')->getDataValidation();
$validationJenis->setType(DataValidation::TYPE_LIST);
$validationJenis->setAllowBlank(false);
$validationJenis->setFormula1('"personal,company"'); // dropdown pic
$validationPic = $sheet->getCell('C2')->getDataValidation();
$validationPic->setType(DataValidation::TYPE_LIST);
$validationPic->setAllowBlank(false);
$validationPic->setFormula1('"iqbal,dewi,vic,harzen,hrd"'); // apply ke banyak baris
for ($i = 2; $i <= 100; $i++) {
$sheet->getCell('B' . $i)->setDataValidation(clone $validationJenis);
$sheet->getCell('C' . $i)->setDataValidation(clone $validationPic);
} $writer = new Xlsx($spreadsheet);
$fileName = 'template_perusahaan_' . Carbon::now()->format('dmyHi') . '.xlsx'; header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header("Content-Disposition: attachment; filename=\"$fileName\"");
$writer->save('php://output');
exit;
} // IMPORT EXCEL
public function import(Request $request)
{
$request->validate([
'file' => 'required|mimes:xlsx,xls'
]); $file = $request->file('file')->getPathname();
$spreadsheet = IOFactory::load($file);
$rows = $spreadsheet->getActiveSheet()->toArray(); unset($rows[0]); // skip header foreach ($rows as $row) { if (!$row[0]) continue; $nama = $row[0];
$jenis = strtolower($row[1]);
$pic = $row[2];
$meja = $row[3];
$kuota = $row[4]; // generate kode
$lastKode = Perusahaan::where('kode_perusahaan', 'like', 'ext50-%')
->orderBy('kode_perusahaan', 'desc')
->first(); $number = $lastKode ? ((int)substr($lastKode->kode_perusahaan, -4)) + 1 : 1;
$kode = 'ext50-' . str_pad($number, 4, '0', STR_PAD_LEFT); // create user
$user = User::create([
'name' => $nama,
'email' => Str::slug($kode) . '@extrupack.com',
'password' => Hash::make($kode),
'role' => 'user'
]); // create perusahaan
Perusahaan::create([
'user_id' => $user->id,
'kode_perusahaan' => $kode,
'nama_perusahaan' => $nama,
'jenis' => $jenis,
'pic' => $pic,
'meja' => $jenis === 'personal' ? $meja : null,
'kuota' => $jenis === 'company' ? $kuota : 1,
'token' => Str::random(20),
]);
} return back()->with('success', 'Import berhasil!');
}
}
View (Blade)
<div class="d-flex flex-wrap align-items-center gap-2"> <a href="{{ route('undangan.template') }}" class="btn btn-success btn-sm">
Download Template
</a> <form action="{{ route('undangan.import') }}" method="POST" enctype="multipart/form-data" class="d-flex gap-2">
@csrf
<input type="file" name="file" class="form-control form-control-sm" required>
<button type="submit" class="btn btn-primary btn-sm">Import</button>
</form></div>@if(session('success'))
<div class="alert alert-success mt-2">
{{ session('success') }}
</div>
@endif

Format Excel
| nama_perusahaan | jenis | pic | meja | kuota |
|---|---|---|---|---|
| PT ABC | company | joko | 10 | |
| Bowo | personal | mega | A1 |
QnA
Q: Kenapa kuota personal tidak terbaca?
A: Karena otomatis diset jadi 1 di backend.
Q: Kenapa meja company kosong?
A: Karena memang hanya dipakai untuk personal.
Q: Bisa tambah validasi?
A: Bisa, misalnya cek jenis hanya “personal” atau “company”.
Q: Aman untuk banyak data?
A: Aman, tapi kalau sangat besar gunakan queue.
Q: Bisa dikembangkan lagi?
A: Bisa, seperti:
- cek duplikat perusahaan
- import dengan progress bar
- export ulang ke Excel
Dengan struktur ini, sistem kamu sudah masuk level “semi automation”, bukan sekadar CRUD biasa.

