المتحكمات (Controllers) في Laravel 11 — منطق تطبيقك بطريقة احترافية

تخيل أن موقعك مطعم: المسارات (Routes) هي قائمة الطعام التي يختار منها الزبون، والمتحكم (Controller) هو الطباخ الذي يستلم الطلب، يُحضّر الأكل، ويُرسله إلى الطاولة. كتابة الكود مباشرة في ملف المسارات يشبه أن يكون الطباخ هو نفسه الذي يأخذ الطلب — فوضى تامة في المشاريع الكبيرة. Controllers هي الحل: فصل المسؤوليات، تنظيم الكود، وسهولة الصيانة.

1. إنشاء Controller جديد بأمر Artisan

أسرع وأنظف طريقة لإنشاء Controller في Laravel هي استخدام Artisan. لا تحتاج لإنشاء الملف يدوياً وكتابة الـ namespace — Artisan يفعل كل ذلك:

php artisan make:controller ArticleController

سيُنشئ ملف جديد في app/Http/Controllers/ArticleController.php

الملف الذي يُنشئه Artisan يبدو هكذا:

app/Http/Controllers/ArticleController.php
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ArticleController extends Controller
{
    // هنا ستكتب دوالك (Methods)
}
ما هو extends Controller؟
كل Controller يرث من الكلاس الأساسي App\Http\Controllers\Controller. هذا يمنحك بعض الدوال المساعدة جاهزة مثل validate() وmiddleware(). في Laravel 11، الكلاس الأساسي أصبح أخف وأبسط من الإصدارات السابقة.

تنظيم Controllers في مجلدات فرعية

في المشاريع الكبيرة، قد يكون لديك عشرات الـ Controllers. يمكنك تنظيمها في مجلدات فرعية داخل Controllers/:

php artisan make:controller Admin/UserController

سيُنشئ app/Http/Controllers/Admin/UserController.php مع namespace صحيح تلقائياً.

2. كتابة الدوال وربطها بالمسارات

كل دالة (Method) داخل Controller تستجيب لمسار معين. اتفاقية Laravel لتسمية الدوال ليست عشوائية — لها معاني محددة يفهمها كل مطور Laravel:

app/Http/Controllers/ArticleController.php
<?php
namespace App\Http\Controllers;

use App\Models\Article;
use Illuminate\Http\Request;

class ArticleController extends Controller
{
    // GET /articles — عرض قائمة المقالات
    public function index()
    {
        $articles = Article::latest()->paginate(10);
        return view('articles.index', compact('articles'));
    }

    // GET /articles/{id} — عرض مقال واحد
    public function show(Article $article)
    {
        return view('articles.show', compact('article'));
    }

    // GET /articles/create — عرض نموذج الإنشاء
    public function create()
    {
        return view('articles.create');
    }

    // POST /articles — حفظ مقال جديد
    public function store(Request $request)
    {
        $article = Article::create($request->validated());
        return redirect()->route('articles.show', $article)->with('success', 'تم إنشاء المقال!');
    }
}

والربط في ملف المسارات يكون هكذا:

routes/web.php
use App\Http\Controllers\ArticleController;

Route::get('/articles', [ArticleController::class, 'index'])->name('articles.index');
Route::get('/articles/create', [ArticleController::class, 'create'])->name('articles.create');
Route::post('/articles', [ArticleController::class, 'store'])->name('articles.store');
Route::get('/articles/{article}', [ArticleController::class, 'show'])->name('articles.show');
لاحظ كيف أن ملف المسارات أصبح نظيفاً:
لا يوجد أي منطق برمجي — فقط تعريف: هذا الرابط ← هذه الدالة في هذا الـ Controller. كل المنطق انتقل إلى مكانه الصحيح: الـ Controller.

3. Resource Controllers — CRUD كامل بأمر واحد

في الواقع العملي، معظم Controllers تتبع نفس النمط: قائمة، عرض، إنشاء، حفظ، تعديل، تحديث، حذف. Laravel يعرف هذا ويوفر لك اختصاراً رائعاً:

php artisan make:controller ArticleController --resource

ينشئ Controller يحتوي على 7 دوال جاهزة الهيكل.

الملف المُنشَأ يحتوي على هذه الدوال مع تعليقات توضيحية:

app/Http/Controllers/ArticleController.php — --resource
<?php
class ArticleController extends Controller
{
    public function index()    { /* قائمة */ }
    public function create()   { /* نموذج إنشاء */ }
    public function store()    { /* حفظ جديد */ }
    public function show()     { /* عرض واحد */ }
    public function edit()     { /* نموذج تعديل */ }
    public function update()   { /* تحديث */ }
    public function destroy()  { /* حذف */ }
}

