Membuat Crud Laravel Inertia.js dan React.js
Membuat Crud Laravel Inertia.js dan React.js - Morning Sobs-sobs sekalian, gimana keadaanya ? mudah-mudahan sehat sobs baik fisik dan mental sehingga bisa menikmati pahit kopi hitam.
Oke sobs-sobs pada kesempatan kali ini gua akan share materi tentang pembuatan Crud Sederhana dengan Laravel + Inertia.js + Dede Gemez (React.js).
disini gua asamumsikan sobs-sobs mengerti fundamental React.js dan Inertia.js, jika belum jangan putus asa sobs terus membaca dari sumber manapun terutama dokumentasi resminya.
adapun yang perlu disiapkan tentunya :
1. Laptop / PC (wajib)
2. Koneksi Internet (wajib)
3. Kopi (tidak wajib)
Langsung gas ke materi pembahasannya ya sobs
1. Install Laravel
Langkah pertama yaitu kita install laravel, kita bisa menginstall lewat composer sobs-sobs.
composer create-project laravel/laravel crud-inertia-react
2. Konfigurasi .env
jika sudah melewati langkah pertama selanjutnya silahkan add folder project laravel kita di code editor kesayangan kita, selanjutnya kita edit file .env dan sesuaikan pengaturan yang penting seperti
DB_DATABASE = Untuk database yang sobs-sobs gunakan.
DB_USERNAME = Untuk user databasenya.
DB_PASSWORD = Password username database ya sobs.
Silahkan sobs-sobs konfigurasi diatas dan sesuaikan dengan yang sobs-sobs gunakan.
3. Install dan Konfigurasi Inertia.js
Pada proses ini dibagi menjadi dua bagian penting instalasi dan konfigurasi inertia yaitu server side dan client side.
3.1 Server side
kita akan menginstall inertia adapter di sisi server dengan kata lain yaitu dilaravelnya sobs-sobs dengan menggunakan composer seperti biasa.
composer require inertiajs/inertia-laravel
jika sudah buat file dengan nama app.blade.php di dalam folder resource/views. kemudian sesuaikan dengan seperti dibawah ini.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
@viteReactRefresh
@vite('resources/js/app.jsx')
@inertiaHead
</head>
<body>
@inertia
</body>
</html>
file app.blade.php ini berfungsi sebagai root template.step berikutnya kita konfigurasi middleware dari inertia.js ini. silahkan jalankan perintah dibawah ini sobs-sobs :
php artisan inertia:middleware
Kemudian buka file Kernel.php di folder app/Http dan tambahkan baris seperti dibawah ini pada middlewareGroups pada bagian web, sehingga menjadi seperti dibawah ini.
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
// Penambahan untuk inertia middleware
\App\Http\Middleware\HandleInertiaRequests::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
Selanjutnya adalah pengaturan inertia di sisi client.3.2 Client Side
Untuk instalasi inertia di sisi client kita akan mengunakan node package manager (npm). silahkan jalankan perintah ini di terminal atau command promt sobs-sobs.
npm install @inertiajs/react @vitejs/plugin-react react react-dom bootstrap sweetalert2
Oh iya sobs-sobs perintah diatas tidak hanya sekedar menginstall inertia.js saja tetapi menginstall pendukung-pendukung lainnya seperti vite plugin react, react, react-dom, bootstrap dan sweetalert.Sekarang rename file app.js menjadi app.jsx yang berada di folder resources/js. jika sudah silahkan masukan kode seperti dibawah ini.
import { createInertiaApp } from "@inertiajs/react";
import { createRoot } from "react-dom/client";
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.min.js";
createInertiaApp({
resolve: (name) => {
const pages = import.meta.glob("./Pages/**/*.jsx", { eager: true });
return pages[`./Pages/${name}.jsx`];
},
setup({ el, App, props }) {
createRoot(el).render(<App {...props} />);
},
});
kode diatas berfungsi sebagai pengarah render halaman dari aplikasi kita, adapun lokasi akan diarahkan ke dalam folder Pages. oleh karena itu kita harus buat sebuah folder dengan nama Pages didalam folder resources/js. ingat ya sobs-sobs nama folder harus Pages.Oke untuk sisi client side dari inertia sudah selesai. maka kita akan lanjut ke step selanjutnya.
4. Konfigurasi vite
Silahkan sobs-sobs buka file vite.config.js lalu silahkan hapus semuanya lalu ganti dengan seperti dibawah ini :
export default defineConfig({
plugins: [
react(),
laravel({
input: ["resources/css/app.css", "resources/js/app.jsx"],
refresh: true,
}),
],
});
5. Buat model, migration dan controller
Oke sobs-sobs silahkan kita akan buat model, database migration, dan resource controller sekaligus.
php artisan make:model Product -mrc
jika sudah silahkan buka model file Product.php didalam folder app/Models lalu tambahkan baris kode seperti dibawah ini.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use HasFactory;
// tambahan
protected $guarded = ['id'];
public function getRouteKeyName()
{
return "sku";
}
}
oke selanjutnya kita ke migration file silahkan buka file migration yang berlokasi di database/migrations cari filename dengan create_products_table.php kemudian sesuaikan dengan dibawah ini.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('sku')->unique();
$table->string('nama');
$table->text('deskripsi')->nullable();
$table->string('foto')->nullable();
$table->unsignedInteger('stok')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('products');
}
};
jika sudah maka kita akan menjalankan migrasi, silahkan sobs-sobs jalankan perintah dibawah ini
php artisan migrate
next step.6. route, controller dan views
Oke sobs-sobs silahkan buka file web.php yang berlokasi di folder routes, kita akan buat route resource untuk ProductController.
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;
Route::resource('/products', ProductController::class);
Selanjutnya kita akan berfokus ke ProductController, Silahkan sobs-sobs buka ProductController di app/Http/Controllers.- Pertama-tama kita berfokus ke method index silahkan sobs-sobs sesuaikan dengan dibawah ini :
public function index()
{
$products = Product::orderBy('id', 'desc')->get();
return Inertia::render('Index', [
'products' => $products,
]);
}
Dan jangan lupa untuk menambahkan use Inertia\Inertia;
diluar baris atas diluar class ProductController.
penjelasan method diatas adalah kita ambil semua data Product lalu kita render dan kirimkan data ke inertia.
selanjutnya kita buat file view yang bernama Index.jsx didalam folder resources/js/Pages.
kemudian isi seperti kode dibawah ini.
import React from "react";
import { Head, Link, router, usePage } from "@inertiajs/react";
import Swal from "sweetalert2";
const Index = ({ products }) => {
let number = 0;
// property untuk menangkap session pesan flash
const { flash } = usePage().props;
// untuk handle hapus data
const handleDelete = (e) => {
Swal.fire({
title: "Are you sure?",
text: "You won't be able to revert this!",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete it!",
}).then((result) => {
if (result.isConfirmed) {
router.delete(`/products/${e.target.id}`);
}
});
};
return (
<>
{/* untuk menambahkan title page */}
<Head title="List product sruputkode.com"></Head>
<div className="container">
<div className="row">
<div className="col-12">
<h2 className="text-center text-primary">
Data produk
</h2>
<p className="text-center">
<small>Data produk di sruputkode.com</small>
</p>
<hr />
</div>
</div>
<div className="row">
<div className="col-12">
{/* menampilkan pesan flash jika ada */}
{flash.msg && (
<div
className="alert alert-success alert-dismissible fade show"
role="alert"
>
<span className="text-center">{flash.msg}</span>
<button
type="button"
className="btn-close"
data-bs-dismiss="alert"
aria-label="Close"
/>
</div>
)}
<Link
href="/products/create"
className="btn btn-primary btn-sm mb-3"
>
+ Tambah produk
</Link>
<table className="table">
<thead>
<tr>
<th>#</th>
<th>SKU</th>
<th>Nama Produk</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
{products.length === 0 ? (
<tr>
<td
colSpan={4}
className="text-center text-danger"
>
Tidak ada data
</td>
</tr>
) : (
products.map((product, index) => {
number += 1;
return (
<tr key={index}>
<td>{number}</td>
<td>{product.sku}</td>
<td>{product.nama}</td>
<td>
<Link
href={`products/${product.sku}`}
className="btn btn-primary btn-sm me-1"
>
lihat
</Link>
<Link
href={`products/${product.sku}/edit`}
className="btn btn-warning btn-sm me-1"
>
ubah
</Link>
<button
id={product.sku}
onClick={handleDelete}
className="btn btn-danger btn-sm"
>
hapus
</button>
</td>
</tr>
);
})
)}
</tbody>
</table>
</div>
</div>
</div>
</>
);
};
export default Index;
selanjutnya kita buka file HandleInertiaRequest.php di app/Http/Middleware. silahkan ubah sesuai dengan kode dibawah ini :
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
/**
* The root template that's loaded on the first page visit.
*
* @see https://inertiajs.com/server-side-setup#root-template
* @var string
*/
protected $rootView = 'app';
/**
* Determines the current asset version.
*
* @see https://inertiajs.com/asset-versioning
* @param \Illuminate\Http\Request $request
* @return string|null
*/
public function version(Request $request): ?string
{
return parent::version($request);
}
/**
* Defines the props that are shared by default.
*
* @see https://inertiajs.com/shared-data
* @param \Illuminate\Http\Request $request
* @return array
*/
public function share(Request $request): array
{
return array_merge(parent::share($request), [
"flash" => [
"msg" => $request->session()->has('msg') ? $request->session()->get('msg') : "",
]
]);
}
}
jika sudah silahkan jalankan kedua perintah ini secara bersamaan di terminal atau cmd sobs-sobs.
php artisan serve
dan
npm run dev
ingat ya sobs-sobs jangan ada yang diberhentikan dari kedua perintah diatas.Selanjutnya
buka browser sobs-sobs silahkan ketikan alamat url 127.0.0.1:8000/products jika berhasil maka tampilannya akan seperti dibawah ini.
Selanjutnya kita buka kembali file ProductController lalu kita akan fokus ke method create. silahkan sobs-sobs sesuaikan seperti dibawah ini.
public function create()
{
return Inertia::render('Create');
}
Next kita buat file viewsnya silahkan sobs-sobs buat file dengan nama Create.jsx di dalam folder resources/js/Pages jika sudah silahkan sesuaikan isi dibawah ini sobs-sobs.
import React from "react";
import { Link, router, Head } from "@inertiajs/react";
const Create = ({ errors }) => {
// state untuk value form
const [input, setInput] = React.useState({
sku: "",
nama: "",
deskripsi: "",
foto: null,
stok: 0,
});
// function untuk handle form submit
const handleFormSubmit = (e) => {
e.preventDefault();
router.post("/products", input);
};
return (
<>
<Head title="Tambah Produk" />
<div className="container">
<div className="row">
<div className="col-12">
<h4 className="mt-3 text-primary">Input produk baru</h4>
<hr />
</div>
<div className="col-12">
<Link
href="/products"
className="btn btn-primary btn-sm my-3"
>
Kembali
</Link>
<div className="card">
<div className="card-body">
<form
encType="multipart/form-data"
onSubmit={handleFormSubmit}
>
<div className="mb-3">
<label
htmlFor="sku"
className="form-label"
>
SKU
</label>
<input
type="text"
className={
errors.sku
? "form-control is-invalid"
: "form-control"
}
id="sku"
name="sku"
value={input.sku}
onChange={(e) =>
setInput({
...input,
sku: e.target.value,
})
}
/>
{errors.sku && (
<div className="invalid-feedback">
{errors.sku}
</div>
)}
</div>
<div className="mb-3">
<label
htmlFor="nama"
className="form-label"
>
Nama produk
</label>
<input
type="text"
className={
errors.nama
? "form-control is-invalid"
: "form-control"
}
id="nama"
value={input.nama}
onChange={(e) =>
setInput({
...input,
nama: e.target.value,
})
}
/>
{errors.nama && (
<div className="invalid-feedback">
{errors.nama}
</div>
)}
</div>
<div className="mb-3">
<label
htmlFor="deskripsi"
className="form-label"
>
Deskripsi produk
</label>
<textarea
className={
errors.deskripsi
? "form-control is-invalid"
: "form-control"
}
id="deskripsi"
rows={3}
value={input.deskripsi}
onChange={(e) =>
setInput({
...input,
deskripsi: e.target.value,
})
}
/>
{errors.deskripsi && (
<div className="invalid-feedback">
{errors.deskripsi}
</div>
)}
</div>
<div className="mb-3">
<label
htmlFor="foto"
className={
errors.foto
? "form-control is-invalid"
: "form-control"
}
>
Foto produk
</label>
<input
className="form-control"
type="file"
id="foto"
onChange={(e) =>
setInput({
...input,
foto: e.target.files[0],
})
}
/>
{errors.foto && (
<div className="invalid-feedback">
{errors.foto}
</div>
)}
</div>
<div className="mb-3">
<label
htmlFor="stok"
className="form-label"
>
Stok
</label>
<input
type="number"
className={
errors.stok
? "form-control is-invalid"
: "form-control"
}
id="stok"
min={0}
value={input.stok}
onChange={(e) =>
setInput({
...input,
stok: e.target.value,
})
}
/>
{errors.stok && (
<div className="invalid-feedback">
{errors.stok}
</div>
)}
</div>
<button
type="submit"
className="btn btn-primary"
>
Submit
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default Create;
jika sudah kita akan berfokus ke ProductController pada method store, silahkan sobs-sobs sesuaikan method store dengan kode dibawah ini :
public function store(Request $request)
{
$request->validate([
'sku' => 'required|unique:products,sku',
'nama' => 'required',
'deskripsi' => 'nullable',
'foto' => 'nullable|image',
'stok' => 'nullable',
]);
$dataInput = [
'sku' => $request->sku,
'nama' => $request->nama,
'stok' => $request->stok,
];
if ($request->filled('deskripsi')) {
$dataInput['deskripsi'] = $request->deskripsi;
}
if ($request->hasFile('foto')) {
$requestFoto = $request->file('foto');
$namaFoto = $dataInput['sku'] . "." . $requestFoto->getClientOriginalExtension();
$requestFoto->storeAs('public/products', $namaFoto);
$dataInput['foto'] = "products/" . $namaFoto;
}
Product::create($dataInput);
return to_route('products.index')->with('msg', 'Produk berhasil ditambahkan');
}
Jika sudah silahkan sobs-sobs save dan lakukan pengetesan insert data dengan membuka url : http://127.0.0.1:8000/products/create
Oke jika berhasil kita akan beranjak ke membuat detail view untuk product.
Silahkan sobs-sobs buka kembali ProductController lalu sesuaikan untuk method show seperti dibawah ini :
public function show(Product $product)
{
return Inertia::render('Show', [
'product' => $product,
]);
}
Jika sudah kita akan buat file viewnya. Silahkan buat file Show.jsx didalam folder resources/js/Pages.kemudian sesuaikan dengan kode dibawah ini :
import React from "react";
import { Link, Head } from "@inertiajs/react";
const Show = ({ product }) => {
// untuk style gambar produk
const _imageStyle = {
width: "200px",
height: "200px",
objectFit: "cover",
};
return (
<>
<Head title="Detail produk" />
<div className="container mt-3">
<div className="row">
<div className="col-12">
<h4 className="text-success">Detail produk</h4>
<small className="text-success">
informasi detail produk sruputkode.com
</small>
<hr />
</div>
</div>
<div className="row">
<div className="col-lg-6 col-md-12 col-sm-6">
<table className="table">
<tbody>
<tr>
<td>Sku</td>
<td>{product.sku}</td>
</tr>
<tr>
<td>Nama produk</td>
<td>{product.nama}</td>
</tr>
<tr>
<td>Deskripsi</td>
<td>
{product.deskripsi
? product.deskripsi
: "Tidak ada deskripsi"}
</td>
</tr>
<tr>
<td>Foto</td>
<td>
{product.foto ? (
<img
src={"/storage/" + product.foto}
alt={product.name}
style={_imageStyle}
/>
) : (
"Tidak ada foto"
)}
</td>
</tr>
<tr>
<td>Stok</td>
<td>{product.stok}</td>
</tr>
<tr>
<td></td>
<td>
<Link
className="btn btn-primary btn-sm"
href="/products"
>
Kembali ke beranda
</Link>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</>
);
};
export default Show;
selanjutnya jalankan perintah dibawah ini di terimal atau cmd sobs-sobs
php artisan storage:link
step berikutnya kita akan berfokus ke proses edit dan update.
Silahkan buka kembali ProductController lalu sesuaikan method edit seperti dibawah ini :
public function edit(Product $product)
{
return Inertia::render('Edit', [
'product' => $product,
]);
}
kemudian buat file kita buat file edit viewsnya sobs-sobs. silahkan sobs-sobs buat file baru dengan nama Edit.jsx didalam folder resources/js/Page. jika sudah sesuaikan dengan kode dibawah ini sobs-sobs.
import React from "react";
import { Link, router, Head } from "@inertiajs/react";
const Create = ({ errors, product }) => {
// state untuk value form
const [input, setInput] = React.useState({
sku: product.sku,
nama: product.nama,
deskripsi: product.deskripsi ? product.deskripsi : "",
foto: null,
stok: product.stok,
});
// function untuk handle form submit update
const handleFormSubmit = (e) => {
e.preventDefault();
router.post(`/products/${product.sku}`, {
_method: "put",
...input,
});
};
return (
<>
<Head title="Tambah Produk" />
<div className="container">
<div className="row">
<div className="col-12">
<h4 className="mt-3 text-primary">Edit produk</h4>
<hr />
</div>
<div className="col-12">
<Link
href="/products"
className="btn btn-primary btn-sm my-3"
>
Kembali
</Link>
<div className="card">
<div className="card-body">
<form
encType="multipart/form-data"
onSubmit={handleFormSubmit}
>
<div className="mb-3">
<label
htmlFor="sku"
className="form-label"
>
SKU
</label>
<input
type="text"
className={
errors.sku
? "form-control is-invalid"
: "form-control"
}
id="sku"
name="sku"
value={input.sku}
onChange={(e) =>
setInput({
...input,
sku: e.target.value,
})
}
/>
{errors.sku && (
<div className="invalid-feedback">
{errors.sku}
</div>
)}
</div>
<div className="mb-3">
<label
htmlFor="nama"
className="form-label"
>
Nama produk
</label>
<input
type="text"
className={
errors.nama
? "form-control is-invalid"
: "form-control"
}
id="nama"
value={input.nama}
onChange={(e) =>
setInput({
...input,
nama: e.target.value,
})
}
/>
{errors.nama && (
<div className="invalid-feedback">
{errors.nama}
</div>
)}
</div>
<div className="mb-3">
<label
htmlFor="deskripsi"
className="form-label"
>
Deskripsi produk
</label>
<textarea
className={
errors.deskripsi
? "form-control is-invalid"
: "form-control"
}
id="deskripsi"
rows={3}
value={input.deskripsi}
onChange={(e) =>
setInput({
...input,
deskripsi: e.target.value,
})
}
/>
{errors.deskripsi && (
<div className="invalid-feedback">
{errors.deskripsi}
</div>
)}
</div>
<div className="mb-3">
<label
htmlFor="foto"
className={
errors.foto
? "form-control is-invalid"
: "form-control"
}
>
Foto produk
</label>
<input
className="form-control"
type="file"
id="foto"
onChange={(e) =>
setInput({
...input,
foto: e.target.files[0],
})
}
/>
{errors.foto && (
<div className="invalid-feedback">
{errors.foto}
</div>
)}
</div>
<div className="mb-3">
<label
htmlFor="stok"
className="form-label"
>
Stok
</label>
<input
type="number"
className={
errors.stok
? "form-control is-invalid"
: "form-control"
}
id="stok"
min={0}
value={input.stok}
onChange={(e) =>
setInput({
...input,
stok: e.target.value,
})
}
/>
{errors.stok && (
<div className="invalid-feedback">
{errors.stok}
</div>
)}
</div>
<button
type="submit"
className="btn btn-primary"
>
Update
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default Create;
oke selanjutnya buka ProductController dan edit method update sesuaikan dengan kode dibawah ini :
public function update(Request $request, Product $product)
{
$request->validate([
'sku' => 'required|unique:products,sku,' . $product->id,
'nama' => 'required',
'deskripsi' => 'nullable',
'foto' => 'nullable|image',
'stok' => 'required',
]);
$dataUpdate = [
'sku' => $request->sku,
'nama' => $request->nama,
'stok' => $request->stok,
];
if ($request->filled('deskripsi')) {
$dataUpdate['deskripsi'] = $request->deskripsi;
}
if ($request->hasFile('foto')) {
if ($product->foto) {
Storage::delete('public/' . $product->foto);
$requestFoto = $request->file('foto');
$namaFoto = $dataUpdate['sku'] . "." . $requestFoto->getClientOriginalExtension();
$requestFoto->storeAs('public/products', $namaFoto);
$dataUpdate['foto'] = "products/" . $namaFoto;
}
}
$product->update($dataUpdate);
return to_route('products.index')->with('msg', 'Produk berhasil terupdate');
}
dan yang terahkir adalah proses hapus data. silahkan sobs-sobs kembali buka ProductController. silahkan edit method destroy dan sesuaikan dengan dibawah ini :
public function destroy(Product $product)
{
if ($product->foto) {
Storage::delete("public/" . $product->foto);
}
$product->delete();
return to_route('products.index')->with('msg', "{$product->name} berhasil dihapus");
}
use Illuminate\Support\Facades\Storage;
Jika sudah silahkan sobs-sobs lakukan pengetesan hapus data.
Dan akhirnya proses panjang dari artikel sudah selesai sobs-sobs, oh iya untuk materi kali ini hasilnya gua taro di github agar memudahkan pembelajaran pada materi kali ini. jika ada error seputar materi kali ini silahkan sobs-sobs cari terlebih dahulu untuk problem solvingnya atau dengan bertanya dikolom komentar.
Oke seee you next time.
Posting Komentar untuk "Membuat Crud Laravel Inertia.js dan React.js"
Posting Komentar