نظام التوجيه (Routing) في Laravel 11 — من الأساسيات إلى الاحتراف
1. أين تُعرَّف مسارات موقعك في Laravel؟
كل مسار (Route) في Laravel يُعرَّف في مجلد routes/.
في Laravel 11، ستجد ملفين رئيسيين تستخدمهما يومياً:
routes/
├── web.php ← مسارات الموقع العادي (متصفح، جلسات، CSRF)
└── api.php ← مسارات الـ API (بدون جلسات، يعتمد Tokens)
هذا الدرس يتمحور حول routes/web.php — الملف الذي يُعرِّف المسارات التي يصل إليها المستخدم عبر متصفحه.
كل رابط تراه في شريط العنوان يمر عبر هذا الملف أولاً.
المستخدم يكتب
https://mysite.com/articles ←
Laravel يفتح routes/web.php ←
يبحث عن مسار يطابق /articles بطريقة GET ←
يُنفّذ الكود المرتبط به ←
يُرجع الاستجابة للمستخدم.
2. أنواع أساليب HTTP وكيف تستخدمها
لكل عملية في الويب أسلوب HTTP (HTTP Method) مخصص. Laravel يوفر دالة مسار لكل أسلوب. هذه هي الأساليب التي ستستخدمها أكثر:
use Illuminate\Support\Facades\Route;
// GET — لعرض صفحة أو جلب بيانات
Route::get('/articles', function () {
return view('articles.index');
});
// POST — لاستلام بيانات نموذج (إرسال Form)
Route::post('/articles', function () {
// حفظ المقال الجديد
});
// PUT / PATCH — لتعديل بيانات موجودة
Route::put('/articles/{id}', function (string $id) {
// تحديث المقال
});
// DELETE — لحذف بيانات
Route::delete('/articles/{id}', function (string $id) {
// حذف المقال
});
| الأسلوب | الاستخدام | مثال |
|---|---|---|
GET | عرض صفحة أو جلب بيانات | /articles أو /articles/5 |
POST | إنشاء سجل جديد | إرسال نموذج تسجيل |
PUT | تحديث كامل لسجل موجود | تعديل كل حقول مقال |
PATCH | تحديث جزئي لسجل موجود | تغيير حالة المقال فقط |
DELETE | حذف سجل | حذف مقال برقم معين |
مسار يرد على كل الأساليب دفعة واحدة
في حالات نادرة، قد تحتاج مساراً يستجيب لأي أسلوب HTTP. Laravel يوفر هذا بـ Route::any():
// يستجيب لأي أسلوب HTTP
Route::any('/webhook', function () {
// معالجة webhook خارجي
});
// يستجيب فقط للأساليب المحددة
Route::match(['get', 'post'], '/contact', function () {
// عرض النموذج (GET) أو معالجته (POST)
});
3. بارامترات المسارات (Route Parameters) — الروابط الديناميكية
في الواقع العملي، روابطك لن تكون ثابتة. ستحتاج لروابط مثل
/articles/42 أو /users/ahmed/posts.
هذا هو دور Route Parameters — أجزاء متغيرة داخل الرابط:
بارامتر إلزامي
يُكتب بين أقواس معقوفة {}. إذا لم يُوفَّر هذا الجزء من الرابط، سيُعطي Laravel خطأ 404.
// /articles/42 ← $id = 42
// /articles/99 ← $id = 99
Route::get('/articles/{id}', function (string $id) {
return "عرض المقال رقم: " . $id;
});
// بارامترات متعددة
// /users/ahmed/posts/5
Route::get('/users/{username}/posts/{postId}', function (string $username, string $postId) {
return "مقالات المستخدم {$username}، المقال رقم {$postId}";
});
بارامتر اختياري
إذا أردت أن يكون الجزء اختيارياً، أضف ? بعد الاسم وحدد قيمة افتراضية في الدالة:
// /users ← $name = 'زائر'
// /users/ahmed ← $name = 'ahmed'
Route::get('/users/{name?}', function (string $name = 'زائر') {
return "مرحباً يا {$name}!";
});
تقييد البارامتر بـ Regex
يمكنك إضافة قيد لضمان أن البارامتر يتبع نمطاً معيناً (أرقام فقط، حروف فقط...). هذا يمنع الطلبات غير الصالحة من ان تصل لكودك.
// يقبل فقط الأرقام: /articles/42 ✓ | /articles/abc ✗
Route::get('/articles/{id}', function (string $id) {
return "مقال رقم: " . $id;
})->whereNumber('id');
// يقبل فقط الحروف الإنجليزية
Route::get('/users/{username}', function (string $username) {
return "ملف المستخدم: " . $username;
})->whereAlpha('username');
// قيد مخصص بـ Regex
Route::get('/category/{slug}', function (string $slug) {
return "تصنيف: " . $slug;
})->where('slug', '[a-z0-9\-]+');
إذا كنت تستخدم
$id في استعلام قاعدة البيانات، التقييد بـ whereNumber
يمنع أي محاولة لحقن سلاسل نصية غير متوقعة في مسارك.
طبقة دفاع إضافية بجانب Eloquent.
4. تسمية المسارات (Named Routes) — ليس مجرد رفاهية
تخيل أنك كتبت الرابط /user/profile في 30 مكان في قوالب Blade،
ثم قررت تغيير الرابط إلى /account/profile. ستحتاج لتعديل 30 مكان!
الحل: تسمية المسارات. تعطي كل مسار اسماً مستقلاً عن الرابط الفعلي. عند تغيير الرابط، يكفي تعديل سطر واحد في ملف المسارات وسيتحدث كل شيء تلقائياً.
Route::get('/user/profile', function () {
return view('profile');
})->name('profile');
Route::get('/articles/{id}', function (string $id) {
return view('articles.show', ['id' => $id]);
})->name('articles.show');
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
الآن، في أي مكان (Blade أو Controller)، أنشئ الروابط هكذا:
{{-- في ملف Blade --}}
<a href="{{ route('profile') }}">ملف المستخدم</a>
{{-- مسار مع بارامتر --}}
<a href="{{ route('articles.show', ['id' => 42]) }}">اقرأ المقال</a>
// في PHP (Controller مثلاً)
return redirect()->route('dashboard'); // إعادة توجيه لـ dashboard
العرف السائد هو استخدام النقطة للفصل:
resource.action.
مثال: articles.index، articles.show، articles.store، articles.destroy.
هذا النمط متوافق مع ما ينشئه Laravel تلقائياً عند استخدام Route::resource().
5. مجموعات المسارات (Route Groups) — تنظيم وقوة
عندما يكبر مشروعك، ستجد أن كثيراً من المسارات تشترك في خصائص متشابهة:
كلها تتطلب تسجيل دخول، أو كلها تبدأ بنفس البادئة /admin.
بدلاً من تكرار هذا في كل مسار، تُجمّعها في Group واحد.
تجميع برابط مشترك (Prefix)
// بدلاً من تكرار /admin في كل مسار:
Route::prefix('admin')->group(function () {
Route::get('/dashboard', function () { // ← /admin/dashboard
return view('admin.dashboard');
});
Route::get('/users', function () { // ← /admin/users
return view('admin.users');
});
Route::get('/settings', function () { // ← /admin/settings
return view('admin.settings');
});
});
تجميع بـ Middleware (الحماية)
هذا هو الاستخدام الأكثر شيوعاً للمجموعات — حماية مجموعة من المسارات بـ Middleware واحد. مثلاً: جميع صفحات لوحة التحكم تتطلب تسجيل الدخول:
// كل هذه المسارات تتطلب تسجيل الدخول
Route::middleware('auth')->group(function () {
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
Route::get('/profile', function () {
return view('profile');
})->name('profile');
Route::get('/settings', function () {
return view('settings');
})->name('settings');
});
// هذا المسار لا يتطلب تسجيل دخول (خارج المجموعة)
Route::get('/about', function () {
return view('about');
});
الجمع بين Prefix وMiddleware واسم مشترك
في المشاريع الحقيقية، تجمع خصائص متعددة معاً لتكوين مجموعات قوية ومنظمة:
Route::prefix('admin')
->middleware(['auth', 'admin']) // تسجيل دخول + صلاحية أدمن
->name('admin.') // أسماء المسارات ستبدأ بـ admin.
->group(function () {
Route::get('/dashboard', function () {
return view('admin.dashboard');
})->name('dashboard'); // الاسم الكامل: admin.dashboard
Route::get('/users', function () {
return view('admin.users');
})->name('users'); // الاسم الكامل: admin.users
});
6. توجيه المسارات للـ Controllers — الطريقة الاحترافية
في الأمثلة السابقة كتبنا الكود مباشرة داخل routes/web.php باستخدام Closures.
هذا مقبول للاختبار والمثل البسيطة، لكن في المشاريع الحقيقية
يجب أن يكون ملف المسارات نظيفاً — فقط تعريف الروابط لا الكود.
الكود ينتقل إلى Controllers.
use App\Http\Controllers\ArticleController;
use App\Http\Controllers\UserController;
// [ClassName::class, 'methodName']
Route::get('/articles', [ArticleController::class, 'index'])->name('articles.index');
Route::get('/articles/{id}', [ArticleController::class, 'show'])->name('articles.show');
Route::get('/articles/create', [ArticleController::class, 'create'])->name('articles.create');
Route::post('/articles', [ArticleController::class, 'store'])->name('articles.store');
Route::delete('/articles/{id}', [ArticleController::class, 'destroy'])->name('articles.destroy');
Route::resource() — الطريقة المثلى للـ CRUD
لاحظت تكرار نفس النمط للعمليات الأساسية (CRUD)؟ Laravel عنده حل أنيق: سطر واحد يُنشئ 7 مسارات تلقائياً!
هذا السطر ينشئ هذه المسارات تلقائياً:
| الأسلوب | الرابط | الدالة في Controller | اسم المسار | الوظيفة |
|---|---|---|---|---|
GET | /articles | index | articles.index | قائمة المقالات |
GET | /articles/create | create | articles.create | نموذج إنشاء |
POST | /articles | store | articles.store | حفظ جديد |
GET | /articles/{article} | show | articles.show | عرض مقال |
GET | /articles/{article}/edit | edit | articles.edit | نموذج تعديل |
PUT/PATCH | /articles/{article} | update | articles.update | تحديث |
DELETE | /articles/{article} | destroy | articles.destroy | حذف |
php artisan make:controller ArticleController --resourceسيُنشئ Controller بالدوال السبع (index, create, store, show, edit, update, destroy) جاهزة للتعبئة.
7. Route Model Binding — Laravel يجلب البيانات تلقائياً
هذه خاصية رائعة في Laravel. بدلاً من أن تكتب بنفسك:
$article = Article::findOrFail($id); في كل دالة Controller،
Laravel يستطيع فعل ذلك تلقائياً إذا سمّيت البارامتر بنفس اسم المتغير.
بدون Route Model Binding
// في routes/web.php
Route::get('/articles/{id}', [ArticleController::class, 'show']);
// في ArticleController.php
public function show(string $id)
{
$article = Article::findOrFail($id); // نجلبه يدوياً
return view('articles.show', compact('article'));
}
مع Route Model Binding (الطريقة الأنيقة)
// في routes/web.php — اسم البارامتر {article} يطابق اسم Model
Route::get('/articles/{article}', [ArticleController::class, 'show']);
// في ArticleController.php
public function show(Article $article) // ← Laravel يجلبه تلقائياً!
{
// $article جاهز للاستخدام مباشرةً
// إذا لم يُوجد ID المطلوب → 404 تلقائياً
return view('articles.show', compact('article'));
}
Laravel يرى أن نوع المتغير في دالة Controller هو
Article (Eloquent Model)،
فيبحث تلقائياً بـ Article::findOrFail($article) ويُمرّر النتيجة للدالة.
إذا لم يجد سجلاً بالـ ID المطلوب، يُعيد تلقائياً خطأ 404 Not Found.
لا حاجة لكتابة حتى سطر واحد للتعامل مع حالة عدم الوجود.
8. أوامر Artisan الضرورية لإدارة المسارات
عند العمل على مشروع كبير مع مسارات كثيرة، هذه الأوامر ستوفر عليك وقتاً كبيراً:
لعرض قائمة بكل المسارات المُعرَّفة في مشروعك:
سيعرض جدولاً بالأسلوب، الرابط، الاسم، الـ Middleware، والـ Controller لكل مسار.
لعرض مسار محدد بالاسم:
لتخزين المسارات في الكاش (للإنتاج — يسرّع التطبيق):
لمسح كاش المسارات (ضروري بعد كل تعديل على المسارات في بيئة الإنتاج):
route:cache في بيئة الإنتاج،
أي تعديل على ملفات المسارات لن يُطبَّق حتى تُعيد تشغيل php artisan route:cache.
لهذا السبب، لا تستخدم Closures داخل ملفات المسارات إذا كنت ستستخدم الكاش
— الكاش يعمل فقط مع المسارات الموجهة لـ Controllers.
أسئلة شائعة عن Routing في Laravel
أسئلة حقيقية يطرحها الطلاب عند التعامل مع نظام التوجيه في Laravel لأول مرة:
استخدم Closure (الدالة المباشرة) فقط في حالتين: الاختبار السريع لفكرة، أو المسارات البسيطة جداً (مثل صفحة "من نحن" الثابتة).
في كل حالة أخرى، وجّه للـ Controller. هذا يجعل ملف المسارات نظيفاً، الكود قابلاً للاختبار، والمشروع سهل الصيانة عند النمو. كقاعدة: إذا كان الكود داخل الـ Closure أكثر من 3 أسطر، انقله لـ Controller.
هذا سؤال دقيق ومهم! Laravel يتحقق من المسارات حسب ترتيب تعريفها.
إذا عرّفت /articles/{id} أولاً، فطلب /articles/create
سيُطابقه وسيتعامل مع الكلمة "create" كـ ID!
الحل: ضع المسارات الثابتة دائماً قبل المسارات الديناميكية:
Route::get('/articles/create', ...); // ← ثابت أولاً
Route::get('/articles/{id}', ...); // ← ديناميكي بعده
Laravel يوفر عدة طرق لإعادة التوجيه:
// إعادة توجيه لرابط مباشر
return redirect('/dashboard');
// إعادة توجيه باسم المسار (الأفضل)
return redirect()->route('dashboard');
// إعادة توجيه مع بارامتر
return redirect()->route('articles.show', ['article' => 42]);
// إعادة التوجيه للصفحة السابقة
return redirect()->back();
استخدم دائماً redirect()->route() مع الاسم للحصول على مرونة التعديل مستقبلاً.
نعم، تماماً. يمكنك إضافة Middleware لمسار واحد بدون مجموعة:
// Middleware على مسار واحد
Route::get('/admin', function () {
return view('admin.dashboard');
})->middleware('auth');
// Middleware متعدد على مسار واحد
Route::get('/admin', function () {
// ...
})->middleware(['auth', 'admin']);
لكن إذا كان عندك 5+ مسارات تشترك في نفس الـ Middleware، استخدم المجموعة لإبقاء الكود DRY.
9. ملخص — دليل مرجعي سريع للـ Routing
جدول يجمع كل ما تعلمته في هذا الدرس للرجوع إليه في أي وقت:
| تريد فعل ماذا؟ | الصيغة |
|---|---|
| تعريف مسار GET بسيط | Route::get('/path', fn() => view('name')); |
| تمرير بارامتر إلزامي | Route::get('/users/{id}', fn($id) => ...); |
| تمرير بارامتر اختياري | Route::get('/search/{q?}', fn($q = null) => ...); |
| تقييد البارامتر بأرقام فقط | Route::get('/{id}', ...)->whereNumber('id'); |
| تسمية المسار | Route::get('/path', ...)->name('route.name'); |
| توجيه لـ Controller | Route::get('/path', [Ctrl::class, 'method']); |
| إنشاء 7 مسارات CRUD دفعة واحدة | Route::resource('articles', ArticleController::class); |
| مجموعة بـ prefix مشترك | Route::prefix('admin')->group(...); |
| حماية مجموعة بـ Middleware | Route::middleware('auth')->group(...); |
| عرض قائمة كل المسارات | php artisan route:list |
المتحكمات (Controllers) — أين يعيش الكود الحقيقي 🧠
الآن تعرف كيف تُعرِّف المسارات وتوجّهها. الخطوة التالية هي تعلم كيف تكتب منطق تطبيقك داخل Controllers بطريقة منظمة وقابلة للصيانة.
تعلم المتحكمات