DEV Community

ahmadasroni38
ahmadasroni38

Posted on

Tutorial Lengkap: Membuat Aplikasi Laravel 10 dengan CRUD dan Basic Authentication

Daftar Isi

  1. Pendahuluan
  2. Requirement & Persiapan
  3. Instalasi Laravel 10
  4. Konfigurasi Database
  5. Setup Authentication
  6. Membuat Fitur CRUD
  7. Testing Aplikasi
  8. Kesimpulan

Pendahuluan

Dalam tutorial ini, kita akan membuat aplikasi web sederhana menggunakan Laravel 10 yang dilengkapi dengan:

  • Basic Authentication: Register, Login, Logout
  • CRUD Operations: Create, Read, Update, Delete untuk manajemen produk
  • User Protection: Hanya user yang login yang bisa mengakses fitur CRUD

Aplikasi ini adalah MVP (Minimum Viable Product) yang cocok untuk dipelajari pemula dan bisa dikembangkan lebih lanjut.


Requirement & Persiapan

Sistem Requirements

  • PHP >= 8.1
  • Composer
  • MySQL/MariaDB atau database lainnya
  • Node.js & NPM (untuk frontend assets)

Cek Versi PHP

php -v
Enter fullscreen mode Exit fullscreen mode

Cek Composer

composer --version
Enter fullscreen mode Exit fullscreen mode

Instalasi Laravel 10

1. Buat Project Baru

composer create-project laravel/laravel:^10.0 laravel-crud-app
cd laravel-crud-app
Enter fullscreen mode Exit fullscreen mode

2. Jalankan Development Server

php artisan serve
Enter fullscreen mode Exit fullscreen mode

Buka browser dan akses http://localhost:8000 untuk memastikan Laravel terinstall dengan benar.


Konfigurasi Database

1. Buat Database

Buat database baru di MySQL:

CREATE DATABASE laravel_crud_db;
Enter fullscreen mode Exit fullscreen mode

2. Setup File .env

Edit file .env di root project:

APP_NAME="Laravel CRUD App"
APP_ENV=local
APP_KEY=base64:xxxxx
APP_DEBUG=true
APP_URL=http://localhost:8000

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_crud_db
DB_USERNAME=root
DB_PASSWORD=
Enter fullscreen mode Exit fullscreen mode

Catatan: Sesuaikan DB_USERNAME dan DB_PASSWORD dengan konfigurasi MySQL Anda.

3. Test Koneksi Database

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Jika berhasil, tabel-tabel default Laravel akan dibuat.


Setup Authentication

Laravel 10 menyediakan beberapa cara untuk setup authentication. Kita akan menggunakan Laravel Breeze karena paling sederhana dan cocok untuk MVP.

1. Install Laravel Breeze

composer require laravel/breeze --dev
Enter fullscreen mode Exit fullscreen mode

2. Install Breeze Scaffolding

php artisan breeze:install blade
Enter fullscreen mode Exit fullscreen mode

Pilih opsi:

  • Blade (untuk templating tradisional)
  • Dark mode: Optional (No)
  • PHPUnit: Yes

3. Install Dependencies & Build Assets

npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

Buka terminal baru dan jalankan:

php artisan serve
Enter fullscreen mode Exit fullscreen mode

4. Jalankan Migrasi

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

5. Test Authentication

Akses http://localhost:8000/register untuk register user baru, kemudian login di http://localhost:8000/login.


Membuat Fitur CRUD

Kita akan membuat fitur CRUD untuk mengelola Products (Produk).

1. Buat Model, Migration, dan Controller

php artisan make:model Product -mcr
Enter fullscreen mode Exit fullscreen mode

Penjelasan flags:

  • -m: Membuat migration
  • -c: Membuat controller
  • -r: Membuat resource controller (dengan method CRUD lengkap)

2. Setup Migration untuk Products

Edit file database/migrations/xxxx_create_products_table.php:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->string('name');
            $table->text('description')->nullable();
            $table->decimal('price', 10, 2);
            $table->integer('stock')->default(0);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};
Enter fullscreen mode Exit fullscreen mode

Jalankan migration:

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

3. Setup Model Product

Edit file app/Models/Product.php:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id',
        'name',
        'description',
        'price',
        'stock',
    ];

    // Relasi dengan User
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Update User Model

