404 Not Found

404 Not Found


nginx

التحقق من النماذج في Laravel

التحقق هو "نقطة التفتيش الأمنية" في Laravel — جميع البيانات الداخلة يجب أن تجتاز التحقق، ولا يمكن لأي بيانات غير صالحة أن تنفذ.

1. ما ستتعلمه


2. قصة حقيقية لمدقق أمني

(1) المشكلة: إدخال المستخدم يسبب تناقضات في البيانات

عندما سجلت Alice في ShopMetrics، أدخلت "abc" كبريد إلكتروني و"123" كرقم هاتف وتركت حقل اسم المتجر فارغًا — النظام قبل كل شيء، مما أدى إلى قاعدة بيانات مليئة بالبيانات غير الصالحة. قدم مستخدم خبيث منتجًا بسعر سلبي عبر API لمتجر Bob، مما أدى إلى مبلغ طلب بقيمة -999 دولار وتعطل التقارير المالية. أثناء تدقيق أمني، اكتشف Charlie أن 17 نقطة نهاية ليس لديها أي تحقق من الإدخال.

(2) حلول التحقق في Laravel

يعترض تحقق Laravel البيانات قبل دخولها النظام — مصفوفة واحدة من القواعد تغطي كل منطق التحقق، وصنف FormRequest يبقي المتحكمات نظيفة.

PHP
// قواعد التحقق — 30 ثانية للكتابة، تحمي للأبد
$validated = $request->validate([
    'name' => 'required|string|max:255',
    'email' => 'required|email|unique:users',
    'price' => 'required|numeric|min:0',
]);

(3) العائد

بعد أن فعّلت Alice التحقق، انخفضت التسجيلات غير الصالحة إلى صفر؛ أسعار منتجات Bob لن تكون سالبة أبدًا بعد الآن؛ واجتاز Charlie جميع نقاط النهاية الـ 17 في التدقيق الأمني.


3. مرجع سريع لقواعد التحقق

(1) القواعد الشائعة

القاعدة الوصف مثال
required مطلوب 'name' => 'required'
email تنسيق بريد إلكتروني 'email' => 'required|email'
unique:table,column فريد 'slug' => 'unique:shops,slug'
exists:table,column موجود 'shop_id' => 'exists:shops,id'
min:n حد أدنى للقيمة/الطول 'price' => 'numeric|min:0'
max:n حد أقصى للقيمة/الطول 'name' => 'string|max:255'
numeric رقم 'quantity' => 'required|numeric'
integer عدد صحيح 'page' => 'integer|min:1'
string سلسلة نصية 'name' => 'required|string'
boolean قيمة منطقية 'is_active' => 'boolean'
date تاريخ 'published_at' => 'date'
file ملف 'logo' => 'file|max:2048'
image ملف صورة 'photo' => 'image|mimes:jpeg,png'
confirmed تأكيد ثانوي 'password' => 'confirmed'
regex:pattern تعابير نمطية 'phone' => 'regex:/^[0-9]{10}$/'
in:a,b,c قيمة تعداد 'status' => 'in:active,suspended'

(2) عملية خط أنابيب التحقق

100%
flowchart TD
    A[إدخال الطلب] --> B[قواعد التحقق]
    B -->|نجاح| C[بيانات مُنظفة]
    C --> D[منطق المتحكم]
    B -->|فشل| E[إعادة توجيه مع الأخطاء]
    E --> F["عرض \$errors في العرض"]

(1) ▶ مثال: التحقق من إنشاء منتج ShopMetrics

PHP
// تحقق مباشر في المتحكم
public function store(Request $request): RedirectResponse
{
    $validated = $request->validate([
        'name' => 'required|string|max:255',
        'sku' => 'required|string|unique:products,sku',
        'price' => 'required|numeric|min:0.01|max:999999.99',
        'stock' => 'required|integer|min:0',
        'category_id' => 'required|exists:categories,id',
        'description' => 'nullable|string|max:5000',
        'is_active' => 'boolean',
    ]);

    $product = Product::create($validated);

    return redirect()->route('products.show', $product)
        ->with('success', 'تم إنشاء المنتج.');
}

الناتج:

TEXT
// تم التنفيذ بنجاح

4. صنف التحقق من Form Request

(1) إنشاء Form Request

BASH
php artisan make:request StoreShopRequest
php artisan make:request UpdateShopRequest

(2) تعريف القواعد والتفويض

