انرشيا ورياكت ولارافيل CRUD

انيرشيا و رياكت و لارافيل inertia+react+laravel 11 for CRUD

من خلال استخدام Inertia.js، يمكن دمج إطار العمل الخلفي Laravel مع واجهة المستخدم React بسهولة، مما يتيح تطوير تطبيقات CRUD (إنشاء، قراءة، تحديث، وحذف) بشكل متكامل دون الحاجة إلى استخدام REST APIs التقليدية أو التعامل مع JSON مباشرة. Inertia.js يعمل كجسر يربط بين Laravel و React، مما يسهل عملية نقل البيانات والتحكم في التنقل بين الصفحات بدون إعادة تحميل الصفحة، مما يعزز من أداء التطبيق وتجربة المستخدم. في هذا المقال، سنستعرض كيفية إعداد بيئة عمل لتطوير تطبيق CRUD باستخدام Laravel 11، React، و Inertia.js مع التركيز على تنفيذ العمليات الأساسية والتفاعل مع قاعدة البيانات بشكل مرن وفعال.

انيرشيا و رياكت و لارافيل inertia+react+laravel 11 for CRUD

المتطلبات

كما ذكرنا في السابق برامج لا غني عنها فيجوال ستوديو كود و اكسامب و نود جي اس

visual studio code
pexels-photo-11035380-11035380.jpg
xampp

تثبيت لارافيل 11 Laravel

سنقوم بانشاء مشروع جديد باسم customers باستخدام الكود التالي

				
					composer create-project --prefer-dist laravel/laravel customer

				
			

بعد ذلك ندخل علي ملف المشروع باستخدام الامر التالي

				
					cd customer

				
			

تثبيت Laravel Breeze

نقوم باعداد قاعدة المصادقة Breeze علي وحدة التحكم باستخدام الامر التالي

				
					composer require laravel/breeze --dev
				
			

تثبيت Breeze مع React

بعد ذلك نقوم بتثبيت حزمة Breeze مع رياكت باستخدام الامر التالي

				
					php artisan breeze:install react

				
			

تثبيت Inertia

بعد ذلك نقوم بتثبيت حزمة Inertia js باستخدام الامر التالي

				
					composer require inertiajs/inertia-laravel

				
			

تثبيت fontawesome و sweetalert2

عند الحاجة الي ايقونات بشكل لطيف او نوافذ منبثقة يجب تثبيت هذين الاداتين باستخدام الكود التالي

				
					npm i @fortawesome/fontawesome-free sweetalert2
				
			

ثم اضافة السطر التالي في ملف App.jsx 

				
					// add this in App.jsx
// add this in any page want to used
import '@fortawesome/fontawesome-free/css/all.min.css';
import Swal from 'sweetalert2';
				
			

ملف env.

لا تنسي ان تقوم باستدعاء الفولدر الخاص بالمشروع file / open folder ثم اختيار فولدر customer ليظهر لك جميع محتوياتة داخل البرنامج بعد ذلك نقوم بتعديل ملف env. ليصبح بالشكل التالي

هنا قمنا بانشاء بيانات قاعدة البيانات حيث اننا نستخدم mysql باسم قاعدة بيانات customer_db برقم بورت 3306 واسم مستخدم root وبدون باسوورد علي هوست 127.0.0.1

انشاء موديل وكونترولر وميجريشن

الان سنقوم بانشاء وحدة تحكم controller و نموذج model باسم Customers و ترحيل البيانات 

				
					php artisan make:model Customers -mcr
				
			

migrate

نذهب الي موقع الترحيل وهو الموجود داخل database/migrations باسم 2024_08_18_162521_create_customers_table.php ثم نقوم بالتعديل علية ليصبح بالشكل الاتي

				
					return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('customers', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('address');
            $table->string('phone');
            $table->string('email')->unique();
            $table->timestamps();
        });
    }
				
			

بعد ذلك نقم بترحيل البيانات الي قاعدة البيانات باستخدام الامر التالي

				
					php artisan migrate
				
			

سيطلب منك انشاء قاعدة بيانات باسم customers_db وتقوم باختيار yes لكي يقوم بانشاءها بالشكل الاتي

