ما هي Laravel Middleware ومتى يجب إستخدامها وما هي الإمور التي يجب الإنتباه لها عند إستخدامها

ما هي Laravel Middleware ومتى يجب إستخدامها وما هي الإمور التي يجب الإنتباه لها عند إستخدامها

2024-05-15 وقت القراءه : 8 دقائق

جدول المحتويات

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

 

مثال لفرض أن لدينا حارس عمارة middleware ، وأنت طلبت منه الدخول إلى هذه العمارة request ، فهو سيتأكد من أنك أحد السكان أو من السبب لدخول العمارة، بعد التأكد سيقرر هل يسمح لك بالدخول أم لا response.

 

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


حالات يفضل بها إستخدام Middleware.

  • تسجيل الدخول والصلاحيات.
  • إعدادات اللغة (في حال أن الموقع أكثر من لغة).
  • تفعيل وضع الصيانة maintenance mode في التطبيق.
  • تتبع المستخدمين Tracking the application users.


كما يأتي إطار العمل لارافيل مع بعض middleware المبنية مسبقا، مثل التعامل مع طلبات الإدخال، تحديد حالة الموقع هل هو في وضع الصيانة أم لا.

 

كيفية إنشاء middleware في لارافيل؟

مؤخرا في أحد التطبيقات التي كنت أعمل عليها، طلب مني العميل أنه في حالة تم زيارة الموقع من أجهزة هاتف فإنه يجب توجيههم إلى نطاق فرعي.

وفي هذه الحالة قمت بإنشاء middlware يتحقق من جهاز المستخدم إذا كان mobile سيتم توجييهه إلى النطاق الفرعي وإذا كان جهاز حاسوب أن يتم توجيهه إلى الموقع مباشرة.

 

إنشاء middlware بإستخدام أوامر artisan.

php artisan make:middleware MobileRedirect

 

بعد تنفيذ الأمر أعلاه سيتم إنشاء كلاس باسم MobileRedirect.php في المسار 

