لو عايز تعمل برنامج لتصميم جمعية شهرية باستخدام Laravel، الفكرة هنا إنك بتعمل تطبيق ويب بحيث يكون لكل واحد من الاعضاء اكثر من دور ومبلغ شهري يدفعه والدور يشيل اكثر من عضو . الجمعية دي تعتبر مفهوم واسع، بتلاقيها في ثقافات كتير وأحيانًا بمعاني مختلفة، زي الصندوق العائلي، أو زي جمعيات الأصدقاء اللي كل شهر حد منهم بياخد “النوبة” بتاعته. Laravel هيسهل عليك الموضوع جدًا لإنه بيوفر الأدوات اللي هتساعدك تدير قاعدة البيانات، وتعمل روابط بين الأعضاء والجمعيات والأدوار بشكل منظم. وكمان تقدر تتحكم في المبالغ الشهرية لكل عضو ولكل دور، ودا بيساعد الجمعية إنها تشتغل بشكل منظم وسهل الفهم لكل الناس المشتركة فيها.
الجمعية الشهرية باستخدام لارافيل
المتطلبات
تكلمنا في السابق عن الادوات المستخدمة مثل فيجوال ستوديو كود كمحرر لكتابة الكود و composer لتثبيت لارافيل ونود جي اس لرياكت وهنا نستخدم رياكت للاستفادة من قوتة في الواجهه الامامية و عدم التحميل الكامل للصفحة اثناء ادخال بيانات جديدة
تثبيت laravel
composer create-project laravel/laravel Associations
بعد التثبيت الدخول علي فولدر المشروع بالكود التالي
cd Associations
تثبيت Breeze
composer require laravel/breeze --dev
تثبيت React
php artisan breeze:install react
ضبط اعدادات ملف env.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=association_db
DB_USERNAME=root
DB_PASSWORD=
قمنا بانشاء قاعدة بيانات باسم association_db ونقوم بترحيلها باستخدام migrate
php artisan migrate
سيطلب منك انشاء قاعدة بيانات جديدة باسم association_db
بعد الموافقة علي انشاء قاعدة البيانات نكون قد اتممنا الجزء الاول وهو authentication بهذا الشكل
اضافة animate
npm install animate.css --save
بعد ذلك نضع الكود التالي في صفحة app.jsx لاستيراد animation
import 'animate.css';
انشاء جدول الجمعيات Associations
سوف نقوم بانشاء موديل وكنترولر وميجريشن للجمعيات باتباع التالي
php artisan make:model Association -mrc
المايجريشن migrate
سوف يكون الجدول الخاص بالجمعيات عبارة عن اسم الجمعية وعدد الاشهر واجمالي المبلغ الشهري علي اعتبار ان الدور كل شهر من database اختيار migrations ثم اختيار جدول associations ويكون بالشكل التالي
public function up(): void
{
Schema::create('associations', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('total_months');
$table->decimal('role_price', 10, 2);
$table->timestamps();
});
}
ثم ترحيل الجدول لقواعد البيانات باستخدام الامر التالي
php artisan migrate
الموديل Model
من app اختيار Models ثم Association واضافة الكود التالي
class Association extends Model
{
protected $fillable = ['name', 'total_months', 'role_price'];
}
كونترولر Controller
من app اختيار http ثم controller ثم AssociationController واضافة الكود التالي للصفحة بحيث يتم استيراد الموديل واضافة index و store
namespace App\Http\Controllers;
use App\Models\Association;
use Illuminate\Http\Request;
use Inertia\Inertia;
class AssociationController extends Controller
{
public function index()
{
$associations = Association::all();
return Inertia::render('Associations/Index', [
'associations' => $associations
]);
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'total_months' => 'required|integer|min:1',
'role_price' => 'required|numeric|min:0',
]);
$group = Association::create([
'name' => $validated['name'],
'total_months' => $validated['total_months'],
'role_price' => $validated['role_price'],
]);
return redirect()->route('associations.index');
}
التوجهات (الراوت) Routes
من routes نذهب الي web واضافة الكود
//namespace
use App\Http\Controllers\AssociationController;
// code
Route::resource('associations', AssociationController::class)
->middleware(['auth', 'verified']);
الواجهه الامامية لصفحة الجمعيات Associations/Index
من داخل resources\js\Pages نقوم بانشاء Associations وداخلة ملف 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 Associations({ auth, associations }) {
const { data, setData, post, processing, reset, errors } = useForm({
name: '',
total_months: '',
role_price: ''
});
const handleSubmit = (e) => {
e.preventDefault();
post(route('associations.store'), { onSuccess: () => reset() });
};
return (
{associations.map((association, index) => (
{association.name}
{association.total_months} شهر
{association.role_price} في الشهر
))}
لمشاهدة ما تم تنفيذة استخدم الامر التالي ثم قم بادخال بيانات للتاكد من ان الكود سليم
npm run dev
يجب اضافة اسم الصفحة لل navbar لسهولة التصفح وبالتالي يمكنك اضافة باقي الصفحات باتباعك نفس الطريقة
نذهب الي مجلد Layouts ومن داخلة نختار AuthenticatedLayout.jsx ثم نضيف الكود التالي بعد كود dashboard وتعديل الروت الخاص بالصفحة بالشكل التالي
Dashboard
Association
قمنا باضافة navlink خاص بصفحة association بعد صفحة dashboard وتعديل الروت الخاص بة وسنقوم ايضا باضافتة مرة اخري في نفس الصفحة لكن ResponsiveNavLink
Dashboard
Association
انشاء جدول الادوار Roles
php artisan make:model Role -mrc
المايجريشن migrate
سيرتبط جدول الادوار بجدول الجمعيات حيث ان لكل جمعية اكثر من دور بالتالي سوف يتم انشاء الادوار الخاصة بكل جمعية بمجرد تسجيل الجمعية واختيار عدد الادوار وسيتم احتساب اول شهر في الجمعية من تاريخ تسجيل البيان. لنري ذلك
public function up(): void
{
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('start_date')->nullable();
$table->foreignId('association_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
// للترحيل
php artisan migrate
الموديل Model
// role model
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
protected $fillable = [
'name',
'association_id',
'start_date',
];
public function associations()
{
return $this->belongsTo(Association::class);
}
}
ويجب اضافة الكود التالي في موديل Association للعلاقة بين الجمعية والادوار
// association model
public function roles()
{
return $this->hasMany(Role::class);
}
كونترولر Controller
سوف نضيف الي index في RoleController كل تفاصيل الادوار الخاصة بجمعية معينة بالشكل الاتي
use App\Models\Association;
use Inertia\Inertia;
// code
public function index()
{
$associations = Association::with(['roles'])->get();
return Inertia::render('Roles/Index', [
'associations' => $associations,
]);
}
بعد ذلك نقوم باضافة الادوار الي store في AssociationController الذي يقوم باضافة الادوار الي الجمعية بعد ادخال بيانات الجمعية لتصبح بالشكل التالي
// استدعاء
use Carbon\Carbon;
use Illuminate\Http\RedirectResponse;
use App\Models\Role;
// code
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'total_months' => 'required|integer|min:1',
'role_price' => 'required|numeric|min:0',
]);
$association = Association::create([
'name' => $validated['name'],
'total_months' => $validated['total_months'],
'role_price' => $validated['role_price'],
]);
// لإضافة الأدوار عند إنشاء جمعية جديدة مع تواريخ البداية لكل دور
$role_names = [
'الأول', 'الثاني', 'الثالث', 'الرابع', 'الخامس', 'السادس', 'السابع', 'الثامن', 'التاسع', 'العاشر',
'الحادي عشر', 'الثاني عشر', 'الثالث عشر', 'الرابع عشر', 'الخامس عشر', 'السادس عشر', 'السابع عشر',
'الثامن عشر', 'التاسع عشر', 'العشرون'
];
// تاريخ بداية الأدوار
$startDate = Carbon::now()->startOfMonth(); // البداية من بداية الشهر الحالي
for ($i = 0; $i < $validated['total_months']; $i++) {
// زيادة عدد الأشهر لكل دور
$roleDate = $startDate->copy()->addMonths($i)->format('m-Y');
Role::create([
'name' => $role_names[$i] ?? 'الدور ' . ($i + 1),
'association_id' => $association->id,
'start_date' => $roleDate, // حفظ الشهر والسنة بدون تكرار
]);
}
return redirect(route('associations.index'));
}
التوجهات (الراوت) Routes
//namespace
use App\Http\Controllers\RoleController;
//code
Route::resource('roles', RoleController::class)
->middleware(['auth', 'verified']);
الواجهه الامامية لصفحة الادوار Roles/Index
//resources\js\Pages\Roles\Index.jsx
import React, { useState } from 'react';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head, useForm } from '@inertiajs/react';
export default function Roles({ auth, associations }) {
return (
{associations.map((association, index) => (
{association.name}
عدد الأشهر: {association.total_months}
مبلغ الدور: {association.role_price}
أدوار الجمعية
{association.roles.map((role, roleIndex) => (
openModal(association, role)}
>
{role.name}
{role.start_date}
))}
))}
بعد ذلك اضافة navlink خاص بصفحة roles مثل الصفحة السابقة
npm run dev
بهذا قد قمنا بتنفيذ الجزء الخاص باضافة الجمعية وعدد الادوار بها مع احتساب اول شهر فيها من حين ادخال البيانات ونظرا لطول المقال تم تقسيمه الي جزئين