model

نذهب الي النموذج Customers داخل  App/Models ثم نقوم باضافة الكود التالي ليصبح بالشكل الاتي

				
					class Customers extends Model
{
    use HasFactory;
    protected $table = 'customers';
    protected $primaryKey = 'id';
    protected $fillable = [
    'name',
    'address',
    'phone',
    'email',
];
}
				
			

controller

نذهب الي وحدة التحكم CustomersController داخل  App/Controller ثم نقوم باضافة الكود التالي ليصبح بالشكل الاتي

				
					<?php

namespace App\Http\Controllers;

use App\Models\Customers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Inertia\Inertia;
use Inertia\Response;

class CustomersController extends Controller
{
    public function index(): Response
    {
        return Inertia::render('Customers/Index', [
            'customers' => Customers::all(),
        ]);
    }

    public function create()
    {
        //
    }

    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
           'name' => 'required|string|max:255',
            'address' => 'required|string|max:255',
            'phone' => 'required|string|max:15',
            'email' => 'required|email|max:255',
        ]);

        Customers::create($validated);
        return back()-> with('message','Customers Added successfully');
    }
    public function show(Customers $customer)
    {
        //
    }
    public function edit(Customers $customer)
    {
        //
    }

    public function update(Request $request, $id): RedirectResponse
    {
        
        $request->validate([
            'name' => 'required|string|max:255',
            'phone' => 'required|string|max:255',
            'address' => 'required|string|max:255',
        ]);

        $customer = Customers::findOrFail($id);
        $customer->update($request->only('name', 'phone', 'address', 'message'));
 
        return back()-> with('message','Customers Added successfully');
    }

    public function destroy(Customers $customer)
{
    $customer->delete();

    // إعادة المستخدم إلى الصفحة السابقة مع رسالة نجاح
    return back()->with([
        'message' => 'Customer deleted successfully',
        'status' => 'success'
    ]);
}


}

				
			

في هذا الكود، يتم تعريف CustomersController الذي يتولى إدارة عمليات CRUD (إنشاء، قراءة، تحديث، وحذف) للعملاء (Customers) في التطبيق باستخدام Laravel و Inertia.js.

الشــــــــــــرح :

1. دالة index

هذه الدالة تقوم بعرض جميع العملاء المسجلين في قاعدة البيانات.

تستخدم Inertia.js لتمرير البيانات إلى الواجهة الأمامية React. يتم جلب جميع العملاء من قاعدة البيانات باستخدام Customers::all()، ويتم إرسالها إلى صفحة Customers/Index.

2. دالة store

هذه الدالة تستقبل بيانات العميل الجديد من النموذج عبر Request.

يتم التحقق من البيانات المدخلة باستخدام validate للتأكد من أنها تتوافق مع القواعد المحددة (مثل أن الاسم مطلوب، وأن البريد الإلكتروني يجب أن يكون صالحًا، وما إلى ذلك).

بعد التحقق، يتم إنشاء عميل جديد في قاعدة البيانات باستخدام Customers::create.

يتم إعادة توجيه المستخدم إلى الصفحة السابقة مع رسالة تفيد بأن العميل تم إضافته بنجاح.

3. دالة update

هذه الدالة مسؤولة عن تحديث بيانات عميل معين.

يتم التحقق من البيانات المدخلة باستخدام validate.

يتم العثور على العميل المراد تحديثه باستخدام findOrFail، والتي ستعيد خطأ إذا لم يتم العثور على العميل.

ثم يتم تحديث بيانات العميل باستخدام update مع البيانات الجديدة.

يتم إعادة توجيه المستخدم إلى الصفحة السابقة مع رسالة تفيد بأن العميل تم تحديثه بنجاح.

4. دالة destroy

هذه الدالة مسؤولة عن حذف عميل معين.

يتم العثور على العميل المحدد باستخدام Customers ومن ثم يتم حذفه باستخدام delete.

يتم إعادة توجيه المستخدم إلى الصفحة السابقة مع رسالة نجاح تفيد بأن العميل تم حذفه بنجاح.

