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.

Cart

Isi form checkout, lalu klik Place Order.

Checkout