ตัวอย่างการสร้าง CRUD ด้วย Laravel 9 และ React JS

ตัวอย่างการสร้าง CRUD ด้วย Laravel 9 และ React JS
by pairat.p | มีผู้อ่านล่าสุดเมื่อ 6 ชั่วโมงที่แล้ว

จำนวนผู้อ่าน
172
หัวข้อย่อย
9
ถูกใจ
1

ในบทความนี้เราจะมาสร้าง CRUD ด้วย Laravel 9 และ React js กันครับ โดยเราจะใช้ laravel breeze, inertia js, vite และ tailwind css เพื่อสร้าง react crud ใน laravel app ทำไปทีละขั้นตอน เหมาะสำหรับมือใหม่

ติดตั้ง Laravel หากมีแล้วให้ข้ามขั้นตอนนี้ไป

Server Requirements สำหรับ Laravel 9 นั้น มีความต้องการดังนี้

  • PHP >= 8.0
  • BCMath PHP Extension
  • Ctype PHP Extension
  • cURL PHP Extension
  • DOM PHP Extension
  • Fileinfo PHP Extension
  • JSON PHP Extension
  • Mbstring PHP Extension
  • OpenSSL PHP Extension
  • PCRE PHP Extension
  • PDO PHP Extension
  • Tokenizer PHP Extension
  • XML PHP Extension

ในเครื่องจะต้องติดตั้ง node js  ไว้แล้ว  จากนั้นติดตั้ง laravel ได้เลยครับ

composer create-project laravel/laravel blog

แก้ไข config ไฟล์ .env 

DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=blog
DB_USERNAME=root
DB_PASSWORD=

 

ติดตั้งและใช้งานระบบ Authentication ของ Laravel Breeze

ระบบ Authentication System ของ Laravel Breeze นั้นเป็นเป็นชุด Start Kit 
ที่นำมาใช้กับพวกระบบพื้นฐานเช่น Register,Login,Forget Password ซึ่งสามารถใช้ coposer ติดตั้งได้ตามคำสั่งนี้
 

composer require laravel/breeze --dev

ติดตั้ง  react js 

php artisan breeze:install react

ติดตั้ง nod_module ด้วย node js เพื่อเรียกใช้ package

npm install

เมื่อก่อนใน laravel นั้นจะ laramix webpack.js มาให้ใช้ตั้งแต่ติดตั้ง เพื่อมาช่วยในการจัดการพวก css,js ต่าง แต่ปัจจุบันนั้นมี vite.js แทน

npm run dev


จากนั้นใช้คำสั่ง migrate  เพื่อสร้าง database table

php artisan migrate 

 

ใช้คำสั่งเพื่อสร้าง Migration และ Model

เปิด Terminal ใช้คำสั่ง migration เพื่อสร้าง blog table

php artisan make:migration create_blogs_table

เปิดไฟล์แก้ไฟล์ Migration ตามคำสั่งนี้

<?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('blogs', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('details');
            $table->timestamps();
        });
    }

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

เปิด Terminal จากนั้น run คำสั่ง migrate

php artisan migrate

เปิด Terminal จากนั้น run คำสั่งเพื่อสร้างไฟล์ Model

php artisan make:model Blog

เปิดไฟล์ App/Models/Blog.php

<?php

namespace App\Models;

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

class Blog extends Model
{
    use HasFactory;
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'title', 'details'
    ];
}
จัดการ Route

เปิดไฟล์ routes/web.php แก้ไขฟล์ตามนี้

<?php

use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
use App\Http\Controllers\BlogController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::resource('blog', BlogController::class);

Route::get('/', function () {
    return Inertia::render('Welcome', [
        'canLogin' => Route::has('login'),
        'canRegister' => Route::has('register'),
        'laravelVersion' => Application::VERSION,
        'phpVersion' => PHP_VERSION,
    ]);
});

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

require __DIR__.'/auth.php';
สร้าง Controller

เปิด Terminal แล้วพิมพ์คำสั่ง Artisan CLI ตามด้านล่างดังนี้

php artisan make:controller BlogController

เปิดไฟล์ app/Http/Controllers/BlogController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Inertia\Inertia;
use App\Models\Blog;
use Illuminate\Support\Facades\Validator;