التوجهات web.php

نذهب الي الروت او التوجهات في routes ملف web.php ثم نقوم باضافة الكود التالي

				
					
use App\Http\Controllers\CustomersController;//يضاف في الاعلي للاستدعاء


Route::resource('customers', CustomersController::class)
    ->middleware(['auth', 'verified']);
				
			

والان لنري ما وصلنا اليه حتي الان وذلك باستخدام الامر التالي

				
					npm run dev
//ثم الدخول علي الرابط 
//127.0.0.1/customer/public
				
			

http://127.0.0.1/customer/public

ثم نقم بالتسجيل والدخول علي صفحة dashboard تلقائيا

resuilt
welcome
laravel 11-react-inertia 8
register
laravel 11-react-inertia 9
dashboard

انشاء صفحات Customers

في البداية يجب التوضيح ان جميع الصفحات التي يتم انشاءها ستكون داخل resources/js  وذلك لان عند استخدام Inertia.js مع Laravel و React أو Vue.js، يتم وضع مكونات الواجهة الأمامية (مثل الصفحات) داخل resources/js/Pages أو resources/js/Components. وهذا يتيح لـ Inertia.js العمل بسلاسة مع مكونات React أو Vue.js وتحميل الصفحات مباشرة من هذه الملفات.

انشاء صفحة index.jsx

سوف نقوم بانشاء فولدر داخل resources/js/pages باسم Customers ثم نقوم بانشاء ملف داخله باسم index.jsx ويحتوي علي الاتي

				
					//resources/js/pages/Customers/Index.jsx
import React, { useState } from 'react';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import InputError from '@/Components/InputError';
import PrimaryButton from '@/Components/PrimaryButton';
import { useForm, Head } from '@inertiajs/react';
import Customer from '@/Components/Customer';
import CustomerModal from '@/Components/CustomerModal'; // استيراد الـ Modal


export default function Index({ auth, customers }) {
    const [isModalOpen, setIsModalOpen] = useState(false);
    const { data, setData, post, processing, reset, errors } = 
        useForm({
            name: '',
            address: '',
            phone: '',
            email: '',
            message: '',
    });
    const handleSubmit = (e) => {
        e.preventDefault();
        post(route('customers.store'), {
            onSuccess: () => {
                reset();
                setIsModalOpen(false);
            }, 
        });
    };

    return (
        <AuthenticatedLayout user={auth.user}
        header={<div className="flex justify-between items-center">
                <h2 className="font-semibold text-xl text-gray-800 leading-tight">Customers</h2>
                <button 
                    onClick={() => setIsModalOpen(true)} 
                    className="bg-blue-500 text-white px-4 py-2 rounded">
                    <i className="fas fa-plus"></i>
                </button>
                </div>
                }
            >
            <Head title="Customer" />
            <CustomerModal 
                isOpen={isModalOpen} 
                onClose={() => setIsModalOpen(false)} 
                onSubmit={handleSubmit}
                data={data}
                setData={setData}
            />
                <div class="container mx-auto p-4">
                <div class="grid grid-cols-1 md:grid-cols-12 gap-4"> 
                <div class="md:col-span-12 bg-white p-4 rounded shadow">
                <div className="overflow-x-auto sm:overflow-x-hidden">
                <table className="min-w-full divide-y divide-gray-200">
                    <thead className="bg-gray-50">
                        <tr>
                            <th scope="col" className="px-6 py-3 text-left text-xs bg-green-100 font-medium text-gray-900 uppercase tracking-wider">Name</th>
                            <th scope="col" className="px-6 py-3 text-left text-xs bg-green-100 font-medium text-gray-900 uppercase tracking-wider">Phone</th>
                            <th scope="col" className="px-6 py-3 text-left text-xs bg-green-100 font-medium text-gray-900 uppercase tracking-wider">Address</th>
                            <th scope="col" className="px-6 py-3 text-left text-xs bg-green-100 font-medium text-gray-900 uppercase tracking-wider">Email</th>
                            <th scope="col" className="px-6 py-3 text-left text-xs bg-green-100 font-medium text-gray-900 uppercase tracking-wider">Action</th>
                        </tr>
                    </thead>

                    {customers.length > 0 ? (
                            customers.map(c => (
                                <Customer key={c.id} c={c} />
                                    ))
                                ) : (
                                    <p className='py-12 font-medium text-right '>No customers found.</p>
                                )}
                    </table>
                    </div>
                    </div>
                </div>
                </div>
        </AuthenticatedLayout>
    );

}
				
			