App/Http/Middleware/MobileRedirect.php
class MobileRedirect
{
    /**
     * Handle an incoming request.
     *
     * @param   \Illuminate\Http\Request  $request
     * @param   \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        return $next($request);
    }
}

 

نلاحظ في الكلاس MobileRedirect وهو الـ middleware الذي تم إنشاؤه، أنه يوجد دالة handle وهي بمثابة العمود الفقري، فبداخلها نضع logic الذي نريد إعتماده وهي تقرر أين سيتم توجيه المستخدم.

هنا يوجد أمران يجب فهمهما وهما بمثابة أنواع للـ middlware وهما:

  • Before middleware : وهو شيئ يتم تشغيله قبل أن يتم معالجة الطلب وإظهار الإستجابة.
  • After middleware : يتم تنفيذه بعد معالجة الطلب وإظهار الإستجابة.

 

وفي مثالنا هنا سوف نقرأ الطلب هل هو من جهاز محمول وبناءا عليه سوف نظهر الإستجابه وهي إعادة توجيه المستخدم بالتالي سوف نحتاج على before middlware.

 

إذا سوف يتم التعديل في middlware الذي تم إنشاؤه 

namespace App\Http\Middleware;
  
use Closure;
  
class MobileRedirect
{
    /**
     * Handle an incoming request.
     *
     * @param   \Illuminate\Http\Request  $request
     * @param   \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // check if the request is from mobile device
        if ($request->mobile == "1") {
            return redirect('mobile-site-url-goes-here');
        }
  
        return $next($request);
    }

 

ففي المثال أعلاه سوف نتحقق إذا كانت قيمة mobile query string parameter تساوي true فإنه سوف يتم توجيهه للنطاق الفرعي.

 

نلاحظ أيضا في الدالة أنه تم إستدعاء $next($request) وهو ما سيتم تنفيذه في حالة أن قيمة mobile query string parameter تساوي false إي الذهاب إلى النطاق الحقيقي.

 

تسجيل Middlware

لغاية الأن نحن قمنا بإنشاء middlware وكتابة منطق العمل، لكن لارافيل لا تعلم بوجود هذه middlware، لذلك يجب القيام بتسجيل هذا middlware، ولتسجيله نذهب إلى 

App/Http/Kernel.php
protected $middleware = [
    // \App\Http\Middleware\TrustHosts::class,
    \App\Http\Middleware\TrustProxies::class,
    \Fruitcake\Cors\HandleCors::class,
     \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
     \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];

كما نرى أن المتغير middlware يحتوي على مصفوفة من middlware المثبة مسبقا، ويتم تنفيذها عند كل طلب في الموقع.

في هذه المصفوفة سيتم إضافة middleware الخاص بنا

protected $middleware = [
     \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
     \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
 
    \App\Http\Middleware\MobileRedirect::class
];

 

الأن عند القيام بزيارة الموقع بإستخدام querystring mobile=1 

www.test.test/?mobile=1

فإنه سوف يتم تنفيذ middlware الذي تم إنشاؤه، أي توجيهنا إلى subdomain.

 

الطريقة أعلاه كما قلنا سابقا فإنه سوف يتم تشغيلها عند كل طلب request، لكن ببعض الأحيان نرغب في تشغيل middlware لمسارات محدده، وللقيام بذلك عوضا عن تسجيله في مصفوفة $middleware يتم تسجيله في $routeMiddleware والذي يوجد في نفس المسار

App/Http/Kernel.php
protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
    'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];

 

وهنا يجب أن يتم تسجيل middlware

protected $routeMiddleware = [
   'mobile.redirect'=> \App\Http\Middleware\MobileRedirect::class
]

حيث يمكن إنشاء إسم alias وهو mobile.redirect.

 

ومن ثم تنفيذ middlware على المسار مباشرة            

Route::get('/contact', 'ContactController@index')->middleware('mobile.redirect');

 فعند زيارة الرابط 

http://www.test.test/contact?mobile=1

 هنا سيتم تنفيذ middleware فقط على المسار /contact أما باقي المسارات لن يتم تسجيل middleware عليها.


تسجل middleware على أكثر من رابط

ماذا لو كان لدي ٥ روابط في الموقع أريد تطبيق middlware عليها هل سوف إقوم بإضافة middleware('mobile.redirect') عليها جميعها؟ 

Route::get('/contact', [EmailController::class,'index'])->middleware('mobile.redirect');
Route::get('/hellow', [HellowController::class,'index'])->middleware('mobile.redirect');
Route::get('/cats', [CatController::class,'index'])->middleware('mobile.redirect');

بالطبع هذه الطريقة تعمل دون مشاكل لكن يمكن إختصار الكود وإنشاء group يتم تطبيق middlware عليها :

Route::group(['middleware' => 'mobile.redirect'], function () {
    Route::get('/contact', [EmailController::class, 'index']);
    Route::get('/hellow', [HellowController::class, 'index']);
    Route::get('/cats', [CatController::class, 'index']);
});


تسجيل middlware بداخل الكونترولر .

يوجد ثلاث حالات يمكن تطبيق middleware بها على الدوال وهي:

  • apply in all methods تطبق على جميع الدوال.
  • only بمعنى أن يطبق فقط على الدوال المحددة.
  • except يطبق على جميع الدوال بإستثناء الدوال المحددة.


ولتنفيذ ذلك في بداية Controller ننشئ دالة __construct ونضع بها middleware.

public function __construct()
{
    $this->middleware('auth');//apply to all methods.
    $this->middleware('auth',['only'=>['show']]);//apply just on show method
    $this->middleware('auth',['except'=>['show']]);//apply to all except show
}


محاذير عند كتابة أو إنشاء middleware

  • يجب الأخذ بعين الإعتبار أداء التطبيق، فكما نعلم ان middleware هو وسيط بين الطلب request والإستجابة response فإذا كان middleware يستخدم الكثير من الموارد، فقط يتسبب ذلك في بطئ التطبيق.
  • يتم تنفيذ middleware واحدة تلو الأخرى حسب الترتيب، فيجب الإنتباه أن يتم وضع middleware التي تأخذ القليل من الموارد في الأعلى.
  • معظم أطر العمل بنيت فوق بعضها مثل إطار العمل لارافيل تم بناؤه على مكونات Symfony، لذلك عند كتابة middleware فإننا نكتب تحتت الإطار الأساسي، لذلك لا يجب أن يسبب middleware الذي نكتبه أي تعارض مع المكونات الأخرى.


الخلاصة

  • إنشاء middlware.
php artisan make:middleware MobileRedirect
  • تحديد الإحتياج before middlware, after middlware
public function handle($request, Closure $next)
{
    // check if the request is from mobile device
    if ($request->mobile == "1") {
        return redirect('mobile-site-url-goes-here');
    }
    return $next($request);
}

 

  • تسجيل middlware .
protected $middleware = [
    \App\Http\Middleware\MobileRedirect::class
];
 
//or
 
protected $routeMiddleware = [
   'mobile.redirect'=> \App\Http\Middleware\MobileRedirect::class
]

 

  • تطبيق  middlware.

 

protected $middleware = [
    \App\Http\Middleware\MobileRedirect::class
];
 
//or
 
protected $routeMiddleware = [
   'mobile.redirect'=> \App\Http\Middleware\MobileRedirect::class
 
 
//or
Route::get('/contact', 'ContactController@index')->middleware('mobile.redirect');
 
//or
Route::group(['middleware' => 'mobile.redirect'], function () {
    Route::get('/contact', [EmailController::class, 'index']);
    Route::get('/hellow', [HellowController::class, 'index']);
    Route::get('/cats', [CatController::class, 'index']);
});
 
//or
public function __construct()
{
     $this->middleware('auth');//apply to all methods.
     $this->middleware('auth',['only'=>['show']]);//apply just on show method
     $this->middleware('auth',['except'=>['show']]);//apply to all except show
}

التعليقات
هشام محمد
منذ سنة

شرح ممتاز ما شاء الله

زائر
منذ سنة

مدونة حضرتك بقت من اهم مراجعي .. شكرا

زائر
منذ 11 شهر

:

Younis
منذ شهرين

ابداااع

إضافة تعليق
Loading...