PHP
// app/Http/Requests/StoreShopRequest.php
class StoreShopRequest extends FormRequest
{
    public function authorize(): bool
    {
        return auth()->check() && auth()->user()->can('create', Shop::class);
    }

    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'slug' => 'required|string|unique:shops,slug',
            'description' => 'nullable|string|max:5000',
            'domain' => 'nullable|url',
            'status' => 'in:active,suspended',
        ];
    }

    public function messages(): array
    {
        return [
            'name.required' => 'اسم المتجر مطلوب.',
            'slug.unique' => 'رابط URL هذا مستخدم بالفعل.',
            'domain.url' => 'يرجى إدخال عنوان URL صالح.',
        ];
    }
}

(3) استخدامه في المتحكم

PHP
public function store(StoreShopRequest $request): RedirectResponse
{
    // $request->validated() يحتوي فقط على البيانات المُتحقق منها
    $shop = Shop::create($request->validated());
    return redirect()->route('shops.show', $shop);
}
البُعد تحقق مباشر Form Request
الموقع داخل دالة المتحكم ملف صنف منفصل
قابلية إعادة الاستخدام منخفضة ✅ قابلية إعادة الاستخدام عبر متحكمات متعددة
فحص التفويض يدوي ✅ authorize()
كود المتحكم أطول مبسّط
مناسب لـ تحقق بسيط تحقق معقد/قابل لإعادة الاستخدام

(1) ▶ مثال: StoreShopRequest لـ ShopMetrics

PHP
// app/Http/Requests/StoreShopRequest.php
class StoreShopRequest extends FormRequest
{
    public function authorize(): bool
    {
        return auth()->user()->role === 'tenant_owner';
    }

    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'slug' => 'required|alpha_dash|unique:shops,slug,NULL,id,tenant_id,' . tenant()->id,
            'description' => 'nullable|string|max:5000',
            'status' => 'sometimes|in:active,suspended',
        ];
    }

    public function messages(): array
    {
        return [
            'slug.unique' => 'لديك بالفعل متجر بهذا الرابط.',
            'slug.alpha_dash' => 'الرابط يمكن أن يحتوي فقط على أحرف وأرقام وشرطات.',
        ];
    }

    protected function prepareForValidation(): void
    {
        $this->merge([
            'tenant_id' => tenant()->id,
            'slug' => Str::slug($this->slug ?? $this->name),
        ]);
    }
}

الناتج:

TEXT
// تم التنفيذ بنجاح

5. قواعد التحقق المخصصة

(1) قواعد الإغلاق

PHP
// قاعدة مخصصة مباشرة
$validated = $request->validate([
    'discount' => [
        'required',
        'numeric',
        'min:0',
        function (string $attribute, mixed $value, Closure $fail) {
            if ($value > request('subtotal')) {
                $fail('لا يمكن أن يتجاوز الخصم المبلغ الجزئي.');
            }
        },
    ],
]);

(2) كائن القاعدة

BASH
php artisan make:rule ValidCouponCode
PHP
// app/Rules/ValidCouponCode.php
class ValidCouponCode implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $coupon = Coupon::where('code', $value)
            ->where('expires_at', '>', now())
            ->where('usage_limit', '>', DB::raw('usage_count'))
            ->first();

        if (!$coupon) {
            $fail('رمز الكوبون هذا غير صالح أو منتهي الصلاحية.');
        }
    }
}

// الاستخدام
public function rules(): array
{
    return [
        'coupon_code' => ['nullable', 'string', new ValidCouponCode()],
    ];
}
الطريقة السيناريوهات المناسبة قابلية إعادة الاستخدام
إغلاق منطق بسيط لمرة واحدة منخفضة
كائن قاعدة منطق معقد/قابل لإعادة الاستخدام
طريقة Rule::class تحقق مرتبط بقاعدة البيانات

(3) طرق صنف Rule

PHP
use Illuminate\Validation\Rule;

// فريد مع تجاهل النموذج الحالي
'slug' => Rule::unique('shops', 'slug')->ignore($shop->id),

// in مع قيم ديناميكية
'status' => Rule::in(['active', 'suspended', 'closed']),

// exists مع استعلام إضافي
'shop_id' => Rule::exists('shops', 'id')->where(function ($query) {
    $query->where('tenant_id', tenant()->id);
}),

(1) ▶ مثال: قواعد التحقق من طلبات ShopMetrics

PHP
// app/Http/Requests/StoreOrderRequest.php
class StoreOrderRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'items' => 'required|array|min:1',
            'items.*.product_id' => [
                'required',
                'integer',
                Rule::exists('products', 'id')->where('is_active', true),
            ],
            'items.*.quantity' => 'required|integer|min:1|max:100',
            'coupon_code' => ['nullable', 'string', new ValidCouponCode()],
            'discount' => [
                'sometimes',
                'numeric',
                'min:0',
                function ($attribute, $value, $fail) {
                    if ($value > $this->input('subtotal', 0)) {
                        $fail('لا يمكن أن يتجاوز الخصم المبلغ الجزئي.');
                    }
                },
            ],
        ];
    }
}

