Form Request در Laravel؛ اعتبارسنجی حرفهای دادهها
Form Request در Laravel یکی از بهترین ابزارها برای اعتبارسنجی حرفهای دادهها، تمیز نگهداشتن Controllerها و افزایش امنیت نرمافزارهای تحت وب است. با استفاده از Form Request میتوان قوانین اعتبارسنجی، پیامهای خطا، مجوز دسترسی، آمادهسازی دادهها قبل از اعتبارسنجی و حتی منطقهای تکمیلی بعد از Validation را در کلاسهایی مستقل و قابل نگهداری مدیریت کرد. در این مقاله بهصورت کامل بررسی میکنیم Form Request چیست، چه تفاوتی با اعتبارسنجی مستقیم در Controller دارد، چگونه در پروژههای واقعی Laravel پیادهسازی میشود، چه مزایا و چالشهایی دارد و چطور میتوان از آن برای تولید نرمافزارهای اختصاصی قابل توسعه استفاده کرد.
برای شنیدن متن، روی «پخش صوت مقاله» بزنید.
چرا اعتبارسنجی دادهها در Laravel یک موضوع حیاتی است؟
در هر نرمافزار تحت وب، دادههایی که از سمت کاربر، اپلیکیشن موبایل، پنل مدیریت، API، فرم ثبتنام، درگاه پرداخت، وبهوک یا سرویس خارجی وارد سیستم میشوند، باید قبل از پردازش بررسی شوند. هیچ نرمافزار حرفهای نباید فرض کند داده ورودی همیشه سالم، کامل، درست و امن است.
در پروژههای Laravel، اعتبارسنجی دادهها یکی از پایههای اصلی امنیت، پایداری و کیفیت کد است. اگر دادهها بدون بررسی وارد دیتابیس شوند، ممکن است با خطاهای جدی، اطلاعات ناقص، مشکلات امنیتی، اختلال در گزارشها و حتی آسیب به منطق کسبوکار روبهرو شویم.
برای مثال، در یک سامانه فروشگاهی، اگر تعداد محصول اعتبارسنجی نشود، کاربر ممکن است مقدار منفی ارسال کند. در یک نرمافزار رزرو، اگر تاریخ رزرو بررسی نشود، امکان ثبت تاریخ گذشته وجود دارد. در یک سیستم مالی، اگر مبلغ تراکنش بهدرستی اعتبارسنجی نشود، ممکن است خطای جدی در حسابداری ایجاد شود. در یک CRM، اگر شماره موبایل، ایمیل یا شناسه مشتری بدون کنترل ذخیره شود، کیفیت دادهها در بلندمدت افت میکند.
Laravel ابزارهای مختلفی برای Validation دارد. سادهترین روش استفاده از متد validate در Controller است. اما در پروژههای جدیتر، این روش خیلی زود باعث شلوغی Controllerها، تکرار قوانین و سخت شدن نگهداری کد میشود. راهحل حرفهایتر، استفاده از Form Request در Laravel است.
Form Request کمک میکند اعتبارسنجی دادهها از Controller جدا شود و در کلاسهایی مستقل، تمیز، قابل تست و قابل استفاده مجدد قرار بگیرد. این موضوع در شرکتهایی مثل اسمارتی اپ (SmartyApp) که در زمینه طراحی سایت، تولید نرمافزار اختصاصی و برنامهنویسی نرمافزارهای تحت وب فعالیت میکنند، اهمیت زیادی دارد؛ چون کیفیت معماری پروژه مستقیماً روی هزینه توسعه، پشتیبانی و مقیاسپذیری محصول اثر میگذارد.
Form Request در Laravel چیست؟
Form Request در Laravel یک کلاس اختصاصی برای مدیریت اعتبارسنجی و مجوزدهی درخواستهای ورودی است. این کلاس معمولاً در مسیر زیر قرار میگیرد:
app/Http/Requests
به زبان ساده، Form Request به شما اجازه میدهد قوانین Validation، پیامهای خطا، نام فیلدها، مجوز دسترسی، آمادهسازی دادهها و منطقهای مرتبط با درخواست را از Controller جدا کنید.
برای ساخت یک Form Request میتوان از دستور Artisan زیر استفاده کرد:
php artisan make:request StoreProductRequest
بعد از اجرای این دستور، فایلی مانند زیر ساخته میشود:
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class StoreProductRequest extends FormRequest { public function authorize(): bool { return true; } public function rules(): array { return [ 'title' => ['required', 'string', 'max:255'], 'price' => ['required', 'integer', 'min:0'], 'stock' => ['required', 'integer', 'min:0'], ]; } }
سپس در Controller بهجای استفاده مستقیم از Request، از همین کلاس استفاده میکنیم:
use App\Http\Requests\StoreProductRequest; class ProductController extends Controller { public function store(StoreProductRequest $request) { $data = $request->validated(); Product::create($data); return response()->json([ 'message' => 'محصول با موفقیت ثبت شد.' ]); } }
در این ساختار، قبل از اجرای متد store در Controller، Laravel بهصورت خودکار اعتبارسنجی را انجام میدهد. اگر دادهها معتبر نباشند، خطا برگردانده میشود و Controller اصلاً اجرا نمیشود.
طبق مستندات رسمی Laravel درباره Validation، Form Request یکی از روشهای اصلی Laravel برای جداسازی منطق اعتبارسنجی از Controller و مدیریت درخواستهای پیچیدهتر است.
چرا نباید همه Validationها را داخل Controller بنویسیم؟
در پروژههای کوچک، ممکن است نوشتن Validation داخل Controller ساده و سریع به نظر برسد:
public function store(Request $request) { $validated = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'email', 'unique:users,email'], 'password' => ['required', 'min:8'], ]); User::create($validated); }
این کد برای یک نمونه ساده مشکلی ندارد. اما وقتی پروژه رشد میکند، این روش ضعفهای جدی پیدا میکند.
Controller شلوغ و سختخوان میشود
Controller باید مسئول مدیریت جریان درخواست و پاسخ باشد، نه اینکه دهها خط قانون اعتبارسنجی، پیام خطا و شرطهای پیچیده در آن نوشته شود. وقتی در هر متد Controller تعداد زیادی Rule قرار بگیرد، خواندن و نگهداری آن سخت میشود.
قوانین تکراری میشوند
فرض کنید قوانین ثبت کاربر در چند بخش استفاده میشود:
- ثبتنام کاربر در سایت
- ایجاد کاربر توسط مدیر
- ثبت کاربر از API موبایل
- ورود اطلاعات مشتری از فایل Excel
- ثبت مشتری از طریق CRM
اگر Validation در چند Controller تکرار شود، تغییر یک قانون ساده مثل حداقل طول رمز عبور باید در چند محل انجام شود. این موضوع احتمال خطا را زیاد میکند.
تستنویسی دشوارتر میشود
وقتی Validation در کلاس مستقل قرار بگیرد، تست کردن سناریوهای معتبر و نامعتبر سادهتر است. همچنین Controllerها کوچکتر میشوند و Feature Testها خواناتر خواهند بود.
معماری پروژه ضعیفتر میشود
در پروژههای نرمافزار اختصاصی، هدف فقط کار کردن نسخه اول نیست. محصول باید قابل توسعه، قابل نگهداری و قابل تغییر باشد. Form Request در Laravel کمک میکند معماری پروژه تمیزتر و حرفهایتر باقی بماند.
جدول مقایسه Validation در Controller و Form Request
| معیار مقایسه | Validation داخل Controller | Form Request در Laravel |
|---|---|---|
| خوانایی Controller | در پروژههای بزرگ کاهش پیدا میکند | Controller تمیز و کوتاه میماند |
| قابلیت استفاده مجدد | محدود و دشوار | بهتر و ساختاریافتهتر |
| مدیریت پیام خطا | داخل Controller شلوغ میشود | در متد messages جدا میشود |
| مجوزدهی Request | معمولاً جداگانه پیادهسازی میشود | با متد authorize قابل مدیریت است |
| آمادهسازی داده قبل از Validation | معمولاً دستی و پراکنده | با prepareForValidation ساختاریافته است |
| تستپذیری | متوسط | بهتر |
| مناسب برای پروژه بزرگ | نه چندان مناسب | بسیار مناسب |
| نظم معماری | ضعیفتر | حرفهایتر |
ساختار اصلی Form Request در Laravel
یک Form Request حرفهای معمولاً فقط شامل rules نیست. بسته به نیاز پروژه، میتواند چند بخش مهم داشته باشد:
class StoreCustomerRequest extends FormRequest { public function authorize(): bool { return true; } protected function prepareForValidation(): void { $this->merge([ 'mobile' => normalize_mobile($this->mobile), ]); } public function rules(): array { return [ 'name' => ['required', 'string', 'max:100'], 'mobile' => ['required', 'string', 'size:11', 'unique:customers,mobile'], 'email' => ['nullable', 'email', 'max:255'], ]; } public function messages(): array { return [ 'name.required' => 'وارد کردن نام مشتری الزامی است.', 'mobile.required' => 'وارد کردن شماره موبایل الزامی است.', 'mobile.unique' => 'این شماره موبایل قبلاً ثبت شده است.', ]; } public function attributes(): array { return [ 'name' => 'نام مشتری', 'mobile' => 'شماره موبایل', 'email' => 'ایمیل', ]; } }
در ادامه هر بخش را دقیقتر بررسی میکنیم.
متد authorize در Form Request
متد authorize مشخص میکند آیا کاربر اجازه ارسال این درخواست را دارد یا نه.
public function authorize(): bool { return true; }
اگر مقدار false برگردد، Laravel اجازه ادامه درخواست را نمیدهد و پاسخ 403 برمیگرداند.
مثال کاربردی authorize
فرض کنید فقط مدیر بتواند محصول جدید ثبت کند:
public function authorize(): bool { return $this->user()?->is_admin === true; }
یا در پروژهای که از Policy استفاده میکند:
public function authorize(): bool { return $this->user()->can('create', Product::class); }
استفاده از authorize باعث میشود بخشی از کنترل دسترسی مرتبط با همان Request در محل مناسبی قرار بگیرد. البته باید دقت کرد که Form Request جایگزین کامل Policy، Gate یا Middleware نیست؛ بلکه میتواند در کنار آنها استفاده شود.
متد rules؛ قلب اصلی Form Request
متد rules مهمترین بخش Form Request است. تمام قوانین اعتبارسنجی دادهها در این متد تعریف میشوند.
public function rules(): array { return [ 'title' => ['required', 'string', 'max:255'], 'price' => ['required', 'numeric', 'min:0'], 'category_id' => ['required', 'exists:categories,id'], 'status' => ['required', 'in:draft,published'], ]; }
Laravel تعداد زیادی Rule آماده دارد؛ از جمله:
- required
- nullable
- string
- integer
- numeric
- url
- date
- min
- max
- exists
- unique
- confirmed
- array
- file
- image
- mimes
- boolean
فهرست کامل قوانین در مستندات رسمی Laravel درباره Available Validation Rules قابل مشاهده است.
استفاده از validated و safe برای دریافت دادههای معتبر
یکی از اشتباهات رایج در Laravel این است که بعد از Validation همچنان از کل ورودی استفاده شود:
Product::create($request->all());
این روش خطرناک است، چون ممکن است دادههایی خارج از فیلدهای مجاز نیز در Request وجود داشته باشد.
روش بهتر استفاده از validated است:
$data = $request->validated(); Product::create($data);
یا استفاده از safe:
$data = $request->safe()->only([ 'title', 'price', 'category_id', ]);
در مستندات رسمی Laravel درباره کار با دادههای اعتبارسنجیشده، Laravel توضیح میدهد که میتوان فقط دادههای تأییدشده را با validated یا safe دریافت کرد. این کار برای امنیت، جلوگیری از Mass Assignment ناخواسته و کنترل بهتر ورودیها اهمیت زیادی دارد.
prepareForValidation؛ آمادهسازی دادهها قبل از اعتبارسنجی
گاهی دادهای که کاربر ارسال میکند قبل از Validation باید تمیز، نرمال یا تبدیل شود. اینجاست که prepareForValidation کاربرد دارد.
مثال: تبدیل اسلاگ
protected function prepareForValidation(): void { $this->merge([ 'slug' => str($this->title)->slug()->toString(), ]); }
مثال: نرمالسازی شماره موبایل
protected function prepareForValidation(): void { $mobile = preg_replace('/\D/', '', $this->mobile); if (str_starts_with($mobile, '98')) { $mobile = '0' . substr($mobile, 2); } $this->merge([ 'mobile' => $mobile, ]); }
مثال: تبدیل مقدار checkbox به boolean
protected function prepareForValidation(): void { $this->merge([ 'is_active' => filter_var($this->is_active, FILTER_VALIDATE_BOOLEAN), ]); }
استفاده درست از prepareForValidation باعث میشود دادهها قبل از ورود به مرحله Validation شکل استانداردتری داشته باشند. این موضوع در نرمافزارهای سازمانی که داده از منابع مختلف وارد میشود بسیار مهم است.
messages؛ سفارشیسازی پیامهای خطا
پیامهای خطای پیشفرض Laravel برای پروژههای فارسی معمولاً کافی نیستند. با متد messages میتوان پیامهای دقیقتر و قابل فهمتری برای کاربر تعریف کرد.
public function messages(): array { return [ 'title.required' => 'عنوان محصول الزامی است.', 'title.max' => 'عنوان محصول نباید بیشتر از ۲۵۵ کاراکتر باشد.', 'price.required' => 'قیمت محصول الزامی است.', 'price.min' => 'قیمت محصول نمیتواند منفی باشد.', ]; }
پیام خطای خوب فقط یک متن تزئینی نیست. در تجربه کاربری، پیام خطا باید دقیقاً به کاربر بگوید چه چیزی اشتباه است و چگونه میتواند آن را اصلاح کند.
برای مثال، این پیام ضعیف است:
مقدار وارد شده نامعتبر است.
اما این پیام بهتر است:
شماره موبایل باید ۱۱ رقم باشد و با ۰۹ شروع شود.
attributes؛ نمایش نام فارسی فیلدها
با متد attributes میتوان نام فیلدها را برای نمایش در پیامهای خطا خواناتر کرد.
public function attributes(): array { return [ 'first_name' => 'نام', 'last_name' => 'نام خانوادگی', 'mobile' => 'شماره موبایل', 'national_code' => 'کد ملی', ]; }
این متد مخصوصاً در پروژههای فارسیزبان بسیار کاربردی است؛ چون بهجای نمایش نام فیلد انگلیسی مثل national_code، کاربر عبارت «کد ملی» را میبیند.
اعتبارسنجی در سناریوی ثبت کاربر
فرض کنید در یک نرمافزار تحت وب، کاربر باید ثبتنام کند. قوانین ممکن است شامل نام، ایمیل، موبایل و رمز عبور باشد.
php artisan make:request RegisterUserRequest
کلاس Request:
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rules\Password; class RegisterUserRequest extends FormRequest { public function authorize(): bool { return true; } protected function prepareForValidation(): void { $this->merge([ 'email' => strtolower(trim($this->email)), 'mobile' => preg_replace('/\D/', '', $this->mobile), ]); } public function rules(): array { return [ 'name' => ['required', 'string', 'min:2', 'max:100'], 'email' => ['required', 'email', 'max:255', 'unique:users,email'], 'mobile' => ['required', 'string', 'size:11', 'unique:users,mobile'], 'password' => ['required', 'confirmed', Password::min(8)->letters()->numbers()], ]; } public function messages(): array { return [ 'email.unique' => 'این ایمیل قبلاً ثبت شده است.', 'mobile.unique' => 'این شماره موبایل قبلاً ثبت شده است.', 'password.confirmed' => 'تکرار رمز عبور با رمز عبور یکسان نیست.', ]; } public function attributes(): array { return [ 'name' => 'نام', 'email' => 'ایمیل', 'mobile' => 'شماره موبایل', 'password' => 'رمز عبور', ]; } }
استفاده در Controller:
class AuthController extends Controller { public function register(RegisterUserRequest $request) { $data = $request->validated(); $user = User::create([ ...$data, 'password' => bcrypt($data['password']), ]); return response()->json([ 'message' => 'ثبتنام با موفقیت انجام شد.', 'user' => $user, ], 201); } }
در این مثال، Controller فقط مسئول اجرای فرآیند ثبتنام است، نه مدیریت قوانین ورودی.
اعتبارسنجی حرفهای در سناریوی ثبت محصول
در یک پنل مدیریت فروشگاه اینترنتی، ثبت محصول میتواند شامل موارد زیر باشد:
- عنوان
- اسلاگ
- قیمت
- موجودی
- دستهبندی
- تصویر
- وضعیت انتشار
- توضیحات
نمونه Form Request:
use Illuminate\Validation\Rule; class StoreProductRequest extends FormRequest { public function authorize(): bool { return $this->user()?->can('create', Product::class); } protected function prepareForValidation(): void { $this->merge([ 'slug' => str($this->slug ?: $this->title)->slug()->toString(), ]); } public function rules(): array { return [ 'title' => ['required', 'string', 'max:255'], 'slug' => ['required', 'string', 'max:255', 'unique:products,slug'], 'price' => ['required', 'integer', 'min:0'], 'stock' => ['required', 'integer', 'min:0'], 'category_id' => ['required', 'exists:categories,id'], 'status' => ['required', Rule::in(['draft', 'published'])], 'image' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'], 'description' => ['nullable', 'string'], ]; } }
در این نمونه چند نکته مهم وجود دارد:
- دسترسی با authorize بررسی شده است.
- اسلاگ قبل از Validation آماده شده است.
- قیمت و موجودی نمیتوانند منفی باشند.
- دستهبندی باید واقعاً در جدول categories وجود داشته باشد.
- تصویر فقط با فرمتهای مشخص و حجم محدود پذیرفته میشود.
برای کسبوکارهای فروشگاهی، چنین Validationهایی فقط کدنویسی تمیز نیستند؛ بلکه مستقیماً روی کیفیت داده، مدیریت محصول، تجربه کاربر و کاهش خطاهای عملیاتی اثر دارند.
اعتبارسنجی هنگام ویرایش دادهها
یکی از سناریوهای رایج، اعتبارسنجی فیلدهای Unique هنگام ویرایش است. مثلاً ایمیل کاربر باید یکتا باشد، اما هنگام ویرایش، ایمیل فعلی همان کاربر نباید باعث خطا شود.
نمونه اشتباه:
'email' => ['required', 'email', 'unique:users,email']
این قانون در زمان ویرایش ممکن است حتی برای ایمیل فعلی خود کاربر هم خطا بدهد.
روش بهتر:
use Illuminate\Validation\Rule; public function rules(): array { $user = $this->route('user'); return [ 'name' => ['required', 'string', 'max:100'], 'email' => [ 'required', 'email', Rule::unique('users', 'email')->ignore($user->id), ], ]; }
Laravel در مستندات رسمی Validation درباره Rule Unique روش استفاده از Rule::unique و ignore را توضیح داده است. این روش از نظر خوانایی و امنیت بهتر از ساخت رشتههای پیچیده Validation است.
اعتبارسنجی آرایهها و دادههای Nested
در پروژههای واقعی، بسیاری از فرمها ساده نیستند. مثلاً ثبت سفارش شامل چند آیتم است:
{ "items": [ { "product_id": 1, "quantity": 2 }, { "product_id": 5, "quantity": 1 } ] }
برای اعتبارسنجی چنین ساختاری میتوان نوشت:
public function rules(): array { return [ 'items' => ['required', 'array', 'min:1'], 'items.*.product_id' => ['required', 'integer', 'exists:products,id'], 'items.*.quantity' => ['required', 'integer', 'min:1'], ]; }
این نوع Validation برای فروشگاهها، سیستمهای سفارشگیری، فرمسازها، سامانههای منابع انسانی، CRMها و نرمافزارهای ERP بسیار کاربردی است.
مثال واقعی: ثبت فاکتور
در یک نرمافزار حسابداری تحت وب، فاکتور ممکن است چند ردیف داشته باشد:
public function rules(): array { return [ 'customer_id' => ['required', 'exists:customers,id'], 'invoice_date' => ['required', 'date'], 'items' => ['required', 'array', 'min:1'], 'items.*.title' => ['required', 'string', 'max:255'], 'items.*.quantity' => ['required', 'numeric', 'min:0.01'], 'items.*.unit_price' => ['required', 'integer', 'min:0'], 'items.*.discount' => ['nullable', 'integer', 'min:0'], ]; }
در چنین سناریوهایی، Form Request باعث میشود منطق اعتبارسنجی پیچیده از Controller جدا بماند.
اعتبارسنجی شرطی در Form Request
گاهی قوانین Validation به مقدار فیلدهای دیگر وابسته هستند. مثلاً اگر نوع مشتری حقوقی باشد، شناسه ملی شرکت الزامی است.
public function rules(): array { return [ 'customer_type' => ['required', 'in:individual,company'], 'national_code' => ['required_if:customer_type,individual', 'nullable', 'string', 'size:10'], 'company_national_id' => ['required_if:customer_type,company', 'nullable', 'string', 'size:11'], ]; }
یا اگر روش ارسال «پیک» باشد، آدرس باید الزامی شود:
public function rules(): array { return [ 'delivery_method' => ['required', 'in:pickup,courier'], 'address' => ['required_if:delivery_method,courier', 'nullable', 'string', 'max:500'], ]; }
اعتبارسنجی شرطی برای کسبوکارها بسیار مهم است، چون قوانین فرمها معمولاً به سناریوی کاربر وابستهاند.
after Validation؛ اعتبارسنجیهای تکمیلی
گاهی Ruleهای ساده کافی نیستند و باید بعد از Validation بررسیهای بیشتری انجام شود. برای این کار میتوان از متد after استفاده کرد.
مثلاً در یک سیستم رزرو، کاربر نباید بتواند زمانی را انتخاب کند که قبلاً رزرو شده است:
use Illuminate\Validation\Validator; public function after(): array { return [ function (Validator $validator) { $exists = Reservation::where('date', $this->date) ->where('time', $this->time) ->exists(); if ($exists) { $validator->errors()->add( 'time', 'این زمان قبلاً رزرو شده است.' ); } } ]; }
این روش برای سناریوهایی مفید است که اعتبارسنجی فقط با Ruleهای ساده قابل انجام نیست.
Form Request در APIهای Laravel
در APIها، Form Request اهمیت بیشتری دارد؛ چون دادهها ممکن است از اپلیکیشن موبایل، فرانتاند React یا Vue، سرویس خارجی یا حتی کلاینتهای ناشناس وارد سیستم شوند.
در API، وقتی Validation شکست بخورد، Laravel معمولاً پاسخ JSON شامل خطاها برمیگرداند. نمونه Controller:
class ApiOrderController extends Controller { public function store(StoreOrderRequest $request, OrderService $service) { $order = $service->create( $request->user(), $request->validated() ); return response()->json([ 'message' => 'سفارش با موفقیت ثبت شد.', 'data' => $order, ], 201); } }
در این ساختار، API Controller کوتاه، قابل فهم و قابل نگهداری میماند. این دقیقاً همان چیزی است که در تولید نرمافزارهای تحت وب مقیاسپذیر اهمیت دارد.
Form Request و امنیت نرمافزار
اعتبارسنجی فقط برای نمایش پیام خطا نیست؛ یکی از لایههای مهم امنیتی نرمافزار است. البته Validation جایگزین کامل امنیت نیست، اما نقش بسیار مهمی در کاهش ریسک دارد.
جلوگیری از دادههای نامعتبر
با Validation میتوان جلوی ورود دادههایی را گرفت که با ساختار سیستم سازگار نیستند؛ مثل قیمت منفی، تاریخ نامعتبر، ایمیل اشتباه یا شناسه ناموجود.
کاهش ریسک Mass Assignment
اگر بهجای all() از validated() استفاده شود، فقط فیلدهای مجاز وارد فرآیند ذخیرهسازی میشوند. این موضوع در برابر ارسال فیلدهای ناخواسته مثل is_admin بسیار مهم است.
کنترل فایلهای آپلودی
در فرمهای آپلود فایل، Validation باید نوع، حجم و فرمت فایل را کنترل کند:
'image' => ['required', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048']
کنترل دادههای وابسته به دیتابیس
با Ruleهایی مثل exists و unique میتوان از ثبت دادههای ناسازگار جلوگیری کرد:
'category_id' => ['required', 'exists:categories,id']
در پروژههایی که اسمارتی اپ (SmartyApp) برای کسبوکارها توسعه میدهد، اعتبارسنجی ورودیها بخشی از طراحی امن و قابل اعتماد نرمافزار است، نه یک مرحله فرعی و کماهمیت.
مثال واقعی برای کسبوکارها: سامانه رزرو آنلاین
فرض کنید یک کلینیک، سالن زیبایی، مرکز آموزشی یا شرکت گردشگری، سامانه رزرو آنلاین دارد. فرم رزرو ممکن است شامل اطلاعات زیر باشد:
- شناسه خدمت
- تاریخ رزرو
- ساعت رزرو
- نام مشتری
- شماره موبایل
- توضیحات
- کد تخفیف
Form Request پیشنهادی:
class StoreReservationRequest extends FormRequest { public function authorize(): bool { return true; } protected function prepareForValidation(): void { $this->merge([ 'mobile' => preg_replace('/\D/', '', $this->mobile), ]); } public function rules(): array { return [ 'service_id' => ['required', 'exists:services,id'], 'date' => ['required', 'date', 'after_or_equal:today'], 'time' => ['required', 'date_format:H:i'], 'customer_name' => ['required', 'string', 'max:100'], 'mobile' => ['required', 'string', 'size:11'], 'coupon_code' => ['nullable', 'string', 'max:50'], 'description' => ['nullable', 'string', 'max:1000'], ]; } public function messages(): array { return [ 'date.after_or_equal' => 'تاریخ رزرو نمیتواند قبل از امروز باشد.', 'time.date_format' => 'فرمت ساعت رزرو معتبر نیست.', ]; } }
در این سناریو، Validation باعث میشود کاربر نتواند تاریخ گذشته ثبت کند، شماره موبایل نامعتبر وارد کند یا خدمت ناموجود انتخاب کند. این موضوع مستقیماً روی تجربه مشتری و کاهش خطاهای عملیاتی تأثیر میگذارد.
مثال واقعی برای کسبوکارها: CRM و ثبت مشتری
در یک CRM، کیفیت دادهها بسیار مهم است. اگر شماره موبایل، ایمیل، کد ملی یا نوع مشتری بهدرستی اعتبارسنجی نشود، بعداً در گزارشگیری، ارسال پیامک، بخش فروش و پیگیری مشتری مشکل ایجاد میشود.
class StoreLeadRequest extends FormRequest { public function rules(): array { return [ 'full_name' => ['required', 'string', 'max:150'], 'mobile' => ['required', 'string', 'size:11', 'unique:leads,mobile'], 'email' => ['nullable', 'email', 'max:255'], 'source' => ['required', 'in:website,instagram,phone,referral,ads'], 'status' => ['required', 'in:new,contacted,qualified,lost'], 'notes' => ['nullable', 'string', 'max:2000'], ]; } }
یک CRM حرفهای فقط فرم ورود اطلاعات نیست؛ کیفیت داده در آن نقش مستقیم در فروش، بازاریابی و تصمیمگیری مدیریتی دارد. Form Request کمک میکند این کیفیت از همان ورودی سیستم کنترل شود.
مثال واقعی برای کسبوکارها: نرمافزار مالی و ثبت تراکنش
در نرمافزارهای مالی، اعتبارسنجی باید سختگیرانهتر باشد. مثلاً مبلغ تراکنش، نوع تراکنش، تاریخ، حساب مبدا و مقصد باید دقیق کنترل شود.
class StoreTransactionRequest extends FormRequest { public function authorize(): bool { return $this->user()?->can('create-transaction'); } public function rules(): array { return [ 'account_id' => ['required', 'exists:accounts,id'], 'type' => ['required', 'in:income,expense,transfer'], 'amount' => ['required', 'integer', 'min:1000'], 'transaction_date' => ['required', 'date'], 'description' => ['nullable', 'string', 'max:1000'], ]; } }
در چنین پروژههایی، کوچکترین داده نامعتبر میتواند باعث اختلاف حساب یا گزارش اشتباه شود. بنابراین Validation فقط یک کار فنی نیست؛ بخشی از اعتمادپذیری نرمافزار است.
مزایای Form Request در Laravel
1. تمیز شدن Controllerها
با انتقال قوانین Validation به Form Request، Controller کوتاهتر و خواناتر میشود.
2. جداسازی مسئولیتها
Controller مسئول Request و Response است؛ Form Request مسئول اعتبارسنجی و آمادهسازی داده. این جداسازی باعث معماری بهتر میشود.
3. افزایش امنیت ورودیها
با استفاده از validated و safe فقط دادههای مجاز وارد فرآیند ذخیرهسازی میشوند.
4. مدیریت بهتر پیامهای خطا
پیامهای خطا در یک کلاس مشخص مدیریت میشوند و میتوان آنها را برای کاربران فارسیزبان دقیق و قابل فهم نوشت.
5. قابلیت استفاده مجدد
در برخی سناریوها میتوان قوانین مشترک را بین چند Request استخراج یا به کلاسهای Rule جدا منتقل کرد.
6. مناسب برای پروژههای تیمی
وقتی ساختار مشخص باشد، اعضای تیم راحتتر میدانند Validation باید کجا نوشته شود.
7. تستپذیری بهتر
Form Requestها باعث میشوند تست سناریوهای ورودی معتبر و نامعتبر سادهتر شود.
8. هماهنگی بهتر با Service Layer
در معماری تمیز، Form Request داده معتبر تولید میکند و Service Layer منطق کسبوکار را اجرا میکند. این ترکیب برای پروژههای جدی Laravel بسیار کاربردی است.
چالشهای Form Request در Laravel
1. ساخت Requestهای زیاد
در پروژههای بزرگ ممکن است تعداد Form Requestها زیاد شود. این موضوع اگر ساختار پوشهبندی و نامگذاری مناسب نباشد، باعث شلوغی میشود.
2. قرار دادن منطق کسبوکار در Form Request
Form Request محل اجرای منطق اصلی کسبوکار نیست. مثلاً ثبت سفارش، محاسبه تخفیف یا صدور فاکتور نباید داخل Form Request انجام شود. Form Request فقط باید ورودی را آماده و اعتبارسنجی کند.
3. استفاده نادرست از authorize
متد authorize برای بررسی مجوز همان درخواست مناسب است، اما نباید جایگزین کامل ساختارهای دسترسی مثل Policy و Gate شود.
4. پیچیده شدن Validationهای شرطی
در فرمهای بسیار پیچیده، قوانین شرطی ممکن است طولانی شوند. در این شرایط بهتر است از Rule Object، متدهای کمکی یا حتی چند Request جدا برای سناریوهای مختلف استفاده شود.
5. تکرار قوانین مشترک
گاهی قوانین بین چند Request تکرار میشوند. میتوان برای جلوگیری از تکرار، Ruleهای سفارشی یا کلاسهای کمکی ساخت، اما نباید پروژه را بیدلیل پیچیده کرد.
بهترین روشها برای استفاده حرفهای از Form Request
1. برای هر عملیات مهم Request جدا بسازید
برای ثبت و ویرایش معمولاً بهتر است Requestهای جدا داشته باشید:
StoreProductRequest UpdateProductRequest
چون قوانین ثبت و ویرایش همیشه یکسان نیستند.
2. از نامگذاری دقیق استفاده کنید
نام Request باید مشخص کند برای چه کاری استفاده میشود:
StoreOrderRequest UpdateCustomerRequest VerifyPaymentRequest CancelReservationRequest
از نامهای کلی مثل ProductRequest یا MainRequest بهتر است کمتر استفاده شود.
3. دادههای خام را ذخیره نکنید
بهجای:
$model->create($request->all());
از این روش استفاده کنید:
$model->create($request->validated());
4. پیام خطای قابل فهم بنویسید
پیام خطا باید به زبان کاربر و متناسب با فرم نوشته شود.
5. Validation را با دیتابیس هماهنگ کنید
اگر فیلدی در دیتابیس nullable نیست، در Validation هم نباید بیدلیل nullable باشد. هماهنگی بین Migration، Model و Form Request اهمیت زیادی دارد.
6. از Rule Class برای قوانین پیچیده استفاده کنید
اگر قانونی پیچیده یا تکراری است، آن را در Custom Rule قرار دهید.
php artisan make:rule ValidNationalCode
نمونه استفاده:
'national_code' => ['required', new ValidNationalCode],
7. Form Request را با Service Layer ترکیب کنید
ساختار حرفهایتر:
public function store(StoreOrderRequest $request, OrderService $service) { $order = $service->create($request->validated()); return response()->json([ 'message' => 'سفارش ثبت شد.', 'data' => $order, ]); }
در این مدل، Form Request داده را معتبر میکند و Service مسئول اجرای منطق سفارش است.
8. از prepareForValidation با احتیاط استفاده کنید
این متد برای نرمالسازی داده عالی است، اما نباید منطق سنگین یا Queryهای زیاد داخل آن قرار گیرد.
9. فیلدهای Nested را دقیق اعتبارسنجی کنید
اگر آرایه میپذیرید، فقط بررسی array کافی نیست. باید آیتمهای داخلی هم اعتبارسنجی شوند:
'items' => ['required', 'array'], 'items.*.product_id' => ['required', 'exists:products,id'], 'items.*.quantity' => ['required', 'integer', 'min:1'],
10. اعتبارسنجی را بخشی از تجربه کاربری بدانید
Validation خوب فقط جلوی خطا را نمیگیرد؛ به کاربر کمک میکند فرم را سریعتر و درستتر تکمیل کند.
ارتباط Form Request با معماری حرفهای نرمافزار
Form Request یک ابزار ساده به نظر میرسد، اما در معماری پروژه نقش مهمی دارد. در یک پروژه Laravel حرفهای، معمولاً لایهها به این شکل همکاری میکنند:
Request → Form Request → Controller → Service Layer → Model/Repository → Database
در این معماری:
- Form Request ورودی را کنترل میکند.
- Controller جریان درخواست را مدیریت میکند.
- Service Layer منطق کسبوکار را اجرا میکند.
- Model یا Repository با دادهها کار میکند.
- Response به کاربر برگردانده میشود.
این ساختار باعث میشود پروژه قابل رشد باشد. وقتی کسبوکار نیاز جدیدی مطرح میکند، تیم توسعه مجبور نیست در Controllerهای شلوغ دنبال قوانین پنهان بگردد.
اسمارتی اپ (SmartyApp) در پروژههای طراحی و تولید نرمافزار تحت وب، استفاده درست از ابزارهایی مثل Form Request را بخشی از کیفیت فنی پروژه میداند؛ چون نرمافزار اختصاصی باید هم امروز نیاز کسبوکار را پاسخ دهد و هم فردا قابل توسعه باشد.
Form Request در پروژههای Inertia، Vue و React
اگر پروژه Laravel شما با Inertia، Vue، React یا API مستقل توسعه داده شده باشد، Form Request همچنان کاربرد دارد. تفاوتی ندارد ورودی از فرم Blade بیاید یا از کامپوننت React. در نهایت، داده وارد Backend میشود و باید اعتبارسنجی شود.
در پروژههای SPA یا Hybrid، اعتبارسنجی سمت Frontend برای تجربه کاربری مفید است، اما کافی نیست. کاربر میتواند درخواست را مستقیم با ابزارهایی مثل Postman یا Curl ارسال کند. بنابراین Validation اصلی باید سمت Backend انجام شود.
Form Request در اینجا نقش دروازه امنیتی و ساختاری Backend را ایفا میکند.
Form Request و فایلهای آپلودی
آپلود فایل یکی از حساسترین بخشهای اعتبارسنجی است. فایلها باید از نظر نوع، حجم و گاهی ابعاد کنترل شوند.
نمونه اعتبارسنجی تصویر پروفایل:
public function rules(): array { return [ 'avatar' => ['required', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'], ]; }
نمونه اعتبارسنجی فایل PDF:
public function rules(): array { return [ 'resume' => ['required', 'file', 'mimes:pdf', 'max:5120'], ]; }
در پروژههای سازمانی، آپلود فایل بدون Validation میتواند باعث مصرف بیرویه فضای ذخیرهسازی، اختلال امنیتی یا ورود فایلهای نامعتبر به سیستم شود.
Form Request و Custom Rule
گاهی Ruleهای آماده Laravel کافی نیستند. مثلاً اعتبارسنجی کد ملی، شناسه ملی، شماره شبا یا فرمت اختصاصی یک کد سازمانی نیاز به Rule سفارشی دارد.
ساخت Rule:
php artisan make:rule ValidNationalCode
نمونه ساده:
use Closure; use Illuminate\Contracts\Validation\ValidationRule; class ValidNationalCode implements ValidationRule { public function validate(string $attribute, mixed $value, Closure $fail): void { if (! preg_match('/^\d{10}$/', $value)) { $fail('کد ملی باید ۱۰ رقم باشد.'); } } }
استفاده در Form Request:
public function rules(): array { return [ 'national_code' => ['required', new ValidNationalCode], ]; }
Custom Ruleها برای پروژههایی که قوانین بومی یا اختصاصی دارند بسیار ارزشمند هستند.
اشتباهات رایج در Form Request
1. استفاده از all بهجای validated
این یکی از خطرناکترین اشتباهات است. همیشه دادههای معتبر را استفاده کنید.
2. قرار دادن Queryهای سنگین در rules
گاهی توسعهدهندهها Queryهای پیچیده را مستقیم داخل Validation قرار میدهند. اگر منطق سنگین است، بهتر است با Rule Object یا Service جدا مدیریت شود.
3. نوشتن پیامهای خطای مبهم
پیام خطا باید عملی و قابل فهم باشد.
4. یکی کردن Request ثبت و ویرایش در همه شرایط
گاهی قوانین ثبت و ویرایش تفاوت دارند. جدا نکردن آنها باعث شرطهای زیاد و کد سختخوان میشود.
5. فراموش کردن authorize
اگر Request نیاز به کنترل دسترسی دارد، authorize نباید همیشه بدون فکر true باشد.
6. اعتبارسنجی نکردن آرایههای داخلی
فقط اینکه بگوییم items آرایه است کافی نیست. باید تکتک آیتمهای داخلی هم کنترل شوند.
چه زمانی استفاده از Form Request ضروریتر است؟
استفاده از Form Request در این شرایط بسیار توصیه میشود:
- فرم پیچیده دارید.
- پروژه API دارد.
- چند توسعهدهنده روی پروژه کار میکنند.
- قوانین Validation زیاد یا شرطی هستند.
- پیامهای خطای اختصاصی نیاز دارید.
- پروژه بلندمدت و قابل توسعه است.
- دادهها از منابع مختلف وارد سیستم میشوند.
- نرمافزار مالی، فروشگاهی، CRM، رزرو، آموزشی یا سازمانی است.
- نیاز به امنیت و کیفیت داده بالا دارید.
برای فرمهای بسیار ساده، استفاده مستقیم از validate در Controller همچنان ممکن است قابل قبول باشد. اما در پروژههای حرفهای، Form Request انتخاب تمیزتر و قابل نگهداریتری است.
FAQ: سوالات متداول درباره Form Request در Laravel
1. Form Request در Laravel چیست؟
Form Request یک کلاس اختصاصی برای اعتبارسنجی و مجوزدهی درخواستهای ورودی در Laravel است. این کلاس کمک میکند قوانین Validation از Controller جدا شوند و کد تمیزتر بماند.
2. تفاوت Form Request با validate داخل Controller چیست؟
در روش Controller، قوانین Validation داخل همان متد نوشته میشوند. اما در Form Request، قوانین در یک کلاس جدا قرار میگیرند. این کار خوانایی، نگهداری و تستپذیری را بهتر میکند.
3. آیا استفاده از Form Request همیشه لازم است؟
خیر. برای فرمهای خیلی ساده میتوان از validate داخل Controller استفاده کرد. اما برای پروژههای متوسط و بزرگ، Form Request گزینه حرفهایتری است.
4. متد authorize در Form Request چه کاربردی دارد؟
متد authorize مشخص میکند آیا کاربر اجازه ارسال آن Request را دارد یا نه. اگر مقدار false برگرداند، Laravel پاسخ 403 میدهد.
5. متد rules چه کاری انجام میدهد؟
متد rules قوانین اعتبارسنجی فیلدهای ورودی را تعریف میکند؛ مثل required، email، unique، exists، min، max و سایر Ruleها.
6. تفاوت validated و safe چیست؟
validated آرایهای از دادههای اعتبارسنجیشده را برمیگرداند. safe یک آبجکت امنتر برای انتخاب، حذف یا ترکیب دادههای معتبر ارائه میدهد.
7. آیا Form Request برای API مناسب است؟
بله. Form Request در APIها بسیار کاربردی است، چون خطاهای Validation را ساختاریافته برمیگرداند و Controllerها را تمیز نگه میدارد.
8. آیا میتوان پیامهای خطا را فارسی کرد؟
بله. با متد messages میتوان پیامهای خطای اختصاصی فارسی تعریف کرد. همچنین با متد attributes میتوان نام فیلدها را فارسی نمایش داد.
9. prepareForValidation چه زمانی استفاده میشود؟
وقتی لازم است داده قبل از اعتبارسنجی آماده، تمیز یا تبدیل شود؛ مثلاً تبدیل ایمیل به حروف کوچک، نرمالسازی موبایل یا ساخت اسلاگ.
10. آیا Form Request جایگزین Service Layer است؟
خیر. Form Request مسئول اعتبارسنجی ورودی است، اما Service Layer مسئول منطق کسبوکار است. این دو مکمل هم هستند.
11. آیا میتوان در Form Request از Ruleهای سفارشی استفاده کرد؟
بله. Laravel امکان ساخت Custom Rule را فراهم میکند و میتوان آن را در متد rules استفاده کرد.
12. آیا بهتر است برای Store و Update دو Form Request جدا داشته باشیم؟
در بیشتر پروژههای حرفهای، بله. چون قوانین ثبت و ویرایش معمولاً تفاوتهایی دارند، مخصوصاً در فیلدهای Unique.
جمعبندی: Form Request در Laravel پایه اعتبارسنجی حرفهای دادههاست
Form Request در Laravel یکی از ابزارهای کلیدی برای ساخت نرمافزارهای تمیز، امن، قابل توسعه و قابل نگهداری است. این ابزار کمک میکند قوانین اعتبارسنجی از Controller جدا شوند، پیامهای خطا ساختاریافتهتر باشند، دادههای ورودی قبل از پردازش کنترل شوند و فقط دادههای معتبر وارد لایههای بعدی برنامه شوند.
در پروژههای ساده، شاید Validation داخل Controller کافی باشد. اما در نرمافزارهای حرفهای، مخصوصاً سامانههای فروشگاهی، CRM، رزرو، مالی، آموزشی، SaaS و نرمافزارهای سازمانی، استفاده از Form Request در Laravel یک انتخاب هوشمندانه است.
این الگو وقتی در کنار Service Layer، Policy، Custom Rule، DTO و تستنویسی استفاده شود، میتواند کیفیت فنی پروژه را به شکل محسوسی افزایش دهد. برای کسبوکارهایی که میخواهند نرمافزار اختصاصی قابل توسعه داشته باشند، چنین جزئیاتی در معماری اهمیت زیادی دارد.
اسمارتی اپ (SmartyApp) در طراحی سایت، تولید نرمافزار اختصاصی و برنامهنویسی نرمافزارهای تحت وب، اعتبارسنجی دادهها و طراحی معماری تمیز را بخشی از مسیر ساخت محصول پایدار میداند؛ چون نرمافزار موفق فقط ظاهر خوب ندارد، بلکه باید داده سالم، کد قابل نگهداری و ساختار قابل رشد داشته باشد.
CTA: برای طراحی نرمافزار تحت وب امن و قابل توسعه مشاوره بگیرید
اگر قصد دارید یک نرمافزار اختصاصی، سامانه تحت وب، پنل مدیریتی، فروشگاه اینترنتی، CRM، سیستم رزرو، پلتفرم آموزشی یا محصول SaaS با Laravel طراحی و توسعه دهید، بهتر است از همان ابتدا به معماری، امنیت دادهها و اعتبارسنجی حرفهای ورودیها توجه کنید.
یک Form Request خوب شاید در ظاهر فقط چند خط Validation باشد، اما در عمل میتواند از خطاهای عملیاتی، دادههای ناسالم، باگهای آینده و هزینههای نگهداری جلوگیری کند.
برای بررسی نیازهای فنی پروژه، طراحی معماری مناسب و دریافت مشاوره در زمینه تولید نرمافزارهای تحت وب، میتوانید با تیم اسمارتی اپ (SmartyApp) تماس بگیرید و مسیر توسعه محصول خود را دقیقتر برنامهریزی کنید.
منابع رسمی
- مستندات رسمی Laravel درباره Validation
- مستندات رسمی Laravel درباره Form Request Validation
- مستندات رسمی Laravel درباره کار با دادههای اعتبارسنجیشده
- مستندات رسمی Laravel درباره آمادهسازی داده قبل از Validation
- مستندات رسمی Laravel درباره Rule Unique
- مستندات رسمی Laravel درباره Custom Validation Rules
- Laravel API Documentation برای کلاس FormRequest