المتحكمات (Controllers) في Laravel 11 — منطق تطبيقك بطريقة احترافية
1. إنشاء Controller جديد بأمر Artisan
أسرع وأنظف طريقة لإنشاء Controller في Laravel هي استخدام Artisan. لا تحتاج لإنشاء الملف يدوياً وكتابة الـ namespace — Artisan يفعل كل ذلك:
سيُنشئ ملف جديد في app/Http/Controllers/ArticleController.php
الملف الذي يُنشئه Artisan يبدو هكذا:
<?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/:
سيُنشئ app/Http/Controllers/Admin/UserController.php مع namespace صحيح تلقائياً.
2. كتابة الدوال وربطها بالمسارات
كل دالة (Method) داخل Controller تستجيب لمسار معين. اتفاقية Laravel لتسمية الدوال ليست عشوائية — لها معاني محددة يفهمها كل مطور Laravel:
<?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', 'تم إنشاء المقال!');
}
}
والربط في ملف المسارات يكون هكذا:
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 يعرف هذا ويوفر لك اختصاراً رائعاً:
ينشئ Controller يحتوي على 7 دوال جاهزة الهيكل.
الملف المُنشَأ يحتوي على هذه الدوال مع تعليقات توضيحية:
<?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 مسارات:
إنشاء Controller مع Model و Migration في آن واحد
بدلاً من تشغيل 3 أوامر منفصلة، يمكنك دمجها في أمر واحد قوي:
-m = Migration | -c = Controller | -r = Resource
— ثلاثة بأمر واحد!
لا. إذا كنت لا تحتاج بعض الدوال (مثل: موردك يعرض فقط بدون إنشاء أو حذف)، يمكنك تحديد ما تريد:
// فقط 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 في الدالة (الطريقة الأفضل)
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
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
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 يصبح أنظف بكثير:
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
class ProcessPaymentController extends Controller
{
public function __invoke(Request $request)
{
// منطق معالجة الدفع
$payment = PaymentService::process($request->validated());
return redirect()->route('payment.success')
->with('receipt', $payment->receipt_number);
}
}
في ملف المسارات، لا حاجة لتحديد اسم الدالة:
use App\Http\Controllers\ProcessPaymentController;
// بدون تحديد method — Laravel يعرف أنه __invoke
Route::post('/payment/process', ProcessPaymentController::class)
->name('payment.process');
عندما تكون العملية محددة ومعزولة: تصدير بيانات، إرسال إشعار، معالجة webhook، إعادة ضبط كلمة المرور. يجعل الكود أكثر وضوحاً — اسم ملف Controller يصف ما يفعله بالضبط.
7. تطبيق Middleware على المتحكمات
بدلاً من تطبيق Middleware على كل مسار منفرداً في routes/web.php،
يمكنك تطبيقه مباشرة داخل الـ Controller نفسه — مما يجعله جزءاً من منطق الكلاس.
في Laravel 11، الطريقة الموصى بها هي استخدام الدالة الثابتة middleware():
<?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