Laravel المتقدم
التحقق المتقدم وطلبات النماذج
التحقق المتقدم وطلبات النماذج
أتقن تقنيات التحقق المتقدمة في Laravel بما في ذلك القواعد المعقدة والتحقق الشرطي والمحققات المخصصة وتحسين طلبات النماذج.
قواعد التحقق المعقدة
يوفر Laravel قواعد تحقق قوية تتجاوز الفحوصات الأساسية:
use Illuminate\Validation\Rule;
// فريد مع أعمدة متعددة
'email' => [
'required',
Rule::unique('users')
->where('account_id', $accountId)
->ignore($userId)
->whereNull('deleted_at')
],
// فحص الوجود الشرطي
'parent_id' => [
'nullable',
Rule::exists('categories', 'id')
->where('type', 'parent')
->whereNull('archived_at')
],
// في مع قيم قاعدة البيانات
'status' => [
'required',
Rule::in(Status::pluck('code')->toArray())
],
// قاعدة فريدة مخصصة مع التشفير
'username' => [
'required',
Rule::unique('users')
->using(function ($query) {
return $query->where('tenant_id', auth()->user()->tenant_id);
})
]
نصيحة احترافية: استخدم كائنات Rule بدلاً من السلاسل المفصولة بأنابيب للتحقق المعقد. فهي توفر قابلية قراءة أفضل وإكمال تلقائي لـ IDE.
التحقق الشرطي
تطبيق قواعد التحقق بناءً على الشروط:
use Illuminate\Validation\Rule;
public function rules()
{
return [
'email' => 'required|email',
'role' => 'required|in:user,admin,manager',
// التحقق فقط إذا كان الدور مسؤول
'permissions' => [
Rule::requiredIf($this->role === 'admin'),
'array'
],
'permissions.*' => 'exists:permissions,id',
// التحقق بناءً على حقل آخر
'company_name' => [
Rule::requiredIf(function () {
return $this->account_type === 'business';
})
],
// استبعاد التحقق إذا تم استيفاء الشرط
'vat_number' => [
Rule::excludeIf($this->country !== 'UK'),
'required',
'regex:/^GB[0-9]{9}$/'
],
// منع الحقل بناءً على الشرط
'discount_code' => [
Rule::prohibitedIf($this->user()->hasActiveSubscription()),
'string'
]
];
}
// التحقق أحياناً
public function rules()
{
$rules = [
'name' => 'required',
'email' => 'required|email'
];
// إضافة القواعد بشكل شرطي
if ($this->has('is_premium')) {
$rules['subscription_plan'] = 'required|exists:plans,id';
$rules['payment_method'] = 'required|in:card,paypal';
}
return $rules;
}
قواعد التحقق المخصصة
إنشاء قواعد تحقق مخصصة قابلة لإعادة الاستخدام:
// إنشاء فئة القاعدة
php artisan make:rule Uppercase
// app/Rules/Uppercase.php
namespace App\Rules;
use Illuminate\Contracts\Validation\ValidationRule;
class Uppercase implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (strtoupper($value) !== $value) {
$fail('يجب أن يكون :attribute بأحرف كبيرة.');
}
}
}
// قاعدة مخصصة متقدمة مع معاملات
class PhoneNumber implements ValidationRule
{
public function __construct(
private string $countryCode = 'US'
) {}
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$pattern = match($this->countryCode) {
'US' => '/^\+1[0-9]{10}$/',
'UK' => '/^\+44[0-9]{10}$/',
'SA' => '/^\+966[0-9]{9}$/',
default => '/^\+[0-9]{10,15}$/'
};
if (!preg_match($pattern, $value)) {
$fail("يجب أن يكون :attribute رقم هاتف {$this->countryCode} صحيح.");
}
}
}
// الاستخدام في طلب النموذج
public function rules()
{
return [
'name' => ['required', new Uppercase],
'phone' => ['required', new PhoneNumber('SA')]
];
}
ملاحظة: القواعد المخصصة هي كائنات محفزة، مما يجعلها أكثر مرونة من التحقق المستند إلى الإغلاق. فهي تدعم حقن التبعية وأسهل في الاختبار.
خطافات ما بعد التحقق
تنفيذ المنطق بعد نجاح التحقق:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreProductRequest extends FormRequest
{
public function rules()
{
return [
'name' => 'required|string|max:255',
'price' => 'required|numeric|min:0',
'category_id' => 'required|exists:categories,id'
];
}
// التنفيذ بعد نجاح التحقق
protected function passedValidation()
{
// دمج بيانات إضافية
$this->merge([
'slug' => Str::slug($this->name),
'user_id' => auth()->id(),
'published_at' => now()
]);
// تحويل البيانات
$this->merge([
'price' => $this->price * 100, // التحويل إلى سنتات
'tags' => array_map('trim', explode(',', $this->tags))
]);
}
// التنفيذ بعد إنشاء المحقق ولكن قبل التحقق
public function withValidator($validator)
{
$validator->after(function ($validator) {
// التحقق من الحقول المتقاطعة
if ($this->start_date > $this->end_date) {
$validator->errors()->add(
'end_date',
'يجب أن يكون تاريخ الانتهاء بعد تاريخ البدء.'
);
}
// التحقق من منطق الأعمال
if ($this->discount > 0 && !auth()->user()->canApplyDiscounts()) {
$validator->errors()->add(
'discount',
'ليس لديك إذن لتطبيق الخصومات.'
);
}
});
}
}
التحقق المتداخل
التحقق من هياكل البيانات المتداخلة المعقدة:
public function rules()
{
return [
// التحقق من المصفوفة
'items' => 'required|array|min:1|max:10',
'items.*.name' => 'required|string|max:100',
'items.*.quantity' => 'required|integer|min:1',
'items.*.price' => 'required|numeric|min:0',
// التحقق من الكائن المتداخل
'shipping' => 'required|array',
'shipping.address' => 'required|string',
'shipping.city' => 'required|string',
'shipping.postal_code' => 'required|string',
// التداخل متعدد المستويات
'users' => 'array',
'users.*.name' => 'required|string',
'users.*.email' => 'required|email',
'users.*.roles' => 'array',
'users.*.roles.*.name' => 'required|exists:roles,name',
'users.*.roles.*.permissions' => 'array',
'users.*.roles.*.permissions.*' => 'exists:permissions,id',
// التحقق من المفاتيح الديناميكية
'metadata' => 'array',
'metadata.*' => 'string|max:500'
];
}
// التحقق المتداخل المخصص
public function withValidator($validator)
{
$validator->after(function ($validator) {
$total = collect($this->items)
->sum(fn($item) => $item['quantity'] * $item['price']);
if ($total > 10000) {
$validator->errors()->add(
'items',
'لا يمكن أن تتجاوز قيمة الطلب الإجمالية 10,000 دولار.'
);
}
});
}
تحذير: يمكن أن يؤثر التحقق المتداخل على الأداء في مجموعات البيانات الكبيرة. فكر في التحقق على دفعات أو استخدام قيود قاعدة البيانات للعمليات الجماعية.
تقنيات طلبات النماذج المتقدمة
تحسين طلبات النماذج بأنماط متقدمة:
namespace App\Http\Requests;
class UpdateUserRequest extends FormRequest
{
// الترخيص بالسياسات
public function authorize()
{
return $this->user()->can('update', $this->route('user'));
}
// قواعد ديناميكية بناءً على دور المستخدم
public function rules()
{
$rules = [
'name' => 'required|string|max:255',
'email' => [
'required',
'email',
Rule::unique('users')->ignore($this->route('user'))
]
];
// المسؤولون يمكنهم تغيير الأدوار
if ($this->user()->isAdmin()) {
$rules['role'] = 'required|exists:roles,name';
}
return $rules;
}
// رسائل خطأ مخصصة
public function messages()
{
return [
'email.unique' => 'هذا البريد الإلكتروني مأخوذ بالفعل من قبل مستخدم آخر.',
'name.required' => 'يرجى تقديم اسم للمستخدم.'
];
}
// أسماء الخصائص المخصصة
public function attributes()
{
return [
'email' => 'عنوان البريد الإلكتروني',
'phone' => 'رقم الهاتف'
];
}
// تحضير المدخلات قبل التحقق
protected function prepareForValidation()
{
$this->merge([
'slug' => Str::slug($this->name),
'phone' => preg_replace('/[^0-9+]/', '', $this->phone)
]);
}
}
معالجة أخطاء التحقق
تخصيص كيفية إرجاع أخطاء التحقق:
// إرجاع تنسيق خطأ مخصص
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException(
response()->json([
'success' => false,
'message' => 'فشل التحقق',
'errors' => $validator->errors()->toArray(),
'timestamp' => now()->toIso8601String()
], 422)
);
}
// تسجيل إخفاقات التحقق
protected function failedValidation(Validator $validator)
{
Log::warning('فشل التحقق', [
'user_id' => auth()->id(),
'url' => $this->fullUrl(),
'errors' => $validator->errors()->toArray()
]);
parent::failedValidation($validator);
}
// فشل الترخيص المخصص
protected function failedAuthorization()
{
throw new HttpResponseException(
response()->json([
'message' => 'أنت غير مصرح لك بتنفيذ هذا الإجراء.'
], 403)
);
}
تمرين 1: أنشئ طلب نموذج لإنشاء فاتورة مع عناصر سطر متداخلة. تحقق من أن:
1. الفاتورة لها customer_id و issue_date و due_date
2. تاريخ الاستحقاق بعد تاريخ الإصدار
3. مصفوفة عناصر السطر بها عنصر واحد على الأقل
4. كل عنصر له وصف وكمية (الحد الأدنى 1) وسعر الوحدة (الحد الأدنى 0)
5. لا يتجاوز المبلغ الإجمالي 50,000 دولار
استخدم خطافات ما بعد التحقق لحساب الإجمالي وإنشاء رقم الفاتورة.
1. الفاتورة لها customer_id و issue_date و due_date
2. تاريخ الاستحقاق بعد تاريخ الإصدار
3. مصفوفة عناصر السطر بها عنصر واحد على الأقل
4. كل عنصر له وصف وكمية (الحد الأدنى 1) وسعر الوحدة (الحد الأدنى 0)
5. لا يتجاوز المبلغ الإجمالي 50,000 دولار
استخدم خطافات ما بعد التحقق لحساب الإجمالي وإنشاء رقم الفاتورة.
تمرين 2: أنشئ قاعدة تحقق مخصصة "StrongPassword" تتطلب:
1. 12 حرفاً كحد أدنى
2. حرف كبير واحد على الأقل
3. حرف صغير واحد على الأقل
4. رقم واحد على الأقل
5. حرف خاص واحد على الأقل
6. ليس في قائمة كلمات المرور الشائعة (فحص ضد المصفوفة)
نفذ برسائل خطأ واضحة لكل متطلب فشل.
1. 12 حرفاً كحد أدنى
2. حرف كبير واحد على الأقل
3. حرف صغير واحد على الأقل
4. رقم واحد على الأقل
5. حرف خاص واحد على الأقل
6. ليس في قائمة كلمات المرور الشائعة (فحص ضد المصفوفة)
نفذ برسائل خطأ واضحة لكل متطلب فشل.
تمرين 3: ابن طلب نموذج متعدد الخطوات يتحقق من بيانات تسجيل المستخدم:
الخطوة 1: البريد الإلكتروني، كلمة المرور، تأكيد كلمة المرور
الخطوة 2: الاسم، الهاتف، تاريخ الميلاد
الخطوة 3: العنوان، المدينة، الرمز البريدي، البلد
استخدم التحقق الشرطي بناءً على حقل "خطوة". احفظ البيانات الوسيطة في الجلسة بين الخطوات.
الخطوة 1: البريد الإلكتروني، كلمة المرور، تأكيد كلمة المرور
الخطوة 2: الاسم، الهاتف، تاريخ الميلاد
الخطوة 3: العنوان، المدينة، الرمز البريدي، البلد
استخدم التحقق الشرطي بناءً على حقل "خطوة". احفظ البيانات الوسيطة في الجلسة بين الخطوات.