part 2

الجمعية الشهرية باستخدام لارافيل – الجزء الثاني

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

الجمعية الشهرية باستخدام لارافيل - الجزء الثاني

انشاء جدول الاعضاء Members

				
					php artisan make:model Member -mrc
				
			

المايجريشن migrate

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

				
					public function up(): void
    {
        Schema::create('members', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
    }
				
			
				
					// للترحيل 
php artisan migrate
				
			

الموديل Model

				
					// member model

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Member extends Model
{
    protected $fillable = [
        'name', 
    ];

}
				
			

كونترولر Controller

سوف نضيف الي index , store  في MemberController اسماء الاعضاء بالشكل الاتي

				
					use App\Models\Member;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;

class MemberController extends Controller
{
    public function index()
    {
        $members = Member::all();

        return Inertia::render('Members/Index', [
            'members' => $members
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
        ]);
 
        $members = Member::create([
            'name' => $validated['name'],
        ]);

        return redirect(route('members.index'));
    }
				
			

التوجهات (الراوت) Routes

				
					//namespace
use App\Http\Controllers\MemberController;
//code
Route::resource('members', MemberController::class)
     ->middleware(['auth', 'verified']);

				
			

الواجهه الامامية لصفحة الاعضاء Members/Index

بظهر في اعلي الصفحة فورم لاضافة الاعضاء وكل عضو يضاف يظهر اسمة تحت الفورم بعرض الصفحة ب fade in

				
					//resources\js\Pages\Members\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, Link } from '@inertiajs/react';

export default function Members ({ auth, members }) {

    const { data, setData, post, processing, reset, errors } = useForm({
        name: '',
    });

    const handleSubmit = (e) => {
        e.preventDefault();
        post(route('members.store'), { onSuccess: () => reset() });
    };

    return (
        <AuthenticatedLayout user={auth.user}>
            <div style={{ direction: 'rtl' }}>
            <Head title="Members" />
            <div className="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8 animate__animated animate__fadeIn">
                        <form onSubmit={handleSubmit}>
                            <input
                                type="text"
                                value={data.name}
                                onChange={e => setData('name', e.target.value)}
                                placeholder="اضافة عضو جديد"
                                className="custom-input w-full border border-gray-300 p-2 rounded"
                            />

                            <InputError message={errors.name} className="mt-2" />
                            <PrimaryButton className="mt-4" disabled={processing}>Add Member</PrimaryButton>
                        </form>
                    </div>

                    <div className="flex flex-wrap justify-center mt-4">
                        {members.map((member, index) => (
                            <div
                                className={`animate__animated animate__fadeInUp m-12 transition-transform duration-300 hover:scale-105`}
                                style={{ animationDelay: `${index * 0.3}s` }}
                                key={member.id}
                            >
                                <div className="bg-white shadow-md rounded-lg p-8 flex flex-row relative w-72 hover:bg-yellow-100 hover:text-blue-700">
                                    <div className="flex-grow text-right pr-4">
                                        <div className="text-2xl font-bold">
                                        <Link href="" className="text-blue-600 hover:text-red-500">
                                            {member.name}
                                        </Link>
                                        </div>
                                    </div>
                                    <div className="absolute bottom-2 left-2">
                                        
                                    </div>
                                </div>
                            </div>
                        ))}
                    </div>

            </div>
            </AuthenticatedLayout>
        );
}
				
			

بعد ذلك اضافة navlink خاص بصفحة Member مثل صفحة Associations

				
					npm run dev
				
			

انشاء الجدول الوسيط AssociationMember

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

				
					php artisan make:model AssociationMember -mrc
				
			

المايجريشن migrate

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

				
					 public function up(): void
    {
        Schema::create('association_member', function (Blueprint $table) {
            $table->id();
            $table->foreignId('member_id')->constrained()->onDelete('cascade');
            $table->foreignId('association_id')->constrained()->onDelete('cascade');
            $table->decimal('total_amount', 10, 2); // المبلغ الإجمالي
            $table->timestamps();
        });
    }
				
			

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

				
					php artisan migrate
				
			

الموديل Model

من app اختيار Models ثم AssociationMember واضافة الكود التالي

				
					 public function association()
    {
        return $this->belongsTo(Association::class);
    }
    
     public function member()
    {
        return $this->belongsTo(Member::class);
    }
				
			

من app اختيار Models ثم Association واضافة الكود التالي

				
					    public function members()
{
    return $this->belongsToMany(Member::class, 'association_member')
                ->withPivot('id', 'total_amount') // لتضمين عمود 'id' من الجدول الوسيط
                ->withTimestamps();
}
				
			

كونترولر Controller

من app اختيار http ثم controller ثم AssociationController واضافة الكود التالي

				
					  
use App\Models\Member;

  public function storeMemberAssociation(Request $request, Association $association)
{
    $validated = $request->validate([
        'member_id' => 'required|exists:members,id',
        'total_amount' => 'required|numeric|min:0',
    ]);

    if ($association->members()->where('member_id', $validated['member_id'])->exists()) {
        return back()->withErrors(['member_id' => 'Member is already part of this group.']);
    }

    $association->members()->attach($validated['member_id'], ['total_amount' => $validated['total_amount']]);

    return redirect()->route('associations.show', $association->id)->with('success', 'Member added to group successfully.');
}

public function show($id)
    {
        $associations = Association::with(['roles', 'members'])->findOrFail($id);
        $roles = Role::all();  
        $members = Member::all(); 

        return Inertia::render('Associations/Show', [
            'associations' => $associations,
            'members' => $members,
            'roles' => $roles, 
        ]);
    }
				
			

التوجهات (الراوت) Routes

من routes نذهب الي web واضافة الكود

				
					Route::post('/associations/{association}/members', [AssociationController::class, 'storeMemberAssociation'])->name('associations.members.store');

				
			

صفحة اضافة العضو و مبلغة Associations/Show

				
					import React, { useState } from 'react';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import InputError from '@/Components/InputError';
import PrimaryButton from '@/Components/PrimaryButton';
import { useForm, Head, Link } from '@inertiajs/react';

export default function Associations ({ auth, associations, members }) {

    const { data, setData, post, processing, errors, reset } = useForm({
        member_id: '',
        total_amount: '',
    });

    const totalAmount = associations.members.reduce((acc, member) => {
        return acc + parseFloat(member.pivot?.total_amount || 0); // تحويل المبلغ إلى رقم
    }, 0);
    
    const handleSubmit = (e) => {
        e.preventDefault();
        post(route('associations.members.store', associations.id), {
            onSuccess: () => reset(),
        });
    };

    
    return (
        <AuthenticatedLayout user={auth.user}>
            <Head title="Members Amount" />
            <div className="w-full p-6 sm:p-6 lg:p-12 animate__animated animate__fadeIn" style={{ direction: 'rtl' }}>
                <h4 className="text-lg font-bold text-center">اضافة العضو و اجمالي مبلغة في الجمعية</h4>
                <form onSubmit={handleSubmit} className="max-w-2xl mx-auto mt-6 animate__animated animate__fadeIn">
                    <select
                        value={data.member_id}
                        onChange={e => setData('member_id', e.target.value)}
                        className="custom-input text-center w-full border border-gray-300 p-2 rounded"
                    >
                        <option value=""> اختار العضو</option>
                        {members.map((member) => (
                            <option key={member.id} value={member.id}>
                                {member.name}
                            </option>
                        ))}
                    </select>
                    {errors.member_id && <div className="text-red-500">{errors.member_id}</div>}

                    <input
                        type="number"
                        value={data.total_amount}
                        onChange={e => setData('total_amount', e.target.value)}
                        placeholder="المبلغ"
                        className="custom-input text-center mt-4 w-full border border-gray-300 p-2 rounded"
                    />
                    <InputError message={errors.total_amount} className="mt-2" />
                    <PrimaryButton type="submit" className="btn btn-primary mt-4" disabled={processing}>
                        Add Member
                    </PrimaryButton>
                </form>

                <div className='text-center'>
                    <h2 className="text-xl font-bold p-4">{associations.name}</h2>
                    <p>عدد الاشهر :  {associations.total_months}</p>
                    <p>مبلغ الدور : {associations.role_price}</p>
                </div>

                <h3 className="text-xl mt-4 font-bold">اعضاء الجمعية </h3>
                    <p>إجمالي المبلغ: {totalAmount}</p>
                
               <div className="flex flex-wrap justify-center mt-4">
                                    {associations.members.map((member, memberIndex) => (
                                        <div
                                            className="animate__animated animate__fadeInUp m-4 transition-transform duration-300 hover:scale-105"
                                            style={{ animationDelay: `${memberIndex * 0.3}s` }}
                                            key={member.pivot.id}
                                        >
                                            <div className="bg-white shadow-md rounded-lg p-4 flex flex-row relative w-80 hover:bg-yellow-100 hover:text-blue-700">
                                            <div className="flex-grow text-right pr-2">
                                                <div className="flex-grow text-center">
                                                    <div className="text-1xl">{member.name}</div>
                                                    <div className="text-1xl">{member.pivot.total_amount}</div>
                                                </div>
                                                <div className="absolute bottom-2 left-2">
                                                <Link as="button"
                                                    className="text-red-500 hover:text-red-700"
                                                    href={route('member-amount.destroy', member.pivot.id)} method="delete"
                                                >
                                                      <i className="fas fa-trash"></i>
                                                </Link>
                                                </div>
                                            </div>
                                            </div>
                                        </div>
                                    ))}
                                </div>

                
            </div>
        </AuthenticatedLayout>
    );
}

				
			

بعد ذلك نقوم باستبدال اللينك التالي في صفحة Associations/Index للدخول منة الي صفحة اضافة العضو للجمعية ومبلغة Show

				
					<Link href={route('associations.show', association.id)} className="text-blue-600 hover:text-blue-900">
          {association.name}
    </Link>
				
			

انشاء الجدول الوسيط و الاخير RoleAssociationMember

سوف نقوم بانشاء جدول يضم id الخاص بالجدول الوسيط السابق Association_member مع الدور role مع المبلغ المدفوع في الدور وبذلك يكون لدينا العضو باجمالي المبلغ المدفوع منة في الجمعية مع الادوار المحددة لة في الجمعية مع مبلغة في كل دور 

				
					php artisan make:migration create_association_member_role_table --create=association_member_role
				
			

المايجريشن migrate

				
					public function up(): void
    {
        Schema::create('association_member_role', function (Blueprint $table) {
            $table->id();
            $table->foreignId('association_member_id')->constrained('association_member')->onDelete('cascade');
            $table->foreignId('role_id')->constrained()->onDelete('cascade');
            $table->decimal('paid_amount', 10, 2); // المبلغ المدفوع لكل دور
            $table->timestamps();
        });
    }
				
			

الموديل Model

ويجب اضافة الكود التالي في موديل Association للعلاقة بين الجمعية والادوار

				
					// في App\Models\Association.php
public function association_members()
{
    return $this->hasMany(AssociationMember::class);
}
				
			

ويجب اضافة الكود التالي في موديل AssociationMember للعلاقة بين الادوار و الجدول الوسيط 

				
					public function roles()
    {
        return $this->belongsToMany(Role::class, 'association_member_role')
                    ->withPivot('paid_amount')
                    ->withTimestamps();
    }
				
			

ويجب اضافة الكود التالي في موديل Member للعلاقة بين الجمعية و العضو في الجدول الوسيط 

				
					 public function associations()
{
    return $this->belongsToMany(Association::class, 'association_member')
                ->withPivot('id','total_amount')
                ->withTimestamps();
}
				
			

ويجب اضافة الكود التالي في موديل Role للعلاقة بين الدور في الجداول الوسيطة و العضو في الجدول الوسيط 

				
					public function association_members()
    {
        return $this->belongsToMany(AssociationMember::class, 'association_member_role')
                    ->withPivot('paid_amount')
                    ->with('member') // تضمين بيانات العضو
                    ->withTimestamps();
    }
				
			

كونترولر Controller

تعديل دالة index في RoleController بالشكل التالي

				
					public function index()
    {
        $associations = Association::with(['roles', 'members' => function($query) {
            $query->select('members.id', 'name', 'association_member.id as association_member_id');
            },'roles.association_members' => function ($query) {
                $query->withPivot('paid_amount');
            }
            ])->get();
            
        $roles = Role::with(['association_members.member'])->get();

        return Inertia::render('Roles/Index', [
            'associations' => $associations,
        ]);
    }

				
			

يمكنك اضافة التعديل والحذف بالرجوع الي الدروس السابقة مثال

مصادر خارجية

اترك تعليقاً

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

Scroll to Top