انشاء صفحة customer.jsx

وداخل فولدر component الموجود داخل resources/js نقوم بانشاء فايل Customer.jsx ثم نقوم بطباعة الكود التالي داخلة

				
					//resources/js/Components/Customer.jsx
import React, { useState } from 'react';
import Dropdown from '@/Components/Dropdown';
import InputError from '@/Components/InputError';
import PrimaryButton from '@/Components/PrimaryButton';
import { useForm, usePage } from '@inertiajs/react';
import Swal from 'sweetalert2';
import axios from 'axios';

export default function Customer({ c }) {
    const { auth } = usePage().props;

    const [editing, setEditing] = useState(false);
    const [modalIsOpen, setModalIsOpen] = useState(false);
    const [confirmDelete, setConfirmDelete] = useState(false);

    const { data, setData, patch, processing, reset, errors } = useForm({
        name: c.name,
        phone: c.phone,
        address: c.address,
        message: c.message,
    });

    const submit = (e) => {
        e.preventDefault();
        patch(route('customers.update', c.id), {
            data: {
                name: data.name,
                phone: data.phone,
                address: data.address,
                message: data.message,
            },
            onSuccess: () => setEditing(false),
        });
    };

    return (
        <>
            <tbody className="bg-white divide-y divide-gray-200">
                <tr>
                    {editing ? (
                        <td colSpan="4" className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
                            <form onSubmit={submit}>
                                <div className="mt-4">
                                    <textarea value={data.name} onChange={e => setData('name', e.target.value)} className="w-full text-gray-900 border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"></textarea>
                                    <InputError message={errors.name} className="mt-2" />
                                </div>
                                <div className="mt-4">
                                    <textarea value={data.phone} onChange={e => setData('phone', e.target.value)} className="w-full text-gray-900 border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"></textarea>
                                    <InputError message={errors.phone} className="mt-2" />
                                </div>
                                <div className="mt-4">
                                    <textarea value={data.address} onChange={e => setData('address', e.target.value)} className="w-full text-gray-900 border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"></textarea>
                                    <InputError message={errors.address} className="mt-2" />
                                </div>
                                <div className="space-x-2 mt-4">
                                    <PrimaryButton className="mt-4">Save</PrimaryButton>
                                    <button className="mt-4" onClick={() => { setEditing(false); reset(); }}>Cancel</button>
                                </div>
                            </form>
                        </td>
                    ) : (
                        <>
                            <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{c.name}</td>
                            <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{c.phone}</td>
                            <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{c.address}</td>
                            <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{c.email}</td>
                            <td className="px-12 whitespace-nowrap text-sm text-gray-500">
                                <div className="flex justify-between items-center">                                   
                                            <button className="block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:bg-gray-100 transition duration-150 ease-in-out" onClick={() => setEditing(true)}>
                                            <i className='fas fa-pen hover:text-green-700'></i>
                                            </button>
                                            <Dropdown.Link as="button" onClick={() => {
                                              axios.delete(route('customers.destroy', c.id))                                       
                                                .catch((error) => {
                                                  console.error(error);
                                                  Swal.fire({
                                                    title: 'Deleted!',
                                                    text: 'Customer has been deleted successfully!',
                                                    icon: 'success',
                                                    timer: 2000,
                                                    showConfirmButton: false,
                                                  });
                                                });
                                            }}>
                                              <i className='fas fa-trash-alt text-red-600 hover:text-red-800'></i>
                                            </Dropdown.Link>
                                </div>
                            </td>
                        </>
                    )}
                </tr>
            </tbody>
        </>
    );
}



				
			

