Pada tutorial kali ini, kita akan membuat halaman checkout. Untuk migration, kita sebelumnya sudah buat dengan nama tabel orders dan order_items. Kita akan menggunakan kedua tabel tersebut untuk kita gunakan di halaman checkout ini.
Langkah 1: Membuat Model Order dan OrderItem
Buat model untuk Order dan OrderItem agar kita dapat mengelola data checkout lebih mudah.
1.1 Order Model
Buat file model Order.php:
php artisan make:model Order
Tambahkan relasi ke model Order seperti berikut:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
protected $fillable = [
'user_id', 'total', 'status','payment_status'
];
public function items()
{
return $this->hasMany(OrderItem::class);
}
}
1.2 OrderItem Model
Buat file model OrderItem.php:
php artisan make:model OrderItem
Tambahkan relasi di model OrderItem seperti berikut:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class OrderItem extends Model
{
protected $fillable = [
'order_id', 'product_id', 'quantity', 'price'
];
public function order()
{
return $this->belongsTo(Order::class);
}
public function product()
{
return $this->belongsTo(Product::class);
}
}
Langkah 2. Menambahkan tabel Address.
Buat Tabel addresses untuk Menyimpan Banyak Alamat per User
Jika pengguna dapat memiliki lebih dari satu alamat (misalnya, alamat rumah dan kantor), buat tabel addresses. Jalankan perintah berikut untuk membuat tabel baru:
php artisan make:migration create_addresses_table
Edit File Migration
Tambahkan struktur tabel addresses seperti berikut:
public function up()
{
Schema::create('addresses', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade'); // Relasi ke users
$table->string('type')->default('billing'); // billing atau shipping
$table->text('address');
$table->string('city');
$table->string('state');
$table->string('zip');
$table->string('country');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('addresses');
}
Jalankan Migration
Eksekusi migration:
php artisan migrate
Buat Model Address.
Buat model address.php dengan melakukan ketik cmd berikut:
php artisan make:model Address
Tambahkan Relasi di Model
Di User.php, tambahkan relasi ke tabel addresses:
public function addresses()
{
return $this->hasMany(Address::class);
}
Di model Address.php, tambahkan relasi ke pengguna:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Address extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'type',
'address',
'city',
'state',
'zip',
'country',
];
public function user()
{
return $this->belongsTo(User::class);
}
public function orders()
{
return $this->hasMany(Order::class);
}
}
Buat Migration untuk Menambahkan Kolom address_id di kolom users
php artisan make:migration add_address_id_to_users_table --table=users
Buka file migration yang baru saja dibuat di direktori database/migrations. Tambahkan kolom address_id seperti berikut:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddAddressIdToUsersTable extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->unsignedBigInteger('address_id')->nullable()->after('id');
$table->foreign('address_id')->references('id')->on('addresses')->onDelete('set null');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropForeign(['address_id']);
$table->dropColumn('address_id');
});
}
}
Jalankan Migration
Setelah migration selesai diedit, jalankan perintah berikut untuk mengaplikasikan perubahan ke database:
php artisan migrate
Langkah 3: Membuat CheckoutController
Buat CheckoutController untuk mengelola logika checkout:
php artisan make:controller CheckoutController
Tambahkan metode showCheckoutForm dan processCheckout di CheckoutController:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Address;
use App\Models\Product;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use App\Models\Category;
class CheckoutController extends Controller
{
public function showCheckoutForm()
{
$categories = Category::all();
$cart = session()->get('cart', []);
$total = array_reduce($cart, function ($sum, $item) {
return $sum + ($item['price'] * $item['quantity']);
}, 0);
return view('frontend.checkout', compact('cart', 'total','categories'));
}
public function processCheckout(Request $request)
{
$request->validate([
'address' => 'required|string',
'city' => 'required|string',
'state' => 'required|string',
'zip' => 'required|string',
'country' => 'required|string',
]);
$cart = session()->get('cart', []);
if (empty($cart)) {
return redirect()->route('cart.index')->withErrors('Cart is empty');
}
$totalAmount = array_reduce($cart, function ($sum, $item) {
return $sum + ($item['price'] * $item['quantity']);
}, 0);
Address::create([
'user_id' => Auth::id(),
'type' => 'billing', // Bisa ditambah pengaturan untuk shipping
'address' => $request->address,
'city' => $request->city,
'state' => $request->state,
'zip' => $request->zip,
'country' => $request->country,
]);
$order = Order::create([
'user_id' => Auth::id(),
'total' => $totalAmount,
'status' => 'pending',
]);
foreach ($cart as $productId => $item) {
OrderItem::create([
'order_id' => $order->id,
'product_id' => $productId,
'quantity' => $item['quantity'],
'price' => $item['price'],
]);
}
session()->forget('cart');
return redirect()->route('checkout.success')->with('status', 'Order placed successfully');
}
public function successsCheckout(){
$categories = Category::all();
return view('frontend.checkout-success', compact('categories'));
}
}
Langkah 4: Menambahkan Routing
Di dalam file routes/web.php, tambahkan routing untuk menampilkan form checkout dan memproses pesanan:
Route::get('/checkout', [CheckoutController::class, 'showCheckoutForm'])->middleware('auth')->name('checkout.form');
Route::post('/checkout', [CheckoutController::class, 'processCheckout'])->middleware('auth')->name('checkout.process');
Route::get('/checkout/success', [CheckoutController::class, 'successsCheckout'])->middleware('auth')->name('checkout.success');
Langkah 5: Membuat Tampilan (Views)
Buat view untuk halaman checkout, yaitu checkout.blade.php dan checkout-success.blade.php.
5.1 View checkout.blade.php
Buat file resources/views/frontend/checkout.blade.php:
@extends('layouts.frontend')
@section('content')
<div class="container">
<h2>Checkout</h2>
<form action="{{ route('checkout.process') }}" method="POST">
@csrf
<!-- Address Fields -->
<h4>Billing Address</h4>
<div class="mb-3">
<label for="address" class="form-label">Address</label>
<textarea class="form-control" name="address" id="address" rows="3" required>{{ old('address') }}</textarea>
</div>
<div class="mb-3">
<label for="city" class="form-label">City</label>
<input type="text" class="form-control" name="city" id="city" value="{{ old('city') }}" required>
</div>
<div class="mb-3">
<label for="state" class="form-label">State</label>
<input type="text" class="form-control" name="state" id="state" value="{{ old('state') }}" required>
</div>
<div class="mb-3">
<label for="zip" class="form-label">ZIP Code</label>
<input type="text" class="form-control" name="zip" id="zip" value="{{ old('zip') }}" required>
</div>
<div class="mb-3">
<label for="country" class="form-label">Country</label>
<input type="text" class="form-control" name="country" id="country" value="{{ old('country') }}" required>
</div>
<h4>Order Summary</h4>
<ul>
@foreach ($cart as $item)
<li>{{ $item['name'] }} - {{ $item['quantity'] }} x ${{ $item['price'] }}</li>
@endforeach
</ul>
<p><strong>Total: </strong> ${{ number_format($total, 2) }}</p>
<button type="submit" class="btn btn-primary btn-space">Place Order</button>
</form>
</div>
@endsection
5.2 View checkout-success.blade.php
Buat file resources/views/frontend/checkout-success.blade.php untuk menampilkan pesan sukses:
@extends('layouts.frontend')
@section('content')
<div class="container">
<h2>Thank you for your order!</h2>
<p>Your order has been placed successfully.</p>
<a href="{{ route('home') }}" class="btn btn-primary btn-space">Continue Shopping</a>
</div>
@endsection
Langkah 6: Menyempurnakan Desain Halaman Checkout
Tambahkan CSS di dalam file public/css/app.css atau resources/css/app.css untuk memperindah halaman checkout:
h2 {
text-align: center;
margin-bottom: 20px;
}
form .form-group {
margin-bottom: 15px;
}
form .form-group label {
font-weight: bold;
}
button.btn-primary {
width: 100%;
margin-top: 20px;
}
/* Berikan margin bawah pada tombol */
.btn-space {
margin-bottom: 20px; /* Sesuaikan dengan kebutuhan */
}
/* Berikan jarak lebih di bagian bawah konten checkout */
.checkout-content {
margin-bottom: 40px; /* Sesuaikan sesuai kebutuhan */
}
Langkah 7. Edit views cart
Tambahkan tombol botton agar bisa ke halaman checkout dengan merevisi resources/views/frontend/cart.blade.php berikut:
@extends('layouts.frontend')
@section('content')
<div class="container">
<h2>Your Cart</h2>
@if (session('cart') && count(session('cart')) > 0)
<table class="table">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@foreach (session('cart') as $id => $details)
<tr>
<td>{{ $details['name'] }}</td>
<td>${{ number_format($details['price'], 2) }}</td>
<td>{{ $details['quantity'] }}</td>
<td>${{ number_format($details['price'] * $details['quantity'], 2) }}</td>
<td>
<form action="{{ route('cart.remove', $id) }}" method="POST">
@csrf
<button type="submit" class="btn btn-danger btn-sm">Remove</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
<!-- Display Total -->
<div class="d-flex justify-content-between align-items-center mt-4">
<h4>Total: ${{ number_format(array_reduce(session('cart'), function ($total, $item) {
return $total + ($item['price'] * $item['quantity']);
}, 0), 2) }}</h4>
<!-- Checkout Button -->
<a href="{{ route('checkout.form') }}" class="btn btn-primary btn-space">Proceed to Checkout</a>
</div>
@else
<p>Your cart is empty.</p>
@endif
</div>
@endsection
Langkah 8: Testing dan Verifikasi
Akses halaman cart lalu klik checkout di /checkout.
Isi form checkout, lalu klik Place Order.