404 Not Found

404 Not Found


nginx

شرح مفصل لنظام التوجيه في Laravel

المسارات هي "استقبال" Laravel — كل طلب HTTP يمر من هنا أولًا قبل أن يُوجَّه إلى دالة المتحكم المناسبة.

1. ما ستتعلمه


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، ويدعم التسمية والتجميع وقيود المعلمات؛ والتغييرات في مكان واحد تسري تلقائيًا على المستوى العالمي.

PHP
// 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:

PHP
// 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"

PHP
// مطابقة عدة أفعال
Route::match(['get', 'post'], '/shops/search', [ShopController::class, 'search']);

// أي فعل
Route::any('/fallback', [FallbackController::class, 'handle']);

(1) ▶ مثال: تعريفات المسارات الأساسية لـ ShopMetrics

PHP
// 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');

الناتج:

TEXT
// تم التنفيذ بنجاح

4. معلمات المسار والقيود

(1) معلمات مطلوبة

PHP
Route::get('/shops/{id}', [ShopController::class, 'show']);
Route::get('/tenants/{tenant}/shops/{shop}', [ShopController::class, 'showForTenant']);

(2) معلمات اختيارية

PHP
Route::get('/reports/{type?}', [ReportController::class, 'index']);
// /reports → type = null
// /reports/sales → type = 'sales'

(3) التعابير النمطية

PHP
// معرف رقمي فقط
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 مع القيود

PHP
// 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-]+');

الناتج:

TEXT
// تم التنفيذ بنجاح

5. مجموعات المسارات

يسمح تجميع المسارات لمسارات متعددة بمشاركة التهيئات (البرمجيات الوسيطة والبادئات ومساحات الأسماء، إلخ)، مما يمنع تكرار الكود.

(1) تجميع البرمجيات الوسيطة

PHP
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) تجميع البادئات

PHP
Route::prefix('admin')->group(function () {
    Route::get('/users', [AdminUserController::class, 'index']);
    Route::get('/settings', [AdminSettingController::class, 'index']);
    // عنوان URL الكامل: /admin/users، /admin/settings
});

(3) تجميع الأسماء

PHP
Route::name('admin.')->group(function () {
    Route::get('/users', [AdminUserController::class, 'index'])->name('users');
    // اسم المسار: admin.users
});

(4) التجميع المدمج

PHP
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

PHP
// 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);
});

الناتج:

TEXT
// تم التنفيذ بنجاح

6. تسمية المسارات وتوليد عناوين URL

(1) المسارات المُسماة

PHP
Route::get('/shops/{id}', [ShopController::class, 'show'])
    ->name('shops.show');

(2) توليد عنوان URL

PHP
// في قوالب 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

HTML
<a href="{{ route('shops.show', $shop->id) }}">{{ $shop->name }}</a>

<form action="{{ route('shops.update', $shop->id) }}" method="POST">
    @method('PUT')
    @csrf
    <!-- حقول النموذج -->
</form>

الناتج:

TEXT
// تم التنفيذ بنجاح

7. توجيه API

يُستخدم routes/api.php حصريًا لتوجيه API؛ ويضيف تلقائيًا البادئة /api.

(1) مسار apiResource

PHP
// 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

PHP
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

PHP
// 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']);
    });
});

الناتج:

TEXT
// تم التنفيذ بنجاح

8. عملية مطابقة المسارات

100%
flowchart LR
    A[طلب HTTP] --> B{مسار مطابق؟}
    B -->|نعم| C[استخراج المعلمات]
    C --> D[تشغيل البرمجية الوسيطة]
    D --> E[استدعاء دالة المتحكم]
    E --> F[إرجاع الاستجابة]
    B -->|لا| G[مسار بديل]
    G --> H[404 غير موجود]

9. مثال شامل: مخطط التوجيه الكامل لـ ShopMetrics

PHP
// ============================================
// شامل: مسارات 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']);
});

❓ أسئلة شائعة

س ما الفرق بين routes/web.php وroutes/api.php؟
ج المسارات في web.php تطبق تلقائيًا مجموعة برمجيات الوسيطة web (Session، CSRF، تشفير ملفات تعريف الارتباط)، مما يجعلها مناسبة لطلبات الصفحات؛ مسارات api.php تطبق تلقائيًا مجموعة برمجيات الوسيطة API (تقييد معدل throttle)، وعناوين URL تُسبق تلقائيًا بـ /api، مما يجعلها مناسبة لطلبات API.
س متى يجب أن أستخدم Route::resource، ومتى يجب أن أحدد المسارات يدويًا؟
ج عمليات CRUD وRESTful يمكن التعامل معها بسطر واحد باستخدام resource/apiResource؛ للعمليات غير القياسية (مثل البحث والتصدير والعمليات المجمعة)، حدد مسارات إضافية يدويًا.
س ما فوائد تسمية المسارات؟
ج تسمية المسارات تفصل عناوين URL عن الكود. لتعديل عنوان URL، تحتاج فقط إلى تغيير تعريف المسار، وجميع استدعاءات route() تُحدَّث تلقائيًا. مع عناوين URL المشفرة يدويًا، يجب عليك إجراء بحث واستبدال شامل في كل مرة تقوم فيها بالتغيير، مما يجعل من السهل تفويت بعض الحالات.
س ما هو المسار الضحيل؟
ج افتراضيًا، تولّد الموارد المتداخلة عناوين URL مثل /shops/{shop}/orders/{order}. يضمن خيار "shallow" أن الموارد الفرعية تتداخل فقط عندما يكون المعرف مطلوبًا: show وedit وupdate وdelete تستخدم /orders/{order}، بينما index وcreate تحتفظ بالهيكل المتداخل. هذا يقلل من عمق عنوان URL.
س هل يؤثر وجود عدد كبير من المسارات على الأداء؟
ج عدد المسارات له تأثير ضئيل جدًا على الأداء، حيث يستخدم Laravel خوارزمية مطابقة فعالة. ومع ذلك، إذا كان لديك أكثر من 1000 مسار، نوصي بتخزين جدول المسارات مؤقتًا باستخدام php artisan route:cache.
س كيف أعرض جميع المسارات المُسجلة؟
ج شغّل php artisan route:list لسرد جميع المسارات، بما في ذلك الطريقة وURI والاسم والبرمجية الوسيطة. أضف --path=shops لتصفية المسارات حسب بادئة محددة.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (⭐): حدد المسارات التالية لـ ShopMetrics: الصفحة الرئيسية (GET /)، صفحة عن (GET /about)، وصفحة اتصل (GET+POST /contact). استخدم مسارات مُسماة وتحقق من إمكانية الوصول إليها في المتصفح.

  2. تمرين متقدم (⭐⭐): استخدم مجموعات المسارات لتصميم مخطط مسارات API v1 لـ ShopMetrics، بما في ذلك ثلاثة apiResources — shops وproducts وorders — مع برمجية وسيطة للمصادقة والبادئة /api/v1.

  3. تحدٍ (⭐⭐⭐): نفّذ تجميع مسارات متعددة المستأجرين /{tenant}/*. اكتب برمجية وسيطة TenantResolve لتحليل المستأجر من URL وحقنه في Request، مع التأكد من أن جميع المسارات الفرعية يمكنها استرداد المستأجر الحالي عبر $request->tenant().

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%