شرح مفصل لنظام التوجيه في Laravel
المسارات هي "استقبال" Laravel — كل طلب HTTP يمر من هنا أولًا قبل أن يُوجَّه إلى دالة المتحكم المناسبة.
1. ما ستتعلمه
- المسارات الأساسية: Route::get/post/put/patch/delete
- معلمات المسار وقيود التعابير النمطية
- تجميع المسارات: middleware/prefix/name/domain
- تسمية المسارات وتوليد عناوين URL
- توجيه API وRoute::apiResource()
2. قصة حقيقية لمدير منتجات
(1) المشكلة: هيكل عناوين URL الفوضوي يؤدي إلى كارثة SEO
صمّمت Alice أكثر من 30 صفحة لـ ShopMetrics، لكن اصطلاح تسمية عناوين URL كان عشوائيًا تمامًا: /shop_view.php?id=5 و/admin-users-list و/api/getData كانت كلها ممزوجة. زاحف Google كان بطيئًا للغاية، وعندما شارك المستخدمون الروابط، كان شريط العناوين يعرض سلسلة من معلمات علامة الاستفهام. عندما أعاد Bob هيكلة الواجهة الخلفية وغيّر عنوان URL واحد، أعادت جميع الحالات الـ 15 المشفرة يدويًا على الواجهة الأمامية أخطاء 404.
(2) حلول توجيه Laravel
يستخدم توجيه Laravel بنية تصريحية لتعريف قواعد URL، ويدعم التسمية والتجميع وقيود المعلمات؛ والتغييرات في مكان واحد تسري تلقائيًا على المستوى العالمي.
// routes/web.php — مسارات نظيفة ومسماة وRESTful
Route::get('/shops/{slug}', [ShopController::class, 'show'])
->name('shops.show')
->where('slug', '[a-z0-9-]+');
// توليد URL بالاسم — لا تشفير يدوي
$url = route('shops.show', ['slug' => 'alice-store']);
// => /shops/alice-store
(3) العائد
قيّدت Alice قواعد URL لـ ShopMetrics باستخدام المسارات المُسماة، مما أدى إلى زيادة ترتيب SEO بنسبة 30%. عندما أعاد Bob هيكلة عناوين URL، كان يحتاج فقط إلى تحديث تعريفات المسار؛ ودالة route() على الواجهة الأمامية تولّد عناوين URL الجديدة تلقائيًا، بدون أي أخطاء 404.
3. التوجيه الأساسي
(1) توجيه أفعال HTTP
يوفر Laravel دالة مسار مقابلة لكل فعل HTTP:
// routes/web.php
Route::get('/shops', [ShopController::class, 'index']);
Route::post('/shops', [ShopController::class, 'store']);
Route::put('/shops/{id}', [ShopController::class, 'update']);
Route::patch('/shops/{id}', [ShopController::class, 'updateStatus']);
Route::delete('/shops/{id}', [ShopController::class, 'destroy']);
| فعل HTTP | الغرض | التكرارية | العمليات النموذجية |
|---|---|---|---|
| GET | استرجاع مورد | ✅ | قائمة/تفاصيل |
| POST | إنشاء مورد | ❌ | إضافة |
| PUT | تحديث كامل | ✅ | استبدال |
| PATCH | تحديث جزئي | ✅ | تغيير الحالة |
| DELETE | حذف مورد | ✅ | حذف |
(2) مسارات "match" و"any"
// مطابقة عدة أفعال
Route::match(['get', 'post'], '/shops/search', [ShopController::class, 'search']);
// أي فعل
Route::any('/fallback', [FallbackController::class, 'handle']);
(1) ▶ مثال: تعريفات المسارات الأساسية لـ ShopMetrics
// routes/web.php
Route::get('/', [HomeController::class, 'index'])->name('home');
Route::get('/about', [AboutController::class, 'index'])->name('about');
Route::get('/pricing', [PricingController::class, 'index'])->name('pricing');
Route::get('/contact', [ContactController::class, 'create'])->name('contact.create');
Route::post('/contact', [ContactController::class, 'store'])->name('contact.store');
الناتج:
// تم التنفيذ بنجاح
4. معلمات المسار والقيود
(1) معلمات مطلوبة
Route::get('/shops/{id}', [ShopController::class, 'show']);
Route::get('/tenants/{tenant}/shops/{shop}', [ShopController::class, 'showForTenant']);
(2) معلمات اختيارية
Route::get('/reports/{type?}', [ReportController::class, 'index']);
// /reports → type = null
// /reports/sales → type = 'sales'
(3) التعابير النمطية
// معرف رقمي فقط
Route::get('/shops/{id}', [ShopController::class, 'show'])
->where('id', '[0-9]+');
// تنسيق slug: أحرف صغيرة وأرقام وشرطات
Route::get('/shops/{slug}', [ShopController::class, 'showBySlug'])
->where('slug', '[a-z0-9-]+');
// قيود متعددة
Route::get('/tenants/{tenant}/orders/{id}', [OrderController::class, 'show'])
->where(['tenant' => '[a-z0-9-]+', 'id' => '[0-9]+']);
| طريقة القيد | الاستخدام | الوصف |
|---|---|---|
where() |
تعبير نمطي لمعلمة واحدة | الأكثر مرونة |
whereNumber() |
أرقام فقط | مكافئ لـ where('id', '[0-9]+') |
whereAlpha() |
أحرف فقط | مكافئ لـ where('name', '[a-zA-Z]+') |
whereAlphaNumeric() |
أحرف + أرقام | مكافئ لـ where('name', '[a-zA-Z0-9]+') |
whereUuid() |
تنسيق UUID | تحقق تلقائي من UUID v4 |
(1) ▶ مثال: مسارات ShopMetrics مع القيود
// routes/web.php
Route::get('/shops/{id}', [ShopController::class, 'show'])
->whereNumber('id');
Route::get('/categories/{slug}', [CategoryController::class, 'show'])
->where('slug', '[a-z0-9-]+');
Route::get('/tenants/{tenant}/dashboard', [DashboardController::class, 'index'])
->where('tenant', '[a-z0-9-]+');
الناتج:
// تم التنفيذ بنجاح
5. مجموعات المسارات
يسمح تجميع المسارات لمسارات متعددة بمشاركة التهيئات (البرمجيات الوسيطة والبادئات ومساحات الأسماء، إلخ)، مما يمنع تكرار الكود.
(1) تجميع البرمجيات الوسيطة
Route::middleware(['auth', 'tenant.resolve'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
Route::get('/shops', [ShopController::class, 'index']);
Route::get('/orders', [OrderController::class, 'index']);
});
(2) تجميع البادئات
Route::prefix('admin')->group(function () {
Route::get('/users', [AdminUserController::class, 'index']);
Route::get('/settings', [AdminSettingController::class, 'index']);
// عنوان URL الكامل: /admin/users، /admin/settings
});
(3) تجميع الأسماء
Route::name('admin.')->group(function () {
Route::get('/users', [AdminUserController::class, 'index'])->name('users');
// اسم المسار: admin.users
});
(4) التجميع المدمج
Route::prefix('admin')
->middleware(['auth', 'admin'])
->name('admin.')
->group(function () {
Route::get('/users', [AdminUserController::class, 'index'])->name('users');
Route::get('/plans', [AdminPlanController::class, 'index'])->name('plans');
// عنوان URL: /admin/users، الاسم: admin.users
});
(1) ▶ مثال: مجموعات مسارات متعددة المستأجرين لـ ShopMetrics
// routes/web.php — مسارات واعية بالمستأجر
Route::middleware(['auth', 'tenant.resolve'])->prefix('/{tenant}')->group(function () {
Route::get('/dashboard', [TenantDashboardController::class, 'index'])
->name('tenant.dashboard');
Route::resource('/shops', ShopController::class);
Route::resource('/orders', OrderController::class);
Route::resource('/products', ProductController::class);
});
الناتج:
// تم التنفيذ بنجاح
6. تسمية المسارات وتوليد عناوين URL
(1) المسارات المُسماة
Route::get('/shops/{id}', [ShopController::class, 'show'])
->name('shops.show');
(2) توليد عنوان URL
// في قوالب Blade أو المتحكمات
$url = route('shops.show', ['id' => 5]);
// => http://shopmetrics.test/shops/5
// مع معلمات الاستعلام
$url = route('shops.index', ['sort' => 'name', 'page' => 2]);
// => http://shopmetrics.test/shops?sort=name&page=2
| الدالة | الغرض | مثال |
|---|---|---|
route() |
توليد عنوان URL لمسار مُسمى | route('shops.show', 5) |
url() |
توليد عنوان URL مطلق | url('/shops') |
action() |
توليد بناءً على دالة المتحكم | action([ShopController::class, 'show'], 5) |
(1) ▶ مثال: استخدام المسارات المُسماة في Blade
<a href="{{ route('shops.show', $shop->id) }}">{{ $shop->name }}</a>
<form action="{{ route('shops.update', $shop->id) }}" method="POST">
@method('PUT')
@csrf
<!-- حقول النموذج -->
</form>
الناتج:
// تم التنفيذ بنجاح
7. توجيه API
يُستخدم routes/api.php حصريًا لتوجيه API؛ ويضيف تلقائيًا البادئة /api.
(1) مسار apiResource
// routes/api.php
use App\Http\Controllers\Api\ShopController;
Route::apiResource('shops', ShopController::class);
// يولّد:
// GET /api/shops → index
// POST /api/shops → store
// GET /api/shops/{shop} → show
// PUT /api/shops/{shop} → update
// DELETE /api/shops/{shop} → destroy
| الطريقة | apiResource | resource |
|---|---|---|
| index | ✅ | ✅ |
| create | ❌ | ✅ |
| store | ✅ | ✅ |
| show | ✅ | ✅ |
| edit | ❌ | ✅ |
| update | ✅ | ✅ |
| destroy | ✅ | ✅ |
(2) التحكم في إصدار API
Route::prefix('v1')->group(function () {
Route::apiResource('shops', Api\V1\ShopController::class);
Route::apiResource('orders', Api\V1\OrderController::class);
});
Route::prefix('v2')->group(function () {
Route::apiResource('shops', Api\V2\ShopController::class);
});
(1) ▶ مثال: مخطط توجيه API لـ ShopMetrics
// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
Route::prefix('v1')->name('api.v1.')->group(function () {
Route::apiResource('tenants.shops', Api\V1\TenantShopController::class);
Route::apiResource('shops.orders', Api\V1\ShopOrderController::class);
Route::apiResource('products', Api\V1\ProductController::class);
Route::get('analytics/overview', [Api\V1\AnalyticsController::class, 'overview']);
Route::post('reports/generate', [Api\V1\ReportController::class, 'generate']);
});
});
الناتج:
// تم التنفيذ بنجاح
8. عملية مطابقة المسارات
flowchart LR
A[طلب HTTP] --> B{مسار مطابق؟}
B -->|نعم| C[استخراج المعلمات]
C --> D[تشغيل البرمجية الوسيطة]
D --> E[استدعاء دالة المتحكم]
E --> F[إرجاع الاستجابة]
B -->|لا| G[مسار بديل]
G --> H[404 غير موجود]
9. مثال شامل: مخطط التوجيه الكامل لـ ShopMetrics
// ============================================
// شامل: مسارات ShopMetrics الكاملة
// يغطي: مسارات الويب، مسارات API، المجموعات، القيود
// ============================================
// routes/web.php
Route::get('/', [HomeController::class, 'index'])->name('home');
Route::get('/pricing', [PricingController::class, 'index'])->name('pricing');
Route::middleware('auth')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
Route::resource('shops', ShopController::class)->whereNumber('shop');
Route::resource('shops.orders', OrderController::class)->shallow();
Route::post('/shops/{shop}/logo', [ShopLogoController::class, 'update'])
->name('shops.logo.update');
});
// routes/api.php
Route::prefix('v1')->middleware('auth:sanctum')->group(function () {
Route::apiResource('shops', Api\ShopController::class);
Route::apiResource('shops.products', Api\ProductController::class)->shallow();
Route::apiResource('shops.orders', Api\OrderController::class)->shallow();
Route::get('analytics/summary', [Api\AnalyticsController::class, 'summary']);
Route::post('reports/generate', [Api\ReportController::class, 'generate']);
});
❓ أسئلة شائعة
web.php تطبق تلقائيًا مجموعة برمجيات الوسيطة web (Session، CSRF، تشفير ملفات تعريف الارتباط)، مما يجعلها مناسبة لطلبات الصفحات؛ مسارات api.php تطبق تلقائيًا مجموعة برمجيات الوسيطة API (تقييد معدل throttle)، وعناوين URL تُسبق تلقائيًا بـ /api، مما يجعلها مناسبة لطلبات API.Route::resource، ومتى يجب أن أحدد المسارات يدويًا؟resource/apiResource؛ للعمليات غير القياسية (مثل البحث والتصدير والعمليات المجمعة)، حدد مسارات إضافية يدويًا.route() تُحدَّث تلقائيًا. مع عناوين URL المشفرة يدويًا، يجب عليك إجراء بحث واستبدال شامل في كل مرة تقوم فيها بالتغيير، مما يجعل من السهل تفويت بعض الحالات.php artisan route:cache.php artisan route:list لسرد جميع المسارات، بما في ذلك الطريقة وURI والاسم والبرمجية الوسيطة. أضف --path=shops لتصفية المسارات حسب بادئة محددة.📖 ملخص
- يوفر Laravel دوال توجيه لكل فعل HTTP: GET وPOST وPUT وPATCH وDELETE
- يمكن أن تكون معلمات المسار مطلوبة أو اختيارية؛ يمكنك استخدام
where()لإضافة قيود التعابير النمطية - تجميع المسارات يسمح لمسارات متعددة بمشاركة البرمجيات الوسيطة والبادئات ومساحات الأسماء
- استخدم المسارات المُسماة مع دالة
route()لفصل عناوين URL عن الكود - apiResource: يولّد تلقائيًا مسارات API وRESTful (باستثناء create/edit)
- تخزين المسارات مؤقتًا (route:cache) يمكن أن يحسّن بشكل كبير أداء مطابقة عدد كبير من المسارات
📝 تمارين
-
تمرين أساسي (⭐): حدد المسارات التالية لـ ShopMetrics: الصفحة الرئيسية (GET /)، صفحة عن (GET /about)، وصفحة اتصل (GET+POST /contact). استخدم مسارات مُسماة وتحقق من إمكانية الوصول إليها في المتصفح.
-
تمرين متقدم (⭐⭐): استخدم مجموعات المسارات لتصميم مخطط مسارات API v1 لـ ShopMetrics، بما في ذلك ثلاثة apiResources — shops وproducts وorders — مع برمجية وسيطة للمصادقة والبادئة /api/v1.
-
تحدٍ (⭐⭐⭐): نفّذ تجميع مسارات متعددة المستأجرين
/{tenant}/*. اكتب برمجية وسيطة TenantResolve لتحليل المستأجر من URL وحقنه في Request، مع التأكد من أن جميع المسارات الفرعية يمكنها استرداد المستأجر الحالي عبر$request->tenant().