وفي ملف المسارات، سطر واحد فقط يربط الـ 7 مسارات:

Route::resource('articles', ArticleController::class);

إنشاء Controller مع Model و Migration في آن واحد

بدلاً من تشغيل 3 أوامر منفصلة، يمكنك دمجها في أمر واحد قوي:

php artisan make:model Article -mcr

-m = Migration  |  -c = Controller  |  -r = Resource — ثلاثة بأمر واحد!

هل يجب استخدام الـ 7 دوال كلها؟
لا. إذا كنت لا تحتاج بعض الدوال (مثل: موردك يعرض فقط بدون إنشاء أو حذف)، يمكنك تحديد ما تريد:
// فقط index و show (للعرض فقط)
Route::resource('articles', ArticleController::class)->only(['index', 'show']);

// كل شيء ماعدا destroy
Route::resource('articles', ArticleController::class)->except(['destroy']);

4. استلام بيانات المستخدم عبر Request

عندما يُرسل المستخدم نموذجاً (Form)، أو بارامترات في الرابط، أو بيانات JSON، Laravel يُجمّعها كلها في كائن Request. تستطيع الوصول إليه بطريقتين:

حقن Request في الدالة (الطريقة الأفضل)

app/Http/Controllers/ArticleController.php
use Illuminate\Http\Request;

public function store(Request $request)
{
    // قراءة حقل واحد
    $title = $request->input('title');

    // قراءة مع قيمة افتراضية
    $status = $request->input('status', 'draft');

    // قراءة مباشرة كخاصية
    $content = $request->content;

    // التحقق من وجود حقل
    if ($request->has('image')) {
        // معالجة الصورة
    }

    // جلب كل البيانات كـ array
    $allData = $request->all();

    // جلب بيانات محددة فقط (آمن!)
    $safeData = $request->only(['title', 'content', 'category_id']);

    // جلب كل شيء ماعدا حقول معينة
    $data = $request->except(['_token', '_method']);

    return redirect()->route('articles.index');
}

معلومات إضافية يمكن جلبها من Request

معلومات الطلب نفسه
// أسلوب HTTP (GET, POST...)
$method = $request->method();

// الرابط الكامل
$url = $request->url();

// عنوان IP للزائر
$ip = $request->ip();

// هل الطلب Ajax؟
$isAjax = $request->ajax();

// هل تم الطلب عبر HTTPS؟
$isSecure = $request->secure();

5. التحقق من البيانات (Validation) — حماية تطبيقك

لا تثق أبداً بالبيانات القادمة من المستخدم. قبل حفظ أي شيء في قاعدة البيانات، يجب التحقق من أن البيانات صحيحة ومكتملة. Laravel يوفر نظام Validation رائع يجعل هذا الأمر سهلاً وأنيقاً.

Validation مباشرة في Controller

app/Http/Controllers/ArticleController.php
public function store(Request $request)
{
    // تعريف قواعد التحقق
    $validated = $request->validate([
        'title'       => 'required|string|min:5|max:255',
        'content'     => 'required|string|min:50',
        'category_id' => 'required|exists:categories,id',
        'image'       => 'nullable|image|mimes:jpg,png,webp|max:2048',
        'published'   => 'boolean',
    ]);

    // إذا فشل التحقق، Laravel يُعيد المستخدم للصفحة السابقة
    // تلقائياً مع رسائل الخطأ — لا حاجة لكتابة أي كود إضافي!

    // هنا: البيانات موثوقة 100%
    Article::create($validated);

    return redirect()->route('articles.index')
        ->with('success', 'تم نشر المقال بنجاح!');
}

أهم قواعد التحقق في Laravel

القاعدة المعنى مثال
requiredالحقل إلزامي، لا يقبل فراغاً'name' => 'required'
stringيجب أن يكون نصاً'title' => 'string'
min:Nأدنى عدد أحرف'password' => 'min:8'
max:Nأقصى عدد أحرف'title' => 'max:255'
emailتنسيق بريد إلكتروني صحيح'email' => 'email'
unique:tableالقيمة غير موجودة في الجدول'email' => 'unique:users'
exists:table,colالقيمة موجودة في الجدول'cat_id' => 'exists:categories,id'
nullableالقيمة يمكن أن تكون فارغة'bio' => 'nullable|string'
imageيجب أن يكون صورة'photo' => 'image'
confirmedيجب وجود حقل تأكيد مطابق'password' => 'confirmed'