الناتج:

TEXT
// تم التنفيذ بنجاح

6. معالجة رسائل الخطأ

(1) عرض الأخطاء في صفحات الويب

HTML
<!-- عرض جميع الأخطاء -->
@if ($errors->any())
    <div class="alert alert-error">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

<!-- عرض خطأ لحقل محدد -->
<input type="text" name="name" value="{{ old('name') }}"
       class="{{ $errors->has('name') ? 'border-red-500' : '' }}">
@error('name')
    <p class="text-red-500 text-sm">{{ $message }}</p>
@enderror

(2) استجابة خطأ API JSON

JSON
{
    "message": "البيانات المقدمة غير صالحة.",
    "errors": {
        "name": ["حقل الاسم مطلوب."],
        "email": ["يجب أن يكون البريد الإلكتروني عنوان بريد إلكتروني صالح."]
    }
}
نوع الطلب سلوك فشل التحقق تنسيق غير صالح
نماذج الويب إعادة توجيه للنموذج + عرض الأخطاء متغير عرض $errors
API JSON يعيد 422 + JSON {"errors": {...}}
AJAX يعيد 422 + JSON مثل API

(1) ▶ مثال: معالجة أخطاء التحقق في API لـ ShopMetrics

PHP
// app/Http/Controllers/Api/ShopController.php
public function store(StoreShopRequest $request): JsonResponse
{
    $shop = Shop::create($request->validated());

    return response()->json([
        'message' => 'تم إنشاء المتجر بنجاح.',
        'data' => new ShopResource($shop),
    ], 201);
}

// العميل يستلم 422 عند فشل التحقق:
// {
//   "message": "الرابط مستخدم بالفعل.",
//   "errors": {
//     "slug": ["الرابط مستخدم بالفعل."]
//   }
// }

الناتج:

TEXT
// تم التنفيذ بنجاح

7. التحقق الشرطي

(1) قاعدة sometimes

PHP
// التحقق فقط إذا كان الحقل موجودًا
$validated = $request->validate([
    'name' => 'required|string',
    'notes' => 'sometimes|nullable|string|max:5000',
]);

// قواعد شرطية بناءً على حقل آخر
Validator::make($data, [
    'payment_method' => 'required|in:credit_card,bank_transfer',
    'card_number' => 'required_if:payment_method,credit_card|numeric',
    'bank_account' => 'required_if:payment_method,bank_transfer|numeric',
]);

(2) قاعدة bail

PHP
// إيقاف التحقق بعد أول فشل في حقل
$validated = $request->validate([
    'email' => 'bail|required|email|unique:users',
    // إذا فشل required، لن يعمل email وunique
]);
القاعدة الوظيفة حالة الاستخدام
sometimes تحقق فقط إذا كان الحقل موجودًا حقل اختياري
bail إيقاف بعد أول فشل تحقق مكلف
required_if حقل مطلوب طريقة الدفع ← رقم البطاقة
required_unless حقل اختياري
required_with مطلوب تأكيد كلمة المرور
prohibited_if محظور شرطيًا
exclude_if استبعاد شرطي لا يُكتب في validated
nullable يسمح بـ null حقل اختياري

(1) ▶ مثال: سيناريو التحقق الشرطي في ShopMetrics

PHP
// app/Http/Requests/UpdateSubscriptionRequest.php
class UpdateSubscriptionRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'plan_id' => 'required|exists:plans,id',
            'payment_method' => 'required|in:credit_card,paypal,bank_transfer',
            'card_number' => 'required_if:payment_method,credit_card|string|size:16',
            'card_cvv' => 'required_if:payment_method,credit_card|string|size:3',
            'paypal_email' => 'required_if:payment_method,paypal|email',
            'bank_account' => 'required_if:payment_method,bank_transfer|string',
            'coupon_code' => 'sometimes|nullable|string|max:50',
        ];
    }
}

الناتج:

TEXT
// تم التنفيذ بنجاح

8. مثال شامل: عملية التحقق الكاملة لـ ShopMetrics

PHP
// ============================================
// شامل: التحقق من طلبات ShopMetrics
// يغطي: Form Request، قواعد مخصصة، شرطي، أخطاء
// ============================================

// app/Rules/SufficientStock.php
class SufficientStock implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $productId = request()->input(str_replace('.quantity', '.product_id', $attribute));
        $product = Product::find($productId);
        if ($product && $value > $product->stock) {
            $fail("متاح فقط {$product->stock} وحدة من {$product->name}.");
        }
    }
}

