النماذج والتحقق من البيانات في Laravel
في هذا الدرس سنكمل المدونة التي نبنيها: صفحة يكتب فيها المستخدم مقالاً جديداً، فيتحقق Laravel من البيانات، ويعرض الأخطاء بوضوح، ثم يحفظ المقال ويعرض رسالة نجاح. تذكّر كم سطراً كتبت لنموذج التواصل في كورس PHP، وستلاحظ كم يختصر Laravel من العمل.
المسارات والمتحكم
Section titled “المسارات والمتحكم”نحتاج مسارين: واحد يعرض النموذج، وآخر يستقبل بياناته:
Route::get('/posts/create', [PostController::class, 'create'])->name('posts.create');Route::post('/posts', [PostController::class, 'store'])->name('posts.store');لاحظ أن المسار /posts/create مكتوب قبل المسار /posts/{id} في الملف، حتى لا تُفهم كلمة create على أنها رقم مقال.
public function create(){ return view('posts.create');}النموذج وحماية CSRF
Section titled “النموذج وحماية CSRF”@extends('layouts.app')
@section('title', 'مقال جديد')
@section('content') <h1>مقال جديد</h1>
<form action="{{ route('posts.store') }}" method="POST"> @csrf
<label for="title">العنوان</label> <input type="text" id="title" name="title" value="{{ old('title') }}"> @error('title') <p class="error">{{ $message }}</p> @enderror
<label for="body">المحتوى</label> <textarea id="body" name="body">{{ old('body') }}</textarea> @error('body') <p class="error">{{ $message }}</p> @enderror
<button type="submit">نشر</button> </form>@endsectionأهم سطر هنا هو @csrf. يتحول في الصفحة إلى حقل مخفي فيه رمز سري فريد:
<input type="hidden" name="_token" value="..." autocomplete="off">لماذا؟ هجوم اسمه CSRF (Cross-Site Request Forgery): موقع خبيث يخدع متصفحك ليرسل نموذجاً لموقعك وأنت مسجل الدخول، فينشر أو يحذف باسمك دون علمك. الرمز السري يثبت أن النموذج جاء من صفحتك أنت. وأي طلب POST بدون هذا الرمز يرفضه Laravel بخطأ 419 Page Expired.
تخيّله مثل ختم المدرسة على الشهادة: أي ورقة بدون الختم الأصلي مرفوضة، مهما كان كلامها مقنعاً.
التحقق من البيانات: validate
Section titled “التحقق من البيانات: validate”public function store(Request $request){ $validated = $request->validate([ 'title' => 'required|min:5|max:100', 'body' => 'required|min:20', ]);
Post::create($validated);
return redirect()->route('posts.index') ->with('success', 'تم نشر المقال بنجاح');}الدالة validate تستقبل قواعد لكل حقل، مفصولة بالعلامة |:
| القاعدة | معناها |
|---|---|
required |
الحقل إجباري |
min:5 و max:100 |
أقل وأكثر عدد حروف للنص (أو قيمة للرقم) |
email |
بريد إلكتروني صحيح |
numeric و integer |
رقم، ورقم صحيح |
unique:users,email |
القيمة غير مكررة في عمود بقاعدة البيانات |
confirmed |
يطابق حقلاً آخر اسمه ..._confirmation، مثل تأكيد كلمة المرور |
والسحر هنا: إذا فشل التحقق، لا يُنفَّذ أي سطر بعد validate. يعيد Laravel المستخدم تلقائياً إلى صفحة النموذج، ومعه الأخطاء والقيم التي كتبها. وإذا نجح، تُرجع validate مصفوفة بالحقول المتحقق منها فقط، فنمررها مباشرة إلى Post::create.
عرض الأخطاء والاحتفاظ بالقيم
Section titled “عرض الأخطاء والاحتفاظ بالقيم”عند إرسال عنوان «قصير» ومحتوى فارغ، يعود المستخدم للنموذج ويرى:
<input type="text" id="title" name="title" value="قصير"><p class="error">The title field must be at least 5 characters.</p>
<textarea id="body" name="body"></textarea><p class="error">The body field is required.</p>@error('title')…@enderror: يظهر محتواه فقط إذا كان للحقل خطأ، والمتغير$messageفيه نص الخطأ.old('title'): يعيد ما كتبه المستخدم، فلا يضطر لكتابته من جديد.
رسائل خطأ بالعربية
Section titled “رسائل خطأ بالعربية”الرسائل الافتراضية بالإنجليزية. أسرع حل: مرّر رسائلك كمعامل ثانٍ لـ validate، بصيغة اسم_الحقل.القاعدة:
$request->validate([ 'title' => 'required|min:5|max:100', 'body' => 'required|min:20',], [ 'title.required' => 'اكتب عنواناً للمقال', 'body.min' => 'المحتوى يجب ألا يقل عن :min حرفاً',]);الرمز :min يُستبدل تلقائياً بالقيمة من القاعدة. عند إرسال عنوان فارغ ومحتوى «قصير»:
<p class="error">اكتب عنواناً للمقال</p><p class="error">المحتوى يجب ألا يقل عن 20 حرفاً</p>التحويل مع رسالة نجاح
Section titled “التحويل مع رسالة نجاح”بعد الحفظ، لا نعرض صفحة مباشرة، بل نحوّل المستخدم لصفحة المقالات. لماذا؟ لو عرضنا الصفحة مباشرة ثم ضغط المستخدم «تحديث»، سيرسل المتصفح النموذج مرة ثانية وينشر المقال مكرراً! هذا النمط اسمه Post/Redirect/Get.
والدالة with('success', '...') تحفظ رسالة مؤقتة (Flash) تظهر في الطلب التالي فقط ثم تختفي. نعرضها في صفحة المقالات:
@if (session('success')) <p class="alert">{{ session('success') }}</p>@endifبعد نشر مقال صحيح، يرى المستخدم:
<p class="alert">تم نشر المقال بنجاح</p><h1>كل المقالات</h1><article> <h2><a href="http://localhost:8000/posts/1">تعلّمت Laravel اليوم</a></h2></article>جرّب بنفسك
Section titled “جرّب بنفسك”- أضف المسارين
posts.createوposts.store، والدالتينcreateوstoreفي المتحكم. - أنشئ صفحة النموذج بـ
@csrfو@errorوold. - جرّب الإرسال فارغاً، ثم بعنوان قصير، ثم ببيانات صحيحة، وتأكد من ظهور رسالة النجاح.
- احذف
@csrfمؤقتاً وجرّب الإرسال، ولاحظ خطأ 419، ثم أعده. - تحدٍّ: أضف رسائل عربية لكل القواعد، وأضف صفحة تعديل بدالتي
editوupdate.
أخطاء شائعة
Section titled “أخطاء شائعة”- خطأ 419 Page Expired: نسيت
@csrfداخل النموذج. - ضياع ما كتبه المستخدم بعد الخطأ: نسيت
value="{{ old('title') }}". - التحقق في المتصفح فقط:
requiredفي HTML يمكن تجاوزها.validateعلى الخادم إجبارية دائماً.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ماذا يحدث إذا فشل $request->validate() في المتحكم؟
عند فشل التحقق يتوقف تنفيذ الدالة، ويحوّل Laravel المستخدم للخلف ومعه الأخطاء وما كتبه.
سؤال
ما وظيفة @csrf داخل النموذج؟
الرمز يحمي من هجمات CSRF، وأي طلب POST بدونه يُرفض بخطأ 419.
الخلاصة
Section titled “الخلاصة”- مسار GET يعرض النموذج، ومسار POST يستقبله، والنموذج يحتاج
@csrfدائماً. $request->validate([...])تتحقق بقواعد مثلrequired|min:5، وعند الفشل تعيد المستخدم تلقائياً.@errorو$messageلعرض الأخطاء، وold()للاحتفاظ بالقيم.- الرسائل العربية بمعامل ثانٍ لـ
validateأو بملفات اللغة فيlang/ar. - بعد الحفظ حوّل بـ
redirect()->route(...)->with(...)، واعرض الرسالة بـsession().