Sebelum kita mulai ngetik kode, mari kita resapi dulu kutipan sakti dari Om Robert C. Martin di bukunya yang legendaris, Clean Code:
"Even bad code can function. But if code isn’t clean, it can bring a development organization to its knees."
Maksudnya gini coy, kode yang berantakan emang kadang bisa aja jalan, tapi ujung-ujungnya bakal bikin puyeng sendiri pas website udah makin gede. Makanya, dari penjelasan ini kita kudu biasain nulis script yang rapi. Ngulik struktur Laravel ini ibaratnya kita lagi nyusun pondasi website biar kerangka SEO-nya cakep. Kalo ente udah kebiasa utak-atik kode XML atau HTML yang ruwet demi ngejar skor sempurna 100 di PageSpeed mobile, nah mental teliti dan bersih kayak gitu bakal ngebantu banget pas kita mainan logika backend di mari.
Udah siap? Yuk, tarik napas, buka XAMPP, idupin Apache sama MySQL-nya. Kita masuk ke selanjutnya!
Tutorial Laravel E-Commerce Dari Bikin Database Sampai Online di cPanel
Babat Alas Database & Bikin Etalase Barang
Di penjelasan pertama ini, target kita kagak usah muluk-muluk dulu. Kita bakal bikin pondasi buat nyimpen data barang jualan. Kalo di toko offline, ini ibarat kita lagi pesen rak kaca buat majang barang. Di Laravel, rak kaca ini namanya Database dan Migration.
Ane asumsikan ente udah install Laravel-nya pake composer dan udah nyambungin file .env ke database MySQL di XAMPP. Kalo udah, buka terminal atau CMD, arahin ke folder project ente, trus ketik mantra sakti ini:
php artisan make:model Product -m
Penjelasan Singkat: Perintah di atas itu ibarat sekali tepuk dua laler dapet. Kita nyuruh Laravel bikinin file Model (namanya Product) sekaligus bikinin file Migration (karena ada tambahan -m). Model itu buat jembatan komunikasi ke data, nah Migration itu blueprint atau rancangan tabel buat di MySQL-nya.
Sekarang, coba ente buka text editor (kayak VS Code). Cari folder
database/migrations/. Di situ bakal ada file baru yang berakhiran _create_products_table.php. Buka dah tuh file.
Di dalemnya, ada fungsi up(). Nah, di sini kita tentuin isi rak etalase kita ada apa aja. Tambahin script di bawah ini:
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name'); // Buat nyimpen nama barang
$table->text('description'); // Buat deskripsi panjang, biar SEO friendly!
$table->integer('price'); // Buat nyimpen harga
$table->integer('stock'); // Buat nyimpen sisa stok barang
$table->string('image')->nullable(); // Buat nyimpen nama file gambar, boleh kosong
$table->timestamps(); // Ini otomatis ngerekam kapan barang ditambahin/diubah
});
}
Kalo udah diketik rapi, save filenya. Trus balik lagi ke terminal, jalankan perintah ini biar rancangannya beneran dibikinin jadi tabel di phpMyAdmin ente:
php artisan migrate
Kalo muncul tulisan Done atau ijo-ijo, berarti etalase ente udah jadi di database! Cakep bener dah.
Tabel udah jadi, sekarang kita kudu ngatur jembatannya alias si Model. Cari folder
app/Models/ trus buka file Product.php.
Kita kudu ngasih tau si Laravel, kolom mana aja di tabel tadi yang boleh diisi data sama sistem (Mass Assignment). Ini penting buat keamanan. Tambahin properti $fillable kayak gini:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use HasFactory;
// Ini ngasih tau Laravel: "Hei, kolom-kolom ini aja ya yang boleh diisi datanya!"
protected $fillable = [
'name',
'description',
'price',
'stock',
'image',
];
}
Save lagi. Selesai dah tugas kita buat nyiapin pondasi barang! Sambil nyeruput kopi, kita lanjutin lagi ngoprek website e-commerce-nya.
Bikin Pelayan, Buka Pintu, dan Nata Ruang Pameran
Mari kita resapi dulu petuah sakti dari suhu User Experience (UX), Om Steve Krug:
"As a rule, people don't like to puzzle over how to do things. The fact that the people who built the site didn't care enough to make things obvious—and easy—can erode our confidence..."
Jangan bikin orang mikir keras pas ngeliat kode buatan kita. Semuanya kudu dirancang gampang dimengerti dan rapi. Makanya di Laravel, urusan ngambil data, ngatur alamat URL, sampe nampilin desain HTML itu dipisah-pisah tempatnya (MVC).
Pelayan toko di Laravel ini namanya Controller. Tugasnya nerima permintaan dari pengunjung, nanya ke Model (penjaga etalase), trus ngasih datanya ke ruang pameran (View).
Buka terminal ente, ketik mantra ini:
php artisan make:controller ProductController
Buka app/Http/Controllers/ProductController.php, tambahin kode ini:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Product; // Panggil penjaga etalasenya di mari
class ProductController extends Controller
{
public function index()
{
// Nyuruh Model 'Product' ngambil semua data dari database
$products = Product::all();
// Bawa datanya ke ruang pameran (view) yang namanya 'products.index'
return view('products.index', compact('products'));
}
}
Pelayan udah siap nungguin, tapi pintunya belom ada. Pembeli kudu lewat URL mana kalo mau liat-liat barang? Meluncur ke folder
routes/ trus buka file web.php.
use App\Http\Controllers\ProductController;
// Kalo ada orang ngetik URL '/produk', arahin ke ProductController bagian 'index'
Route::get('/produk', [ProductController::class, 'index']);
Buka folder
resources/views/. Bikin folder baru products. Di dalemnya, bikin file index.blade.php.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Katalog Barang Jualan</title>
</head>
<body style="font-family: sans-serif; padding: 20px;">
<h2>Daftar Barang Jualan</h2>
<div style="display: flex; gap: 20px; flex-wrap: wrap;">
{{-- Kita pake '@foreach' buat ngulang nampilin data produk --}}
@foreach ($products as $item)
<div style="border: 1px solid #ccc; padding: 15px; width: 250px; border-radius: 8px;">
<h3>{{ $item->name }}</h3>
<p style="color: #555; font-size: 14px;">{{ $item->description }}</p>
<p><strong>Harga: </strong> Rp {{ number_format($item->price, 0, ',', '.') }}</p>
<p>Sisa Stok: {{ $item->stock }}</p>
<button style="background-color: green; color: white; padding: 10px; border: none;">
Beli Sekarang
</button>
</div>
@endforeach
</div>
{{-- Kalo datanya kosong --}}
@if($products->isEmpty())
<p>Yah, barangnya belom ada nih. Etalase masih kosong melompong!</p>
@endif
</body>
</html>
Bikin Formulir dan Masukin Barang ke Gudang
"Defensive coding is not about being paranoid... It’s about validating everything you are given." - Andrew Hunt & David Thomas
Kita kagak boleh percaya gitu aja sama apa yang diinput orang ke dalem sistem kita. Kudu dicek, divalidasi, biar database kagak jebol.
Buka
routes/web.php.
// Pintu buat nampilin halaman formulir (GET)
Route::get('/produk/tambah', [ProductController::class, 'create']);
// Pintu rahasia buat nangkep data dan dikirim ke gudang (POST)
Route::post('/produk/simpan', [ProductController::class, 'store']);
Masuk ke folder
resources/views/products/, bikin file create.blade.php.
<form action="/produk/simpan" method="POST" enctype="multipart/form-data">
{{-- Token keamanan biar form kagak dibajak --}}
@csrf
<div>
<label>Nama Barang:</label><br>
<input type="text" name="name" required>
</div>
<div>
<label>Deskripsi (SEO Friendly):</label><br>
<textarea name="description" rows="4" required></textarea>
</div>
<div>
<label>Harga (Rupiah):</label><br>
<input type="number" name="price" required>
</div>
<div>
<label>Jumlah Stok:</label><br>
<input type="number" name="stock" required>
</div>
<div>
<label>Foto Produk / Thumbnail:</label><br>
<input type="file" name="image" accept="image/*">
</div>
<button type="submit">💾 Simpan Barang</button>
</form>
Formulir udah cakep, sekarang pelayan toko kita kudu diajarin cara nangkep datanya di
ProductController.php.
// Fungsi buat nampilin formulir
public function create()
{
return view('products.create');
}
// Fungsi buat nangkep data (Validasi & Simpan)
public function store(Request $request)
{
// 1. Ini satpamnya. Kita validasi dulu datanya bener kagak.
$request->validate([
'name' => 'required|max:255',
'description' => 'required',
'price' => 'required|numeric',
'stock' => 'required|numeric',
'image' => 'image|mimes:jpeg,png,jpg|max:2048', // Maks 2MB
]);
// 2. Ngurusin foto kalo pengunjung nge-upload gambar
$imageName = null;
if ($request->hasFile('image')) {
$imageName = time() . '.' . $request->image->extension();
$request->image->move(public_path('images'), $imageName);
}
// 3. Masukin data ke database (Model Product)
Product::create([
'name' => $request->name,
'description' => $request->description,
'price' => $request->price,
'stock' => $request->stock,
'image' => $imageName,
]);
// 4. Balik lagi ke halaman depan
return redirect('/produk');
}
Bengkel Revisi (Edit) dan Petugas Kebersihan (Delete)
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." - Martin Fowler
Kita butuh tiga rute baru di web.php: satu buat nampilin form edit, satu buat nyimpen perubahannya, dan satu lagi buat ngapus data. Tambahin script ini:
// Pintu form edit (butuh ID barang)
Route::get('/produk/{id}/edit', [ProductController::class, 'edit']);
// Pintu nyimpen data edit (PUT/PATCH)
Route::put('/produk/{id}', [ProductController::class, 'update']);
// Pintu ngapus data (DELETE)
Route::delete('/produk/{id}', [ProductController::class, 'destroy']);
public function edit($id)
{
$product = Product::findOrFail($id);
return view('products.edit', compact('product'));
}
public function update(Request $request, $id)
{
$request->validate([
'name' => 'required|max:255',
'description' => 'required',
'price' => 'required|numeric',
'stock' => 'required|numeric',
]);
$product = Product::findOrFail($id);
$product->update($request->all());
return redirect('/produk');
}
public function destroy($id)
{
$product = Product::findOrFail($id);
$product->delete();
return redirect('/produk');
}
Majang Thumbnail Produk & Ngejar Skor SEO
"Users experience the usability of a site before they have committed to using it and before they have spent any money on it." - Jakob Nielsen
Di index.blade.php, ane udah tambahin atribut alt buat deskripsi gambar (penting banget buat technical SEO) sama loading="lazy" biar website ente kagak ngos-ngosan pas load gambar.
@if($item->image)
<img src="{{ asset('images/' . $item->image) }}" alt="Thumbnail {{ $item->name }}" loading="lazy">
@else
<span>No Image</span>
@endif
Nyambungin Tombol Beli Langsung ke WhatsApp (WA Checkout)
"Success is not delivering a feature; success is learning how to solve the customer's problem." - Eric Ries
Ketimbang kita pusing bikin sistem Keranjang (Cart) sama Payment Gateway yang ribetnya minta ampun buat pemula, kita potong kompas aja pake metode WhatsApp Checkout.
@php
$nomorWA = "6281234567890";
$teksPesan = "Halo Admin Toko, ane mau order barang ini dong:\n\n";
$teksPesan .= "Nama Barang: " . $item->name . "\n";
$teksPesan .= "Harga: Rp " . number_format($item->price, 0, ',', '.') . "\n\n";
$teksPesan .= "Kira-kira sisa stok yang " . $item->stock . " biji itu masih ready kagak?";
$linkBeli = "https://wa.me/" . $nomorWA . "?text=" . urlencode($teksPesan);
@endphp
<a href="{{ $linkBeli }}" target="_blank">🛒 Beli via WhatsApp</a>
Misahin Pintu Admin & Pasang Gembok (Middleware)
"SEO is only not seen as a rocket science by those who do not know it." - Danny Sullivan
Kita bakal bikin halaman depan khusus buat pembeli (bersih dari tombol edit/hapus), trus mindahin akses gudang (CRUD) ke URL khusus yang digembok (auth.basic).
// 1. PINTU DEPAN BUAT PEMBELI (Cuma bisa liat barang)
Route::get('/', [ProductController::class, 'katalogUtama']);
// 2. PINTU BELAKANG BUAT ADMIN (Bisa nambah, ngedit, ngapus - udah digembok!)
Route::prefix('admin')->middleware('auth.basic')->group(function () {
// Taruh route CRUD disini...
});
Bikin Kolom Pencarian Biar Pembeli Kagak Nyasar
"Findability precedes usability. In the alphabet and on the Web. You can't use what you can't find." - Peter Morville
Di Controller, kita modif sedikit logika buat nangkep pencarian pake perintah LIKE.
if ($request->has('cari') && $request->cari != '') {
$katakunci = $request->cari;
$products = Product::where('name', 'like', "%" . $katakunci . "%")
->orWhere('description', 'like', "%" . $katakunci . "%")
->get();
}
Meroket ke Internet (Hosting & Deploy)
"The only purpose of starting is to finish, and while the projects we do are never really finished, they must ship." - Seth Godin
Ini bagian paling krusial buat technical SEO dan keamanan pas di cPanel. File
.env ini nyimpen rahasia dapur kita. Pastiin APP_DEBUG=false wajib hukumnya biar kalo ada error, pengunjung cuma liat layar "500 Server Error" biasa!
Akhirnya kelar juga series panjang kita ini. Ente udah resmi jadi developer e-commerce sekarang. Selamat berkarya, dan semoga website barunya ngacir mendatangkan cuan berkah!
🧐 FAQ Seputar E-Commerce Laravel & Hosting
Q1: Udah sukses di-hosting ke cPanel, tapi pas dibuka webnya malah muncul "500 Server Error". Kenapa tuh?
Ini penyakit sejuta umat pas baru deploy. Biasanya ada dua biang keroknya: Pertama, ente lupa nyesuain isi file .env (DB_DATABASE, DB_USERNAME, DB_PASSWORD). Kedua, file .sql dari XAMPP belom di-import ke phpMyAdmin cPanel, jadi Laravel kebingungan nyari tabelnya.
Q2: Bang, pas ane upload thumbnail produk, kok error dan formnya malah refresh doang?
Coba intip tag <form> ente. Kalo kelupaan naruh enctype="multipart/form-data", file gambarnya kagak bakal kekirim ke server. Itu syarat mutlak fardhu ain!
Q3: Ane biasa ngulik template Blogger demi PageSpeed 100. Di Laravel ini gimana trik SEO technical-nya?
Ente bisa jalanin perintah php artisan optimize di terminal server buat nge-cache routing dan config. Pastiin juga thumbnail udah di-compress dan pake atribut loading="lazy".
Q4: Kenapa pas ngedit CSS/HTML di hosting, perubahannya kagak langsung nongol?
Ini biasanya gara-gara cache browser. Teken Ctrl + F5 buat *hard refresh*. Kalo di Laravel-nya, ketik php artisan view:clear di terminal cPanel.