class BlogController extends Controller
{
    /**
     * Show the form for creating a new resource.
     *
     * @return Response
     */
    public function index()
    {
        $blog = Blog::all();
        return Inertia::render('Blog/Index', ['blog' => $blog]);
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function create()
    {
        return Inertia::render('Blog/Create');
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return Response
     */
    public function store(Request $request)
    {
        Validator::make($request->all(), [
            'title' => ['required'],
            'details' => ['required'],
        ])->validate();

        Blog::create($request->all());

        return redirect()->route('blog.index');
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function edit(Blog $blog)
    {
        return Inertia::render('Blog/Edit', [
            'blogs' => $blog
        ]);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return Response
     */
    public function update($id, Request $request)
    {
        Validator::make($request->all(), [
            'title' => ['required'],
            'details' => ['required'],
        ])->validate();

        Blog::find($id)->update($request->all());
        return redirect()->route('blog.index');
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return Response
     */
    public function destroy($id)
    {
        Blog::find($id)->delete();
        return redirect()->route('blog.index');
    }
}
สร้างหน้า React Index Pages

ในขั้นตอนนี้ เราจะสร้างไฟล์ react js สำหรับ Index.jsx, Create.jsx และ Edit.jsx

สร้างไฟล Index.jsx ไว้ที่  resources/js/Pages/Blog/Index.jsx

import React from 'react';
import Authenticated from '@/Layouts/Authenticated';
import { Inertia } from "@inertiajs/inertia";
import { Head, usePage, Link } from '@inertiajs/inertia-react';

export default function Dashboard(props) {
    const { blog } = usePage().props

    function destroy(e) {
        if (confirm("Are you sure you want to delete this user?")) {
            Inertia.delete(route("blog.destroy", e.currentTarget.id));
        }
    }

   return (
        <Authenticated
            auth={props.auth}
            errors={props.errors}
            header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Blog</h2>}
        >
            <Head title="Blog" />

            <div className="py-12">
                <div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
                    <div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                        <div className="p-6 bg-white border-b border-gray-200">

                            <div className="flex items-center justify-between mb-6">
                                <Link
                                    className="px-6 py-2 text-white bg-green-500 rounded-md focus:outline-none"
                                    href={ route("blog.create") }
                                >
                                    Create Blog
                                </Link>
                            </div>

                            <table className="table-fixed w-full">
                                <thead>
                                    <tr className="bg-gray-100">
                                        <th className="px-4 py-2 w-20">No.</th>
                                        <th className="px-4 py-2">Title</th>
                                        <th className="px-4 py-2">Details</th>
                                        <th className="px-4 py-2">Action</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    {blog.map(({ id, title, details }) => (
                                        <tr>
                                            <td className="border px-4 py-2">{ id }</td>
                                            <td className="border px-4 py-2">{ title }</td>
                                            <td className="border px-4 py-2">{ details }</td>
                                            <td className="border px-4 py-2">
                                                <Link
                                                    tabIndex="1"
                                                    className="px-4 py-2 text-sm text-white bg-blue-500 rounded"
                                                    href={route("blog.edit", id)}
                                                >
                                                    Edit
                                                </Link>
                                                <button
                                                    onClick={destroy}
                                                    id={id}
                                                    tabIndex="-1"
                                                    type="button"
                                                    className="mx-1 px-4 py-2 text-sm text-white bg-red-500 rounded">
                                                    Delete
                                                </button>
                                            </td>
                                        </tr>
                                    ))}
                                    {blog.length === 0 && (
                                        <tr>
                                            <td
                                                className="px-6 py-4 border-t"
                                                colSpan="4"
                                            >
                                                No contacts found.
                                            </td>
                                        </tr>
                                    )}
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
        </Authenticated>
    );
}

 

สร้างหน้า Create และ Edit

สร้างไฟล Create.jsx ไว้ที่  resources/js/Pages/Blog/Create.jsx

import React from 'react';
import Authenticated from '@/Layouts/Authenticated';
import { Head, useForm, Link } from '@inertiajs/inertia-react';

export default function Dashboard(props) {

    const { data, setData, errors, post } = useForm({
        title: "",
        description: "",
    });

    function handleSubmit(e) {
        e.preventDefault();
        post(route("blog.store"));
    }

    return (
        <Authenticated
            auth={props.auth}
            errors={props.errors}
            header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Create Blog</h2>}
        >
            <Head title="Blog" />

            <div className="py-12">
                <div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
                    <div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                        <div className="p-6 bg-white border-b border-gray-200">

                            <div className="flex items-center justify-between mb-6">
                                <Link
                                    className="px-6 py-2 text-white bg-blue-500 rounded-md focus:outline-none"
                                    href={ route("blog.index") }
                                >
                                    Back
                                </Link>
                            </div>

                            <form name="createForm" onSubmit={handleSubmit}>
                                <div className="flex flex-col">
                                    <div className="mb-4">
                                        <label className="">Title</label>
                                        <input
                                            type="text"
                                            className="w-full px-4 py-2"
                                            label="Title"
                                            name="title"
                                            value={data.title}
                                            onChange={(e) =>
                                                setData("title", e.target.value)
                                            }
                                        />
                                        <span className="text-red-600">
                                            {errors.title}
                                        </span>
                                    </div>
                                    <div className="mb-0">
                                        <label className="">Details</label>
                                        <textarea
                                            type="text"
                                            className="w-full rounded"
                                            label="details"
                                            name="details"
                                            errors={errors.details}
                                            value={data.details}
                                            onChange={(e) =>
                                                setData("details", e.target.value)
                                            }
                                        />
                                        <span className="text-red-600">
                                            {errors.details}
                                        </span>
                                    </div>
                                </div>
                                <div className="mt-4">
                                    <button
                                        type="submit"
                                        className="px-6 py-2 font-bold text-white bg-green-500 rounded"
                                    >
                                        Save
                                    </button>
                                </div>
                            </form>

                        </div>
                    </div>
                </div>
            </div>
        </Authenticated>
    );
}

 

สร้างไฟล Edit.jsx ไว้ที่  resources/js/Pages/Blog/Edit.jsx

import React from 'react';
import Authenticated from '@/Layouts/Authenticated';
import { Head, useForm, usePage, Link } from '@inertiajs/inertia-react';

export default function Dashboard(props) {

    const { blog } = usePage().props;
    const { data, setData, put, errors } = useForm({
        title: blog.title || "",
        details: blog.body || "",
    });

    function handleSubmit(e) {
        e.preventDefault();
        put(route("blog.update", blog.id));
    }

    return (
        <Authenticated
            auth={props.auth}
            errors={props.errors}
            header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Edit Blog</h2>}
        >
            <Head title="Blog" />

            <div className="py-12">
                <div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
                    <div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                        <div className="p-6 bg-white border-b border-gray-200">

                            <div className="flex items-center justify-between mb-6">
                                <Link
                                    className="px-6 py-2 text-white bg-blue-500 rounded-md focus:outline-none"
                                    href={ route("blog.index") }
                                >
                                    Back
                                </Link>
                            </div>

                            <form name="createForm" onSubmit={handleSubmit}>
                                <div className="flex flex-col">
                                    <div className="mb-4">
                                        <label className="">Title</label>
                                        <input
                                            type="text"
                                            className="w-full px-4 py-2"
                                            label="Title"
                                            name="title"
                                            value={data.title}
                                            onChange={(e) =>
                                                setData("title", e.target.value)
                                            }
                                        />
                                        <span className="text-red-600">
                                            {errors.title}
                                        </span>
                                    </div>
                                    <div className="mb-0">
                                        <label className="">Details</label>
                                        <textarea
                                            type="text"
                                            className="w-full rounded"
                                            label="details"
                                            name="details"
                                            errors={errors.details}
                                            value={data.details}
                                            onChange={(e) =>
                                                setData("details", e.target.value)
                                            }
                                        />
                                        <span className="text-red-600">
                                            {errors.details}
                                        </span>
                                    </div>
                                </div>
                                <div className="mt-4">
                                    <button
                                        type="submit"
                                        className="px-6 py-2 font-bold text-white bg-green-500 rounded"
                                    >
                                        Update
                                    </button>
                                </div>
                            </form>

                        </div>
                    </div>
                </div>
            </div>
        </Authenticated>
    );
}
รันคำสั่ง npm run dev และ npm run build

เข้าไปที่ไฟล์ Authenticated.jsx เพิ่ม link นี้ในส่วน Header

...
<NavLink href={route('blog.index')} active={route().current('blog.index')}>
Blog
</NavLink>
...

ขั้นตอนที่จำเป็นทั้งหมดเสร็จสิ้นแล้ว จากนั้นเปิด Terminal และพิมพ์คำสั่งด้านล่างแล้วกด Enter เพื่อเรียกใช้ app Laravel

php artisan serve

หากต้องการเปลี่ยน port เพียงก็ใช้คำสั่งนี้

php artisan serve --port=8081

จากนั้น run คำสั่ง สำหรับ vite.js

npm run dev

หากจะ build โปรเจคเอาไปใช้ในระดับ production ก็ใช้คำสั่งด้านล่างนี้

npm run build

เปิด Browser

http://localhost:8081

 

ทดสอบ Register , login , Create Blog , Edit Blog

 

เปิดลิ้ง ทดสอบ  http://localhost:8081/

 

 Register กันก่อน

เข้าสู่หน้า Blog

เข้าสู่หน้า Create Blog 

 

รายการที่เพิ่มเข้าไป

 


Tank you Ref: https://www.itsolutionstuff.com/post/laravel-9-react-js-crud-using-vite-exampleexample.html