Di langkah ini, kita akan berfokus pada Halaman Depan E-Commerce (Homepage) untuk menampilkan produk kepada pengunjung. 

Langkah 1: Route & Controller untuk Halaman Depan

Buat route untuk homepage di routes/web.php. Karena ini halaman depan, biasanya kita letakkan di root /:

use App\Http\Controllers\FrontendController;

Route::get('/', [FrontendController::class, 'index'])->name('home');

Buat controller FrontendController jika belum ada:

php artisan make:controller FrontendController

Di app/Http/Controllers/FrontendController.php isi sebagai berikut:

<?php

namespace App\Http\Controllers;

use App\Models\Product;
use App\Models\Category;
use Illuminate\Http\Request;
use Inertia\Inertia;

class FrontendController extends Controller
{
    public function index(Request $request)
    {
        // Ambil parameter search & filter kategori
        $search = $request->input('search');
        $categoryId = $request->input('category_id');

        $query = Product::with('images', 'category');

        if ($search) {
            $query->where('name', 'like', '%' . $search . '%');
        }

        if ($categoryId) {
            $query->where('category_id', $categoryId);
        }

        $products = $query->latest()->paginate(12)->withQueryString();

        $categories = Category::all(['id','name']);

        return Inertia::render('Frontend/Homepage', [
            'products' => $products,
            'categories' => $categories,
            'filters' => [
                'search' => $search,
                'category_id' => $categoryId,
            ]
        ]);
    }
}

Penjelasan:

  • Kita mengambil $search dan $categoryId dari query string untuk fitur pencarian dan filter kategori.
  • Query produk di-filter sesuai parameter tersebut.
  • withQueryString() agar pagination mempertahankan parameter search & category saat berganti halaman.
  • Mengirim categories untuk menampilkan dropdown kategori.
  • Mengirim filters untuk mempertahankan nilai form search dan dropdown filter di frontend.

Langkah 2: Komponen Frontend (Vue.js - Homepage)

Buat file resources/js/Pages/Frontend/Homepage.vue:

<template>
  <div class="min-h-screen flex flex-col">
    <!-- NAVBAR -->
    <header class="bg-white shadow z-10">
      <div class="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
        <div class="text-2xl font-bold text-gray-800">MyShop</div>
        <nav class="flex items-center space-x-6">
          <Link href="/" class="text-gray-700 hover:text-gray-900 font-medium px-3 py-2 transition-colors">
            Home
          </Link>
          <Link href="/categories" class="text-gray-700 hover:text-gray-900 font-medium px-3 py-2 transition-colors">
            Categories
          </Link>
          <Link href="/about" class="text-gray-700 hover:text-gray-900 font-medium px-3 py-2 transition-colors">
            About
          </Link>
        </nav>
      </div>
    </header>

    <!-- HERO SECTION -->
    <section
      class="relative bg-cover bg-center flex items-center justify-center text-center"
      style="background-image: url('https://images.unsplash.com/photo-1542831371-29b0f74f9713?ixlib=rb-4.0.3&q=80&fm=jpg&crop=entropy&w=1200'); height: 60vh;"
    >
      <div class="bg-black bg-opacity-50 w-full h-full absolute top-0 left-0"></div>
      <div class="relative z-10 max-w-2xl mx-auto px-4 text-white">
        <h1 class="text-4xl md:text-5xl font-bold mb-4">Welcome to MyShop</h1>
        <p class="text-lg md:text-xl mb-6">Find the best products for your daily life</p>
        <form @submit.prevent="applyFilter" class="flex items-center max-w-md mx-auto">
          <input
            v-model="form.search"
            type="text"
            placeholder="Search products..."
            class="border rounded-l px-3 py-2 w-full"
          />
          <button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-r hover:bg-blue-700">Search</button>
        </form>
      </div>
    </section>

    <!-- MAIN CONTENT -->
    <main class="flex-1 bg-gray-100 py-10">
      <div class="max-w-7xl mx-auto px-4">
        <!-- FILTER BAR -->
        <div class="flex items-center justify-between mb-6">
          <div class="flex space-x-4 items-center">
            <label class="block font-medium text-gray-700">Category:</label>
            <select v-model="form.category_id" @change="applyFilter" class="border px-3 py-2 rounded bg-white">
              <option value="">All</option>
              <option v-for="cat in categories" :key="cat.id" :value="cat.id">{{ cat.name }}</option>
            </select>
          </div>
        </div>
        <!-- PRODUCT GRID -->
        <div class="grid grid-cols-2 md:grid-cols-4 gap-6">
        <div
            v-for="product in products.data"
            :key="product.id"
            class="bg-white p-4 rounded shadow hover:shadow-md transition-shadow"
        >
            <Link :href="route('products.show', product.slug)" class="block relative pb-1/1 overflow-hidden rounded">
            <img
                v-if="product.images && product.images.length > 0"
                :src="`/storage/${product.images[0].image}`"
                alt="Product Image"
                class="w-full h-full object-cover rounded"
            />
            <div v-else class="absolute h-full w-full flex items-center justify-center text-gray-400">
                No Image
            </div>
            </Link>
            <Link :href="route('products.show', product.slug)">
            <h2 class="mt-2 font-semibold text-sm text-gray-800 line-clamp-2 hover:text-blue-600 transition-colors">{{ product.name }}</h2>
            </Link>
            <p class="text-xs text-gray-500">{{ product.category ? product.category.name : 'No Category' }}</p>
            <p class="text-red-600 font-bold mt-1 text-sm">Rp {{ product.price }}</p>
        </div>
        </div>

        <!-- PAGINATION -->
        <div class="mt-8 flex justify-center">
          <div class="inline-flex space-x-2">
            <div v-for="link in products.links" :key="link.url">
              <Link
                :href="link.url"
                v-html="link.label"
                :class="[
                  'px-3 py-1 border rounded text-sm',
                  link.active ? 'bg-blue-600 text-white border-blue-600' : 'bg-white text-gray-700 hover:bg-gray-100 border-gray-300'
                ]"
              ></Link>
            </div>
          </div>
        </div>
      </div>
    </main>

    <!-- FOOTER -->
    <footer class="bg-white py-4">
      <div class="max-w-7xl mx-auto px-4 text-center text-gray-600 text-sm">
        &copy; 2023 MyShop. All rights reserved.
      </div>
    </footer>
  </div>
</template>

<script>
import { Link, useForm } from '@inertiajs/inertia-vue3';

export default {
  props: {
    products: Object,
    categories: Array,
    filters: Object
  },
  components: { Link },
  setup(props) {
    const form = useForm({
      search: props.filters.search || '',
      category_id: props.filters.category_id || ''
    });

    const applyFilter = () => {
      form.get('/', { preserveScroll: true, replace: true });
    };

    return { form, applyFilter };
  },
};
</script>

<style>
.pb-1/1 {
  padding-bottom: 100%;
}
.line-clamp-2 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;  
  overflow: hidden;
}
</style>

Langkah 3: Testing Pencarian & Filter

Buka http://localhost:8000 Jika berhasil, akan seperti berikut:

homepage

Selamat Mencoba!!!. Silahkan tinggalkan komentar jika belum berhasil.