Form Request — الطريقة الاحترافية للـ Validation

عندما تكثر قواعد التحقق، انقلها لكلاس مستقل يُسمى Form Request:

php artisan make:request StoreArticleRequest
app/Http/Requests/StoreArticleRequest.php
<?php
class StoreArticleRequest extends FormRequest
{
    // من يملك صلاحية تقديم هذا الطلب؟
    public function authorize(): bool
    {
        return auth()->check();  // فقط المستخدمون المسجلون
    }

    // قواعد التحقق
    public function rules(): array
    {
        return [
            'title'   => 'required|string|min:5|max:255',
            'content' => 'required|string|min:50',
        ];
    }

    // رسائل خطأ مخصصة (اختياري)
    public function messages(): array
    {
        return [
            'title.required' => 'عنوان المقال مطلوب',
            'title.min'      => 'العنوان يجب أن يكون 5 أحرف على الأقل',
        ];
    }
}

والـ Controller يصبح أنظف بكثير:

Controller بعد استخدام Form Request
use App\Http\Requests\StoreArticleRequest;

public function store(StoreArticleRequest $request)
{
    // $request->validated() تحتوي البيانات الصحيحة فقط
    Article::create($request->validated());

    return redirect()->route('articles.index')
        ->with('success', 'تم نشر المقال!');
}

6. Single Action Controller — متحكم بدالة واحدة

أحياناً تحتاج Controller يقوم بعمل واحد فقط — مثل معالجة دفع، تصدير PDF، أو تشغيل عملية معينة. بدلاً من إنشاء Controller كامل بدالة واحدة وتسميتها بشكل مصطنع، استخدم Single Action Controller عبر الدالة السحرية __invoke:

php artisan make:controller ProcessPaymentController --invokable
app/Http/Controllers/ProcessPaymentController.php
<?php
class ProcessPaymentController extends Controller
{
    public function __invoke(Request $request)
    {
        // منطق معالجة الدفع
        $payment = PaymentService::process($request->validated());

        return redirect()->route('payment.success')
            ->with('receipt', $payment->receipt_number);
    }
}

في ملف المسارات، لا حاجة لتحديد اسم الدالة:

routes/web.php — Single Action Controller
use App\Http\Controllers\ProcessPaymentController;

// بدون تحديد method — Laravel يعرف أنه __invoke
Route::post('/payment/process', ProcessPaymentController::class)
    ->name('payment.process');
متى أستخدم Single Action Controller؟
عندما تكون العملية محددة ومعزولة: تصدير بيانات، إرسال إشعار، معالجة webhook، إعادة ضبط كلمة المرور. يجعل الكود أكثر وضوحاً — اسم ملف Controller يصف ما يفعله بالضبط.

7. تطبيق Middleware على المتحكمات

بدلاً من تطبيق Middleware على كل مسار منفرداً في routes/web.php، يمكنك تطبيقه مباشرة داخل الـ Controller نفسه — مما يجعله جزءاً من منطق الكلاس.

في Laravel 11، الطريقة الموصى بها هي استخدام الدالة الثابتة middleware():

app/Http/Controllers/ArticleController.php
<?php
class ArticleController extends Controller
{
    // تطبيق Middleware على الـ Controller بالكامل
    public static function middleware(): array
    {
        return [
            'auth',                          // كل الدوال تتطلب تسجيل دخول

            // middleware فقط على دوال محددة
            new Middleware('admin', only: ['destroy']),

            // middleware على كل شيء ماعدا دوال معينة
            new Middleware('verified', except: ['index', 'show']),
        ];
    }

    public function index()   { /* متاح للجميع المسجلين */ }
    public function show()    { /* متاح للجميع المسجلين */ }
    public function create()  { /* يتطلب verified */ }
    public function destroy() { /* يتطلب verified + admin */ }
}

8. أنواع الاستجابات التي يُرجعها الـ Controller

الـ Controller لا يُرجع دائماً صفحة HTML — في التطبيقات الحديثة، الاستجابة قد تكون JSON أو ملف أو إعادة توجيه. إليك أنواع الاستجابات الأكثر شيوعاً:

أنواع الاستجابات
// 1. إرجاع View (صفحة HTML)
return view('articles.index', compact('articles'));

// 2. إعادة توجيه
return redirect()->route('articles.index');
return redirect()->back()->withErrors($errors);
return redirect()->route('dashboard')->with('message', 'مرحباً!');