من فولدر layouts ندخل علي فايل AuthenticatedLayout.jsx ثم نقوم باضافة الكود التالي تحت NavLink الخاص بصفحة dashboard

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

 ثم نقوم باضافة الكود التالي تحت ResponsiveNavLink الخاص بصفحة dashboard

				
					<ResponsiveNavLink href={route('customers.index')} active={route().current('customers.index')}>
    Customer
</ResponsiveNavLink>
				
			

انشاء صفحة CustomerModal.jsx

في React.js، المودال (Modal) هو نافذة منبثقة تظهر فوق محتوى الصفحة الرئيسية وتستخدم لعرض معلومات أو لتنفيذ إجراءات دون مغادرة الصفحة. تُستخدم المودالات عادةً للتأكيدات، النماذج، الرسائل التحذيرية، و الكثيــــــــــــــر

هنا ننشيء صفحة لاضافة العملاء باستخدام مودال من فولدر Component نقوم بانشاء فايل باسم CustomerModel.jsx ثم نقوم باضافة الكود التالي بداخلة

				
					//resources/js/Components/CustomerModal.jsx

import React from 'react';

export default function CustomerModal({ isOpen, onClose, onSubmit, data, setData }) {
    if (!isOpen) return null;

    return (
        <div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
            <div className="bg-white p-6 rounded shadow-lg w-full max-w-md md:max-w-lg lg:max-w-xl">
                <h3 className="text-lg font-bold mb-4">Add New Customer</h3>
                <form onSubmit={onSubmit}>
                    <div className="mb-4">
                        <label className="block text-gray-700">Name</label>
                        <input 
                            type="text" 
                            value={data.name} 
                            onChange={e => setData('name', e.target.value)} 
                            className="w-full border border-gray-300 p-2 rounded" 
                            required 
                        />
                    </div>
                    <div className="mb-4">
                        <label className="block text-gray-700">Address</label>
                        <input 
                            type="text" 
                            value={data.address} 
                            onChange={e => setData('address', e.target.value)} 
                            className="w-full border border-gray-300 p-2 rounded" 
                            required 
                        />
                    </div>
                    <div className="mb-4">
                        <label className="block text-gray-700">Phone</label>
                        <input 
                            type="text" 
                            value={data.phone} 
                            onChange={e => setData('phone', e.target.value)} 
                            className="w-full border border-gray-300 p-2 rounded" 
                            required 
                        />
                    </div>
                    <div className="mb-4">
                        <label className="block text-gray-700">Email</label>
                        <input 
                            type="email" 
                            value={data.email} 
                            onChange={e => setData('email', e.target.value)} 
                            className="w-full border border-gray-300 p-2 rounded" 
                            required 
                        />
                    </div>
                    <div className="flex justify-end">
                        <button 
                            type="button" 
                            onClick={onClose} 
                            className="bg-gray-500 text-white px-4 py-2 rounded mr-2">
                            Cancel
                        </button>
                        <button 
                            type="submit" 
                            className="bg-blue-500 text-white px-4 py-2 rounded">
                            Save
                        </button>
                    </div>
                </form>
            </div>
        </div>
    );
}

				
			

واخيرا لتجميع البرنامج استخدم الامر التالي

				
					
npm run build
				
			

قد تجد بعض الملاحظات البسيطة وتسبب اخطاء برمجية مثل بداية الاسماء بحروف كبيرة يجب مراعاة ذلك جيدا و اعلم ان اكتشاف الاخطاء هو بداية تعلم البرمجة

  • النموذج (Model) يمثل كيانًا واحدًا وبالتالي يستخدم بصيغة المفرد.
  • الجدول (Table) يحتوي على العديد من الكيانات، لذا يتم استخدام صيغة الجمع لتعكس ذلك.
  • وحدة التحكم (Controller) غالبًا ما تتعامل مع عدة كيانات، لذا يكون اسمها بصيغة الجمع أو المفرد حسب تفضيل المطور، على الرغم من أن صيغة المفرد شائعة.

Resuilt

laravel 11-react-inertia 10
Resuilt
laravel 11-react-inertia 11
Add
laravel 11-react-inertia 12
Edit And Delete

Sharing to

Facebook
Twitter
LinkedIn

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

Scroll to Top