Edit app/Models/User.php dan tambahkan relasi:

// Tambahkan di dalam class User
public function products()
{
    return $this->hasMany(Product::class);
}
Enter fullscreen mode Exit fullscreen mode

5. Setup ProductController

Edit file app/Http/Controllers/ProductController.php:

<?php

namespace App\Http\Controllers;

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

class ProductController extends Controller
{
    // Tampilkan semua produk
    public function index()
    {
        $products = Product::where('user_id', auth()->id())
                           ->latest()
                           ->paginate(10);

        return view('products.index', compact('products'));
    }

    // Form tambah produk
    public function create()
    {
        return view('products.create');
    }

    // Simpan produk baru
    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => 'required|max:255',
            'description' => 'nullable',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0',
        ]);

        auth()->user()->products()->create($validated);

        return redirect()->route('products.index')
                        ->with('success', 'Produk berhasil ditambahkan!');
    }

    // Tampilkan detail produk
    public function show(Product $product)
    {
        // Pastikan user hanya bisa lihat produknya sendiri
        if ($product->user_id !== auth()->id()) {
            abort(403);
        }

        return view('products.show', compact('product'));
    }

    // Form edit produk
    public function edit(Product $product)
    {
        // Pastikan user hanya bisa edit produknya sendiri
        if ($product->user_id !== auth()->id()) {
            abort(403);
        }

        return view('products.edit', compact('product'));
    }

    // Update produk
    public function update(Request $request, Product $product)
    {
        // Pastikan user hanya bisa update produknya sendiri
        if ($product->user_id !== auth()->id()) {
            abort(403);
        }

        $validated = $request->validate([
            'name' => 'required|max:255',
            'description' => 'nullable',
            'price' => 'required|numeric|min:0',
            'stock' => 'required|integer|min:0',
        ]);

        $product->update($validated);

        return redirect()->route('products.index')
                        ->with('success', 'Produk berhasil diupdate!');
    }

    // Hapus produk
    public function destroy(Product $product)
    {
        // Pastikan user hanya bisa hapus produknya sendiri
        if ($product->user_id !== auth()->id()) {
            abort(403);
        }

        $product->delete();

        return redirect()->route('products.index')
                        ->with('success', 'Produk berhasil dihapus!');
    }
}
Enter fullscreen mode Exit fullscreen mode

6. Setup Routes

Edit file routes/web.php:

<?php

use App\Http\Controllers\ProfileController;
use App\Http\Controllers\ProductController;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Route::middleware('auth')->group(function () {
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');

    // Routes untuk Products CRUD
    Route::resource('products', ProductController::class);
});

require __DIR__.'/auth.php';
Enter fullscreen mode Exit fullscreen mode

7. Buat Views untuk CRUD

a. Layout Navigation (Update)

Edit file resources/views/layouts/navigation.blade.php dan tambahkan menu Products:

<!-- Tambahkan setelah menu Dashboard -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
    <x-nav-link :href="route('products.index')" :active="request()->routeIs('products.*')">
        {{ __('Products') }}
    </x-nav-link>
</div>
Enter fullscreen mode Exit fullscreen mode

b. Index View (List Products)

Buat file resources/views/products/index.blade.php:

<x-app-layout>
    <x-slot name="header">
        <div class="flex justify-between">
            <h2 class="font-semibold text-xl text-gray-800 leading-tight">
                {{ __('Daftar Produk') }}
            </h2>
            <a href="{{ route('products.create') }}" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                Tambah Produk
            </a>
        </div>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            @if (session('success'))
                <div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4">
                    {{ session('success') }}
                </div>
            @endif

            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 text-gray-900">
                    @if($products->count() > 0)
                        <table class="min-w-full divide-y divide-gray-200">
                            <thead class="bg-gray-50">
                                <tr>
                                    <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Nama</th>
                                    <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Harga</th>
                                    <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Stok</th>
                                    <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Aksi</th>
                                </tr>
                            </thead>
                            <tbody class="bg-white divide-y divide-gray-200">
                                @foreach($products as $product)
                                    <tr>
                                        <td class="px-6 py-4 whitespace-nowrap">{{ $product->name }}</td>
                                        <td class="px-6 py-4 whitespace-nowrap">Rp {{ number_format($product->price, 0, ',', '.') }}</td>
                                        <td class="px-6 py-4 whitespace-nowrap">{{ $product->stock }}</td>
                                        <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
                                            <a href="{{ route('products.show', $product) }}" class="text-blue-600 hover:text-blue-900 mr-3">Lihat</a>
                                            <a href="{{ route('products.edit', $product) }}" class="text-indigo-600 hover:text-indigo-900 mr-3">Edit</a>
                                            <form action="{{ route('products.destroy', $product) }}" method="POST" class="inline">
                                                @csrf
                                                @method('DELETE')
                                                <button type="submit" class="text-red-600 hover:text-red-900" onclick="return confirm('Yakin ingin menghapus?')">Hapus</button>
                                            </form>
                                        </td>
                                    </tr>
                                @endforeach
                            </tbody>
                        </table>

                        <div class="mt-4">
                            {{ $products->links() }}
                        </div>
                    @else
                        <p class="text-gray-500">Belum ada produk. <a href="{{ route('products.create') }}" class="text-blue-600 hover:text-blue-800">Tambah produk pertama</a></p>
                    @endif
                </div>
            </div>
        </div>
    </div>
</x-app-layout>
Enter fullscreen mode Exit fullscreen mode

c. Create View (Form Tambah)

Buat file resources/views/products/create.blade.php:

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Tambah Produk Baru') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 text-gray-900">
                    <form method="POST" action="{{ route('products.store') }}">
                        @csrf

                        <!-- Name -->
                        <div class="mb-4">
                            <label for="name" class="block text-gray-700 text-sm font-bold mb-2">Nama Produk</label>
                            <input type="text" name="name" id="name" value="{{ old('name') }}" 
                                   class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline @error('name') border-red-500 @enderror">
                            @error('name')
                                <p class="text-red-500 text-xs italic mt-1">{{ $message }}</p>
                            @enderror
                        </div>

                        <!-- Description -->
                        <div class="mb-4">
                            <label for="description" class="block text-gray-700 text-sm font-bold mb-2">Deskripsi</label>
                            <textarea name="description" id="description" rows="4" 
                                      class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline @error('description') border-red-500 @enderror">{{ old('description') }}</textarea>
                            @error('description')
                                <p class="text-red-500 text-xs italic mt-1">{{ $message }}</p>
                            @enderror
                        </div>

                        <!-- Price -->
                        <div class="mb-4">
                            <label for="price" class="block text-gray-700 text-sm font-bold mb-2">Harga</label>
                            <input type="number" step="0.01" name="price" id="price" value="{{ old('price') }}" 
                                   class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline @error('price') border-red-500 @enderror">
                            @error('price')
                                <p class="text-red-500 text-xs italic mt-1">{{ $message }}</p>
                            @enderror
                        </div>

                        <!-- Stock -->
                        <div class="mb-6">
                            <label for="stock" class="block text-gray-700 text-sm font-bold mb-2">Stok</label>
                            <input type="number" name="stock" id="stock" value="{{ old('stock') }}" 
                                   class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline @error('stock') border-red-500 @enderror">
                            @error('stock')
                                <p class="text-red-500 text-xs italic mt-1">{{ $message }}</p>
                            @enderror
                        </div>

                        <div class="flex items-center justify-between">
                            <button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
                                Simpan Produk
                            </button>
                            <a href="{{ route('products.index') }}" class="text-gray-600 hover:text-gray-800">
                                Batal
                            </a>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>
Enter fullscreen mode Exit fullscreen mode

d. Edit View (Form Edit)