// 3. استجابة JSON (للـ API)
return response()->json([
    'status'   => 'success',
    'articles' => $articles,
    'total'    => $articles->count(),
]);

// 4. استجابة JSON مع كود HTTP
return response()->json(['error' => 'غير مصرح'], 403);

// 5. تحميل ملف
return response()->download(storage_path('exports/report.pdf'), 'تقرير.pdf');

// 6. عرض ملف في المتصفح
return response()->file(storage_path('invoices/INV-001.pdf'));
ماذا تعني with('key', 'value') عند إعادة التوجيه؟
هذه الطريقة تُخزّن رسالة في الـ Session Flash — تبقى متاحة في الطلب التالي فقط ثم تُحذف. في قالب Blade تستخدمها هكذا:
@if (session('success'))
    <div class="alert alert-success">{{ session('success') }}</div>
@endif

أسئلة شائعة عن Controllers في Laravel

أسئلة حقيقية يطرحها الطلاب عند التعامل مع المتحكمات في Laravel لأول مرة:

هذا النمط جيد ومُوصى به في معظم الحالات، لكنه ليس قاعدة صارمة.

قد يكون عندك ArticleController لإدارة المقالات، لكن أيضاً PublishedArticleController فقط لعرض المقالات المنشورة للعموم، وArticleSearchController للبحث. هذا النهج يُسمى Single Responsibility Principle — كل Controller يفعل شيئاً واحداً جيداً.

$request->all() تُرجع كل البيانات المُرسَلة — بما فيها حقول لم تطلبها أو لم تتحقق منها. هذا خطير إذا استخدمته مع Article::create() مباشرةً (ثغرة Mass Assignment).

$request->validated() تُرجع فقط البيانات التي تحققت منها بقواعد Validation. استخدم دائماً validated() عند الحفظ في قاعدة البيانات — هذا آمن 100%.

هذه مشكلة شائعة تُسمى "Fat Controller". الحلول:

  • انقل التحقق لـ Form Request (make:request)
  • انقل المنطق المعقد لـ Service Class داخل app/Services/
  • انقل استعلامات قاعدة البيانات لـ Repository Pattern
  • قسّم الـ Controller الكبير لعدة Controllers أصغر بمسؤوليات محددة

القاعدة الذهبية: إذا كانت دالة Controller تتجاوز 15 سطراً، فكّر في إعادة هيكلتها.

عندما تريد التحكم الكامل في منطق التحقق، استخدم Validator facade:

use Illuminate\Support\Facades\Validator;

$validator = Validator::make($request->all(), [
    'email' => 'required|email|unique:users',
]);

if ($validator->fails()) {
    return redirect()->back()
        ->withErrors($validator)
        ->withInput();
}

// أو استخدم validate() المضمنة للاختصار
$data = $request->validate(['email' => 'required|email']);

Laravel يُرجع أخطاء الـ Validation تلقائياً كـ $errors متاح في Blade.

9. ملخص — دليل مرجعي سريع للـ Controllers

جدول يجمع أهم ما تعلمته في هذا الدرس للرجوع إليه في أي وقت:

تريد فعل ماذا؟ الأمر أو الصيغة
إنشاء Controller جديد php artisan make:controller ArticleController
إنشاء Resource Controller (7 دوال) php artisan make:controller ArticleController --resource
إنشاء Model + Migration + Controller دفعة واحدة php artisan make:model Article -mcr
إنشاء Single Action Controller php artisan make:controller ProcessController --invokable
إنشاء Form Request للتحقق php artisan make:request StoreArticleRequest
استلام بيانات من المستخدم $request->input('field') أو $request->field
التحقق من البيانات $request->validate([...])
إرجاع صفحة View return view('name', compact('var'));
إعادة توجيه مع رسالة redirect()->route('name')->with('key', 'value')
إرجاع JSON return response()->json(['data' => $data]);
الدرس القادم

محرك القوالب Blade — عرض البيانات بطريقة أنيقة 🎨

الآن تعرف كيف تستلم البيانات وتعالجها في Controllers. الخطوة التالية هي تعلم كيف تعرضها للمستخدم بشكل جميل وديناميكي باستخدام محرك القوالب Blade — أداة Laravel الأكثر إبداعاً.

تعلم محرك Blade
المحرر الذكي

اكتب الكود وشاهد النتيجة فوراً

جرب الآن مجاناً
قناة ديف عربي

تابع أحدث الدروس والتحديثات مباشرة على واتساب

انضم الآن