// app/Http/Requests/StoreOrderRequest.php
class StoreOrderRequest extends FormRequest
{
    public function authorize(): bool
    {
        return auth()->check();
    }

    public function rules(): array
    {
        return [
            'items' => 'required|array|min:1|max:50',
            'items.*.product_id' => [
                'required',
                'integer',
                Rule::exists('products', 'id')->where('is_active', true),
            ],
            'items.*.quantity' => [
                'required',
                'integer',
                'min:1',
                'max:100',
                new SufficientStock(),
            ],
            'coupon_code' => 'sometimes|nullable|string|max:50',
            'notes' => 'sometimes|nullable|string|max:1000',
            'shipping_address.line1' => 'required|string|max:255',
            'shipping_address.city' => 'required|string|max:100',
            'shipping_address.zip' => 'required|string|max:20',
            'shipping_address.country' => 'required|string|size:2',
        ];
    }

    public function messages(): array
    {
        return [
            'items.required' => 'سلة التسوق فارغة.',
            'items.min' => 'أضف عنصرًا واحدًا على الأقل لتقديم الطلب.',
            'items.*.product_id.exists' => 'أحد المنتجات المحددة غير متاح.',
            'shipping_address.line1.required' => 'عنوان الشارع مطلوب.',
        ];
    }
}

// المتحكم يبقى نظيفًا
public function store(StoreOrderRequest $request): RedirectResponse
{
    $order = $this->orderService->createFromRequest($request);
    return redirect()->route('orders.show', $order)
        ->with('success', 'تم تقديم الطلب بنجاح!');
}

❓ أسئلة شائعة

س كيف أختار بين validate() وForm Request؟
ج للتحقق البسيط (3-5 قواعد)، استخدم validate() مباشرة؛ للتحقق المعقد (10+ قواعد، قابلية إعادة الاستخدام، أو فحص التفويض)، استخدم Form Request. Form Request هو أفضل ممارسة.
س كيف أستبعد السجل الحالي عند تحديث قاعدة "فريد"؟
ج استخدم Rule::unique('shops', 'slug')->ignore($shop->id) أو تنسيق unique:shops,slug,{shop}. يجب أن يستبعد التحقق من التحديث السجل الحالي؛ وإلا سيفشل التحقق دائمًا.
س ما التنسيق الذي يُعاد عند فشل مصادقة API؟
ج يعيد Laravel تلقائيًا رمز حالة 422 + JSON: {"message":"...","errors":{"field":["رسالة خطأ"]}}. يجب على الواجهة الأمامية تحليل كائن errors بناءً على رمز الحالة 422 لعرض الأخطاء على مستوى الحقل.
س ما غرض prepareForValidation؟
ج يعدّل بيانات الإدخال قبل التحقق — مثل توليد slug تلقائيًا، أو تنسيق رقم الهاتف، أو إضافة قيم افتراضية. البيانات المعدلة تُستخدم في عملية التحقق وفي إخراج validated().
س كيف أختبر قواعد التحقق أثناء الاختبار؟
ج استخدم $this->postJson() لإرسال بيانات غير صالحة؛ تحقق من خطأ 422 ورسالة الخطأ $response->assertJsonValidationErrors('name'). يمكنك أيضًا استخدام Validator::make() لاختبار القواعد مباشرة.
س هل تؤثر قواعد التحقق الكثيرة على الأداء؟
ج قواعد unique وexists تستعلم قاعدة البيانات، مما يكلف أداءً. لواجهات API عالية التردد، فكر في استخدام bail لإنهاء العملية مبكرًا أو تخزين نتائج التحقق مؤقتًا. في معظم السيناريوهات، الأداء ليس مشكلة.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (⭐): أنشئ StoreProductRequest لمنتج ShopMetrics، بما في ذلك قواعد التحقق من name وsku وprice وstock وcategory_id، للتأكد من أن SKU فريد والسعر رقم موجب.

  2. تمرين متقدم (⭐⭐): أنشئ كائن قاعدة ValidCouponCode للتحقق من أن الكوبون لم تنتهِ صلاحيته وما زال لديه استخدامات متبقية. طبّق هذه القاعدة على StoreOrderRequest واختبر استجابات الكوبونات الصالحة وغير الصالحة.

  3. تحدٍ (⭐⭐⭐): نفذ قاعدة SufficientStock (للتحقق من أن كمية الطلب لا تتجاوز المخزون) على حقول items.*.quantity في StoreOrderRequest للتعامل مع مشاكل تنافس المخزون عند طلب عناصر متعددة في نفس الوقت.

Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%