Buat file resources/views/products/edit.blade.php:

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Edit Produk') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 text-gray-900">
                    <form method="POST" action="{{ route('products.update', $product) }}">
                        @csrf
                        @method('PUT')

                        <!-- Name -->
                        <div class="mb-4">
                            <label for="name" class="block text-gray-700 text-sm font-bold mb-2">Nama Produk</label>
                            <input type="text" name="name" id="name" value="{{ old('name', $product->name) }}" 
                                   class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline @error('name') border-red-500 @enderror">
                            @error('name')
                                <p class="text-red-500 text-xs italic mt-1">{{ $message }}</p>
                            @enderror
                        </div>

                        <!-- Description -->
                        <div class="mb-4">
                            <label for="description" class="block text-gray-700 text-sm font-bold mb-2">Deskripsi</label>
                            <textarea name="description" id="description" rows="4" 
                                      class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline @error('description') border-red-500 @enderror">{{ old('description', $product->description) }}</textarea>
                            @error('description')
                                <p class="text-red-500 text-xs italic mt-1">{{ $message }}</p>
                            @enderror
                        </div>

                        <!-- Price -->
                        <div class="mb-4">
                            <label for="price" class="block text-gray-700 text-sm font-bold mb-2">Harga</label>
                            <input type="number" step="0.01" name="price" id="price" value="{{ old('price', $product->price) }}" 
                                   class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline @error('price') border-red-500 @enderror">
                            @error('price')
                                <p class="text-red-500 text-xs italic mt-1">{{ $message }}</p>
                            @enderror
                        </div>

                        <!-- Stock -->
                        <div class="mb-6">
                            <label for="stock" class="block text-gray-700 text-sm font-bold mb-2">Stok</label>
                            <input type="number" name="stock" id="stock" value="{{ old('stock', $product->stock) }}" 
                                   class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline @error('stock') border-red-500 @enderror">
                            @error('stock')
                                <p class="text-red-500 text-xs italic mt-1">{{ $message }}</p>
                            @enderror
                        </div>

                        <div class="flex items-center justify-between">
                            <button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
                                Update Produk
                            </button>
                            <a href="{{ route('products.index') }}" class="text-gray-600 hover:text-gray-800">
                                Batal
                            </a>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>
Enter fullscreen mode Exit fullscreen mode

e. Show View (Detail Produk)

Buat file resources/views/products/show.blade.php:

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Detail Produk') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 text-gray-900">
                    <div class="mb-6">
                        <h3 class="text-2xl font-bold mb-4">{{ $product->name }}</h3>

                        <div class="grid grid-cols-2 gap-4 mb-4">
                            <div>
                                <p class="text-gray-600 font-semibold">Harga:</p>
                                <p class="text-xl">Rp {{ number_format($product->price, 0, ',', '.') }}</p>
                            </div>
                            <div>
                                <p class="text-gray-600 font-semibold">Stok:</p>
                                <p class="text-xl">{{ $product->stock }} unit</p>
                            </div>
                        </div>

                        @if($product->description)
                            <div class="mb-4">
                                <p class="text-gray-600 font-semibold">Deskripsi:</p>
                                <p class="mt-2">{{ $product->description }}</p>
                            </div>
                        @endif

                        <div class="text-sm text-gray-500 mb-4">
                            <p>Dibuat: {{ $product->created_at->format('d M Y H:i') }}</p>
                            <p>Terakhir diupdate: {{ $product->updated_at->format('d M Y H:i') }}</p>
                        </div>
                    </div>

                    <div class="flex space-x-4">
                        <a href="{{ route('products.edit', $product) }}" class="bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded">
                            Edit
                        </a>
                        <a href="{{ route('products.index') }}" class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">
                            Kembali
                        </a>
                        <form action="{{ route('products.destroy', $product) }}" method="POST" class="inline">
                            @csrf
                            @method('DELETE')
                            <button type="submit" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" onclick="return confirm('Yakin ingin menghapus produk ini?')">
                                Hapus
                            </button>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>
Enter fullscreen mode Exit fullscreen mode

Testing Aplikasi

1. Jalankan Server

Pastikan server development berjalan:

php artisan serve
Enter fullscreen mode Exit fullscreen mode

Dan di terminal lain (untuk compile assets):

npm run dev
Enter fullscreen mode Exit fullscreen mode

2. Flow Testing

A. Test Authentication

  1. Buka http://localhost:8000/register
  2. Daftar dengan email dan password
  3. Login ke aplikasi
  4. Pastikan redirect ke dashboard

B. Test CRUD Operations

CREATE (Tambah Produk)

  1. Klik menu "Products"
  2. Klik tombol "Tambah Produk"
  3. Isi form:
    • Nama: "Laptop ASUS"
    • Deskripsi: "Laptop gaming dengan spesifikasi tinggi"
    • Harga: 15000000
    • Stok: 10
  4. Klik "Simpan Produk"
  5. Pastikan muncul pesan sukses dan produk tampil di list

READ (Lihat Produk)

  1. Di halaman Products, klik "Lihat" pada salah satu produk
  2. Pastikan detail produk tampil dengan benar

