Tutorial Laravel 10 - Part #10 - Membuat CRUD ke Single Table Database

Salah satu hal terpenting yang perlu dipelajari dalam membuat sebuah aplikasi adalah bagaimana kita mengelola data dalam database. Oleh karena itu pada tahap ini kita akan membahas tentang mengelola data dalam tabel database yaitu Create, Read, Update dan Delete (CRUD) di Laravel 10.

Sebelum kita memulai membahas tentang Membuat CRUD di Laravel, saya asumsikan bahwa Anda telah menginstall project laravel dari awal dan juga telah melakukan konfigurasi database. Jika belum, Anda dapat mengikuti Tutorial Instalasi Laravel 10 yang telah diposting sebelumnya.

Membuat Model dan Migration

Buat Migration, Model, dan Factory dengan satu perintah seperti berikut:

php artisan make:model Product -mf

Ubah file migration database/migrations/xxxxx_create_products_table.php menjadi seperti berikut:

<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('sku')->index();;
            $table->string('name');
            $table->decimal('price', 15, 2)->nullable();
            $table->integer('stock')->default(0);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};

Ubah Model Product (app/Models/Product) menjadi seperti berikut:

<?php

namespace App\Models;

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

class Product extends Model
{
    use HasFactory;


    protected $fillable = [
        'sku',
        'name',
        'price',
        'stock',
    ];
}

Ubah ProductFactory (database/factories/ProductFactory) menjadi seperti berikut:

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Product>
 */
class ProductFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'sku' => Str::random(10),
            'name' => fake()->name(),
            'price' => fake()->randomNumber(4),
            'stock' => fake()->randomNumber(2), 
        ];
    }
}

Selanjutnya, jalankan migrasi database dengan perintah:

php artisan migrate

Membuat Controller

Buat ProductController resource dengan perintah:

php artisan make:controller ProductController -r

Perintah di atas akan membuat file app/Http/Controllers/ProductController.php yang di dalamnya terdapat method: index, create, store, show, edit, update, dan destroy.

Membuat Route

Buat routing yang akan mengarahkan ke method-method yang ada pada ProductController:

<?php

use Illuminate\Support\Facades\Route;


use App\Http\Controllers\ProductController;


Route::get('products', [ProductController::class, 'index'])->name('products.index');
Route::get('products/create', [ProductController::class, 'create'])->name('products.create');
Route::post('products', [ProductController::class, 'store'])->name('products.store');
Route::get('products/{id}/edit', [ProductController::class, 'edit'])->name('products.edit');
Route::put('products/{id}', [ProductController::class, 'update'])->name('products.update');
Route::delete('products/{id}', [ProductController::class, 'destroy'])->name('products.destroy');

Membuat Validasi dengan Form Request

Disini kita membutuhkan validasi input saat store produk dan update produk. Pertama, kita akan membuat validasi dengan Form Request untuk StoreProductRequest dengan perintah:

php artisan make:request StoreProductRequest

Update file app/Http/Requests/StoreProductRequest.php menjadi seperti berikut:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreProductRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
     */
    public function rules(): array
    {
        return [
            'sku' => ['required', 'unique:products', 'max:100'],
            'name' => ['required', 'max:100'],
            'price' => ['required', 'numeric', 'min:1'],
            'stock' => ['required', 'numeric', 'min:0'],
        ];
    }
}

Kemudian, kita akan membuat UpdateProductRequest dengan perintah:

php artisan make:request UpdateProductRequest

Update file app/Http/Requests/UpdateProductRequest.php menjadi seperti berikut:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class UpdateProductRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
     */
    public function rules(): array
    {
        return [
            'sku' => [
                'required',
                'max:100',
                Rule::unique('products')->ignore($this->id),
            ],
            'name' => ['required', 'max:100'],
            'price' => ['required', 'numeric', 'min:1'],
            'stock' => ['required', 'numeric', 'min:0'],
        ];
    }
}

Pada validasi update produk ini ada sedikit perbedaan. Yaitu saat validasi unique untuk SKU. Jadi pada saat update, kita perlu menambahkan ignore terhadap id produk itu sendiri. Karena kalau tidak di ignore, maka akan mentog di validasi SKU harus unik, padahal SKU tersebut adalah milik dari produk yang sedang diedit.

Membuat Form Create Produk