UPDATE (Edit Produk)

  1. Di halaman Products, klik "Edit" pada produk
  2. Ubah data (misalnya ubah harga atau stok)
  3. Klik "Update Produk"
  4. Pastikan perubahan tersimpan

DELETE (Hapus Produk)

  1. Di halaman Products, klik "Hapus" pada produk
  2. Konfirmasi penghapusan
  3. Pastikan produk terhapus dari list

C. Test Security

  1. Logout dari aplikasi
  2. Coba akses http://localhost:8000/products langsung
  3. Pastikan di-redirect ke halaman login (protection berfungsi)

3. Test dengan Data Banyak

Untuk testing dengan data banyak, buat seeder:

php artisan make:seeder ProductSeeder
Enter fullscreen mode Exit fullscreen mode

Edit database/seeders/ProductSeeder.php:

<?php

namespace Database\Seeders;

use App\Models\Product;
use App\Models\User;
use Illuminate\Database\Seeder;

class ProductSeeder extends Seeder
{
    public function run(): void
    {
        $user = User::first(); // Ambil user pertama

        if ($user) {
            $products = [
                ['name' => 'Laptop', 'price' => 15000000, 'stock' => 5],
                ['name' => 'Mouse Gaming', 'price' => 250000, 'stock' => 20],
                ['name' => 'Keyboard Mechanical', 'price' => 800000, 'stock' => 15],
                ['name' => 'Monitor 24"', 'price' => 2500000, 'stock' => 8],
                ['name' => 'Headset', 'price' => 350000, 'stock' => 30],
            ];

            foreach ($products as $product) {
                Product::create([
                    'user_id' => $user->id,
                    'name' => $product['name'],
                    'description' => 'Deskripsi untuk ' . $product['name'],
                    'price' => $product['price'],
                    'stock' => $product['stock'],
                ]);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Jalankan seeder:

php artisan db:seed --class=ProductSeeder
Enter fullscreen mode Exit fullscreen mode

Kesimpulan

Selamat! Anda telah berhasil membuat aplikasi Laravel 10 dengan fitur:

โœ… Fitur yang Telah Dibuat

  1. Authentication System

    • Register user baru
    • Login & Logout
    • Protected routes
  2. CRUD Operations

    • Create: Menambah produk baru
    • Read: Melihat list dan detail produk
    • Update: Mengubah data produk
    • Delete: Menghapus produk
  3. Security Features

    • Middleware authentication
    • User-specific data (user hanya bisa kelola produknya sendiri)
    • Form validation
    • CSRF protection

๐Ÿš€ Pengembangan Lebih Lanjut

Aplikasi ini bisa dikembangkan dengan:

  1. Upload Gambar Produk
php artisan storage:link
Enter fullscreen mode Exit fullscreen mode

Tambahkan field image di migration dan form

  1. Search & Filter
    Tambahkan form pencarian di halaman index

  2. Kategori Produk
    Buat model Category dan relasi dengan Product

  3. Export Data
    Implementasi export ke Excel/PDF menggunakan package seperti Laravel Excel

  4. API
    Buat API endpoints untuk mobile app

  5. Roles & Permissions
    Implementasi role (Admin, User) menggunakan package Spatie Permission

  6. Soft Deletes
    Tambahkan fitur soft delete untuk recovery data

  7. Image Upload
    Implementasi upload gambar produk

๐Ÿ“š Sumber Belajar Lanjutan

๐Ÿ’ก Tips Development

  1. Gunakan Git untuk version control
  2. Tulis tests untuk fitur-fitur penting
  3. Gunakan .env untuk konfigurasi berbeda di development/production
  4. Buat backup database secara rutin
  5. Follow Laravel best practices

Troubleshooting

Error: Class not found

composer dump-autoload
Enter fullscreen mode Exit fullscreen mode

Error: Permission denied

chmod -R 775 storage bootstrap/cache
Enter fullscreen mode Exit fullscreen mode

Error: Mix manifest not found

npm install && npm run dev
Enter fullscreen mode Exit fullscreen mode

Error: Database connection

  • Pastikan MySQL service running
  • Cek kredensial di file .env
  • Test koneksi: php artisan tinker kemudian DB::connection()->getPdo();

Happy Coding! ๐Ÿš€

Top comments (0)