Buat form sederhana untuk create produk dan simpan pada resources/views/products/create.blade.php:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>My App</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
  </head>
  <body>
    <div id="app">
      <div class="main-wrapper">
        <div class="main-content">
          <div class="container">
            <form method="post" action="{{ route('products.store') }}">
              @csrf
              <div class="card mt-5">
                <div class="card-header">
                  <h3>New Product</h3>
                </div>
                <div class="card-body">
                    @if ($errors->any())
                      <div class="alert alert-danger">
                        <div class="alert-title"><h4>Whoops!</h4></div>
                          There are some problems with your input.
                          <ul>
                            @foreach ($errors->all() as $error)
                              <li>{{ $error }}</li>
                            @endforeach
                          </ul>
                      </div> 
                    @endif

                    @if (session('success'))
                      <div class="alert alert-success">{{ session('success') }}</div>
                    @endif

                    @if (session('error'))
                      <div class="alert alert-danger">{{ session('error') }}</div>
                    @endif
                    <div class="mb-3">
                      <label class="form-label">SKU</label>
                      <input type="text" class="form-control" name="sku" value="{{ old('sku') }}" placeholder="#SKU">
                    </div>
                    <div class="mb-3">
                      <label class="form-label">Name</label>
                      <input type="text" class="form-control" name="name" value="{{ old('name') }}"  placeholder="Name">
                    </div>
                    <div class="mb-3">
                      <label class="form-label">Price</label>
                      <input type="text" class="form-control" name="price" value="{{ old('price') }}"  placeholder="Price">
                    </div>
                    <div class="mb-3">
                      <label class="form-label">Stock</label>
                      <input type="text" class="form-control" name="stock" value="{{ old('stock') }}"  placeholder="Stock">
                    </div>
                </div>
                <div class="card-footer">
                  <button class="btn btn-primary" type="submit">Create</button>
                </div>
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

Kemudian update method create pada ProductController menjadi seperti berikut:

public function create(): Response
    {
        return response(view('products.create'));
    }

Tampilan form create produk:

Membuat Fungsi Store Produk

Ini adalah proses menyimpan input dari form produk ke dalam tabel products di database. Proses ini akan dihandle oleh method store yang ada di dalam ProductController. Pertama, kita perlu melakukan validasi input dengan StoreProductRequest yang telah kita buat di atas. Kemudian, jika inputan tersebut lolos validasi maka kita panggil model Product untuk menyimpan ke tabel products. Kode program method store akan menjadi seperti berikut:

	public function store(StoreProductRequest $request): RedirectResponse
    {
        if (Product::create($request->validated())) {
            return redirect(route('products.index'))->with('success', 'Added!');
        }
    }

Membuat Fungsi Read / List Produk

Untuk menampilkan data produk ini akan dihandle oleh method index dalam ProductController dengan kode program seperti berikut:

public function index(): Response
    {
        $products = Product::all();

        return response(view('products.index', ['products' => $products]));
    }

Pertama, panggil Product::all() untuk melakukan query mengambil semua data dalam tabel products. Selanjutnya data tersebut akan di assign sebagai parameter pada saat render view resources/views/products/index.blade.php.

Adapun kode program untuk index.blade.php adalah seperti berikut:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>My App</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
  </head>
  <body>
    <div id="app">
      <div class="main-wrapper">
        <div class="main-content">
          <div class="container">
            <div class="card mt-5">
              <div class="card-header">
                <h3>List Product</h3>
              </div>
              <div class="card-body">
                @if (session('success'))
                  <div class="alert alert-success">{{ session('success') }}</div>
                @endif

                @if (session('error'))
                  <div class="alert alert-danger">{{ session('error') }}</div>
                @endif
                <p>
                  <a class="btn btn-primary" href="{{ route('products.create') }}">New Product</a>
                </p>
                <table class="table table-striped table-bordered">
                  <thead>
                    <tr>
                      <th>ID</th>
                      <th>SKU</th>
                      <th>Name</th>
                      <th>Price</th>
                      <th>Stock</th>
                      <th>Actions</th>
                    </tr>
                  </thead>
                  <tbody>
                    @forelse ($products as $product)
                      <tr>
                        <td>{{ $product->id }}</td>
                        <td>{{ $product->sku }}</td>
                        <td>{{ $product->name }}</td>
                        <td>{{ $product->price }}</td>
                        <td>{{ $product->stock }}</td>
                        <td>
                          <a href="{{ route('products.edit', ['id' => $product->id]) }}" class="btn btn-secondary btn-sm">edit</a>
                          <a href="#" class="btn btn-sm btn-danger" onclick="
                            event.preventDefault();
                            if (confirm('Do you want to remove this?')) {
                              document.getElementById('delete-row-{{ $product->id }}').submit();
                            }">
                            delete
                          </a>
                          <form id="delete-row-{{ $product->id }}" action="{{ route('products.destroy', ['id' => $product->id]) }}" method="POST">
                              <input type="hidden" name="_method" value="DELETE">
                              @csrf
                          </form>
                        </td>
                      </tr>
                    @empty
                      <tr>
                        <td colspan="6">
                            No record found!
                        </td>
                      </tr>
                    @endforelse
                  </tbody>
                </table>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

Tampilan List Produk:

Membuat Form Edit Produk

Untuk form edit produk ini akan dihandle oleh method edit dalam ProductController. Kode programnya adalah seperti berikut:

public function edit(string $id): Response
    {
        $product = Product::findOrFail($id);

        return response(view('products.edit', ['product' => $product]));
    }

Kita perlu mencari data produk berdasar id produk yang akan diedit. Jika ditemukan, maka data produk tersebut akan di assign sebagai parameter saat render form edit resources/views/products/edit.blade.php. Data produk tersebut akan digunakan untuk menampilkan informasi produk pada form edit. Jadi field-field pada form edit seharusnya berisi dengan data produk yang diambil dari tabel database. Adapun kode program untuk form edit adalah sebagai berikut:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>My App</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
  </head>
  <body>
    <div id="app">
      <div class="main-wrapper">
        <div class="main-content">
          <div class="container">
            <form method="post" action="{{ route('products.update', $product->id) }}">
            @method('PUT')
              @csrf
              <div class="card mt-5">
                <div class="card-header">
                  <h3>Edit Product</h3>
                </div>
                <div class="card-body">
                    @if ($errors->any())
                      <div class="alert alert-danger">
                        <div class="alert-title"><h4>Whoops!</h4></div>
                          There are some problems with your input.
                          <ul>
                            @foreach ($errors->all() as $error)
                              <li>{{ $error }}</li>
                            @endforeach
                          </ul>
                      </div> 
                    @endif

                    @if (session('success'))
                      <div class="alert alert-success">{{ session('success') }}</div>
                    @endif

                    @if (session('error'))
                      <div class="alert alert-danger">{{ session('error') }}</div>
                    @endif
                    <div class="mb-3">
                      <label class="form-label">SKU</label>
                      <input type="text" class="form-control" name="sku" value="{{ old('sku', $product->sku) }}" placeholder="#SKU">
                    </div>
                    <div class="mb-3">
                      <label class="form-label">Name</label>
                      <input type="text" class="form-control" name="name" value="{{ old('name', $product->name) }}"  placeholder="Name">
                    </div>
                    <div class="mb-3">
                      <label class="form-label">Price</label>
                      <input type="text" class="form-control" name="price" value="{{ old('price', $product->price) }}"  placeholder="Price">
                    </div>
                    <div class="mb-3">
                      <label class="form-label">Stock</label>
                      <input type="text" class="form-control" name="stock" value="{{ old('stock', $product->stock) }}"  placeholder="Stock">
                    </div>
                </div>
                <div class="card-footer">
                  <button class="btn btn-primary" type="submit">Update</button>
                </div>
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

Tampilan form edit produk:

Membuat Fungsi Update Produk

Fungsi update produk ini akan dihandle oleh method update pada ProductController. Kode programnya adalah seperti berikut:

public function update(UpdateProductRequest $request, string $id): RedirectResponse
    {
        $product = Product::findOrFail($id);

        if ($product->update($request->validated())) {
            return redirect(route('products.index'))->with('success', 'Updated!'); 
        }
    }

Jika berhasil melakukan update data, maka akan di-redirect ke halaman list produk dengan menampilkan success message.

Membuat Fungsi Delete Produk

Sebelum melakukan proses penghapusan data, program akan menampilkan dialog konfirmasi penghapusan ke user. Fungsi delete produk akan dihandel oleh method destroy pada ProductController. Kode programnya adalah seperti berikut:

public function destroy(string $id): RedirectResponse
    {
        $product = Product::findOrFail($id);

        if ($product->delete()) {
            return redirect(route('products.index'))->with('success', 'Deleted!');
        }

        return redirect(route('products.index'))->with('error', 'Sorry, unable to delete this!');
    }

Pertama akan mengecek data produk berdasarkan id yang diberikan. Jika data produk valid, baru dilakukan penghapusan. Jika sukses hapus maka akan di-redirect ke halaman list produk dengan menampilkan success message.

Tampilan konfirmasi delete produk:

Source Code CRUD Single Tabel Laravel 10

Source code lengkap tutorial ini ada di Github: https://github.com/gieart87/tutorial-laravel10/tree/feature/part-10-crud-single-table

Tulis Komentar