سه روش اعتبارسنجی فرم‌ها در لاراول

اعتبارسنجی فرم یعنی بررسی داده‌هایی که کاربر در فیلدهای فرم نوشته است. مثلن ما می‌خواهیم کاربر فیلد ایمیل را حتمن بنویسد، در فیلد شماره تلفن حتمن یازده عدد بنویسد، یک فیلد را حتمن با حروف فارسی بنویسد و...

در لاراول به روش‌های مختلف می‌توان اعتبارسنجی فرم‌ها را انجام داد. همان طور که در مستندات این فریم‌ورک گفته شده است، سه روش متداول برای این کار عبارتند از:

  • 1- متد validate برای ریکوئست
  • 2- استفاده از facade
  • 3- فرم ریکوئست
انواع روش های اعتبارسنجی فرم ها در لاراول
انواع روش های اعتبارسنجی فرم ها در لاراول


در این نوشته، طبق مستندات لاراول یک نگاه سریع به این سه روش می اندازم و مواردی را که برای خودم گنگ بودند و حالا روشن شدند، توضیح می دهم. برای نوشتن این متن از این نوشته هم کمک گرفتم. من این کدها را در Visual Studio نوشتم و برای نوشتن راحت‌تر کدهای html از افزونه ی Emmet Abbreviation استفاده کردم.

شروع کار

بیایید با اجرای دستور زیر در ترمینال، یک پروژه ی لاراولی به نام posts بسازیم (اگر از قبل پیش نیازها را آماده کرده باشیم):

laravel new posts

حالا در فایل routes/web.php دو روت ساده قرار دهیم:

use App\Http\Controllers\PostController;

Route::get('/post/create', [PostController::class, 'create']);
Route::post('/post', [PostController::class, 'store']);

در کد بالا گفته ایم که اگر کاربر آدرس /post/create را در مرورگر وارد کرد، متد create از کلاس کنترلر PostController اجرا شود (در این جا قرار است فرم ساخت یک پست جدید را به کاربر نمایش دهیم) و اگر به اندپوینت /post چیزی پست شد، متد store از همین کلاس اجرا شود (وقتی کاربر روی دکمه ی submit کلیک کرد، درخواست به این اندپوینت پست می شود و این متد اجرا می شود. این جا همان جایی است که ما می‌خواهیم اعتبارسنجی فرم خود را در بک‌اند انجام دهیم).

حالا کنترلر خود را هم می سازیم:

php artisan make:controller -r PostController

در دستور بالا، سوئیچ r را هم در پایان دستور خود قرار داده ایم تا کنترلر ساخته شده، همراه با توابع پیش فرض لازم برای ریسورس ها باشد (توابعی مانند create و store).

در متد create کنترلر پست این کد را می نویسیم تا صفحه ی مناسب نمایش داده شود:

public function create()
{
    return view('post.create');
}

در پوشه ی resources/views یک پوشه به نام post می سازیم و در پوشه ی جدید یک فایل به نام create.blade.php. در این فایل یک فرم ساده قرار می دهیم (با emmet نوشتن چنین کدی با شورتکدهای html:5، form و input:submit، input:text و در نهایت textarea قابل انجام است):

<!DOCTYPE html>
<html lang=&quoten&quot>
<head>
    <meta charset=&quotUTF-8&quot>
    <meta http-equiv=&quotX-UA-Compatible&quot content=&quotIE=edge&quot>
    <meta name=&quotviewport&quot content=&quotwidth=device-width, initial-scale=1.0&quot>
    <title>Create new post</title>
</head>
<body>
    <form action=&quot/post&quot method=&quotPOST&quot>
        @csrf
        <input type=&quottext&quot name=&quottitle&quot id=&quottitle&quot>
        <textarea name=&quotbody&quot id=&quotbody&quot cols=&quot30&quot rows=&quot10&quot></textarea>
        <input type=&quotsubmit&quot value=&quotSubmit&quot>
    </form>
</body>
</html>

فراموش نکنید که در فرم خود @csrf را هم بگذارید تا یک فیلد مخفی (hidden input) برای توکن ساخته شود و خطای 419 (منقضی شدن صفحه: page expired) دریافت نکنید.

خطای 419 در صورت ننوشتن @csrf
خطای 419 در صورت ننوشتن @csrf


با تغییرات بالا، حالا اگر در ترمینال، سرور را اجرا کنیم، با یک فرم ساده روبرو می شویم:

php artisan serve
فرم ساده ای که ساخته ایم
فرم ساده ای که ساخته ایم


کارهای اولیه انجام شده و الان می توانیم به سراغ اجرای منطق اعتبارسنجی خود برویم.

متد validate برای ریکوئست

حالا متد store از کنترلر جدید خود را این گونه تغییر می دهیم:

public function store(Request $request)
{
   $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);

    // The blog post is valid...
    return dd($request->all());
}

در دستور بالا برای عنوان پست این اعتبارسنجی ها را وارد کرده ایم:

  • وارد کردن عنوان پست اجباری است.
  • این عنوان باید یکتا باشد (پیش از این در جدول post مشابه آن نبوده باشد)
  • حداکثر طول عنوان 255 کاراکتر است.

همچنین برای متن پست فقط یک اعتبارسنجی وارد کرده ایم: اجباری بودن. می بینید که شروط اعتبارسنجی با علامت | از هم جدا شده اند.

اگر همه ی اعتبارسنجی ها درست باشند، مقدار بازگشتی die and dump درخواست است. با تابع dd در واقع گفته ایم که مقادیر واردشده ی عنوان و متن پست چاپ شوند تا مطمئن شویم که درست وارد شده اند (از این تابع برای آزمایش برنامه استفاده می‌شود).

بعد از این تغییرات، اگر فرم خود را به صورت خالی ارسال کنیم (در فیلدهای عنوان و متن چیزی ننویسیم و روی سابمیت کلیک کنیم) دوباره به صفحه ی فرم باز می گردیم. اما اگر چیزی در فیلد عنوان بنویسیم با خطای اتصال به دیتابیس روبرو می شویم. چون اولین شرط اعتبارسنجی برای عنوان پست رعایت شده اما برای بررسی دومین شرط، لازم است که جدول پست در دیتابیس بررسی شود.

خطای اتصال به دیتابیس بعد از پر کردن فیلد فرم
خطای اتصال به دیتابیس بعد از پر کردن فیلد فرم

برای این که درگیر ساخت migration نشویم، این بخش از اعتبارسنجی را پاک می کنیم. انواع اعتبارسنجی‌های ممکن در لاراول را می توانید در مستندات ببینید.

تا این جا موفق شدیم فرم را در بک اند اعتبارسنجی کنیم. حالا اگر بخواهیم خطاهای اعتبارسنجی در صفحه ی فرم نمایش داده شوند چه باید بکنیم؟ در این مرحله می خواهیم به کاربر بگوییم چرا فرم او به درستی ثبت نشده است. برای این کار می توانیم از دستورات بلید در فایل create.blade.php استفاده کنیم:

@if ($errors->any())
    <div class=&quotalert alert-danger&quot>
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

در این کد گفته ایم که اگر خطایی در فرم وجود داشت، یک div از نوع کلاس alert-danger (که کلاس پیش‌فرض نمایش خطا در بوت استرپ است) بساز و به ازای هر خطا، آن خطا را نمایش بده.

حالا اگر فرم را به صورت خالی ثبت کنیم، با چنین صفحه ای روبرو می شویم:

خطاهای نمایش داده شده بعد از ثبت خالی فرم
خطاهای نمایش داده شده بعد از ثبت خالی فرم

این خطاها را می توان در resources/lang/en/validation.php پیدا کرد و در صورت لزوم تغییرشان داد. همچنین می توانید در همین پوشه ی lang یک پوشه به نام fa بسازید و در فایلی با نام مشابه، خطاها را ترجمه کنید. در این صورت، اگر برنامه به زبان فارسی باشد (یا چند زبانه باشد)، خطاها به فارسی نمایش داده می شوند. ترجمه های لاراول به زبان های مختلف (از جمله فارسی) در این مخزن گیتهابی هستند.

حالا فرض کنید یک کاربر، فیلد عنوان را پر کرد اما فیلد متن را نه. در این صورت می خواهیم، وقتی کاربر به همین صفحه برگشت، فیلد عنوان خالی نشده باشد. در واقع فرم، مقدار قبلی واردشده ی کاربر را نگه دارد. برای این کار از تابع old استفاده می کنیم و فیلدهای خود را به این شکل تغییر می دهیم:

<input type=&quottext&quot name=&quottitle&quot id=&quottitle&quot value=&quot{{ old('title') }}&quot>
<textarea name=&quotbody&quot id=&quotbody&quot cols=&quot30&quot rows=&quot10&quot>{{ old('body') }}</textarea>

در این صورت اگر یکی از این دو فیلد را پر کردیم و دیگری خالی بود، با بازگشت به صفحه ی فرم، فیلد پرشده باقی مانده است.

نگه داشتن مقدار فیلد پرشده بعد از بازگشت به صفحه ی فرم
نگه داشتن مقدار فیلد پرشده بعد از بازگشت به صفحه ی فرم

استفاده از facade

روش دوم برای اعتبارسنجی فرم در لاراول، استفاده از متد make در facadeی به نام Validator است (facadeها به طور خلاصه ابزارهایی برای دستیابی به امکانات مختلف لاراول در کلاس ها هستند). دستور اعتبارسنجی بخش قبلی در متد store از کنترلر PostController این طور بازنویسی می کنیم:

public function store(Request $request)
{
    $validator = Validator::make($request->all(), [
        'title' => 'required|max:255',
        'body' => 'required'
    ]);
    if ($validator->fails()) {
        return redirect('post/create')
            ->withErrors($validator)
            ->withInput();
    }
    return dd($request->all());
}

در این جا برخلاف بخش قبل، باید بگوییم که در صورت ناموفق بودن اعتبارسنجی چه اتفاقی بیفتد. در کد بالا گفته ایم که در این صورت، به صفحه ی فرم بازگرد و خطاها و مقادیر قدیمی واردشده را هم به این صفحه ببر. حالا اگر دوباره فرم را در حالت های مختلف تست کنیم، می بینیم که مثل قبل عمل می کند.

اگر بخواهیم، بررسی اعتبارسنجی ها به صورت خودکار انجام شود، می توانیم کد را به این صورت بازنویسی کنیم:

public function store(Request $request)
{
    Validator::make($request->all(), [
        'title' => 'required|max:255',
        'body' => 'required'
    ])->validate();
    return dd($request->all());
}

فرم ریکوئست

حالا به سراغ آخرین روش می رویم. توصیه ی لاراول این است که وقتی سراغ این روش بروید که می خواهید سناریوهای پیچیده تری برای اعتبارسنجی در نظر بگیرید. در این حالت، منطق اعتبارسنجی خود را از کنترلر جدا می کنید.

ابتدا باید این دستور را در ترمینال اجرا کنیم:

php artisan make:request StorePostRequest

با اجرای این دستور، یک پوشه ی جدید به نام Requests در پوشه ی app\Http ساخته می شود و در این پوشه یک فایل به نام StorePostRequest.php ساخته می شود. کلاس StorePostRequest در این فایل دو متد authorize و rules دارد. برای این که اعتبارسنجی را در این فایل انجام دهیم، ابتدا لازم است قوانین اعتبارسنجی مدنظر خود را در متد rules وارد کنیم:

public function rules()
{
    return [
        'title' => 'required|max:255',
        'body' => 'required'
    ];
}

فراموش نکنید که در متد authorize نیز به جای false مقدار true را برگردانید. در غیر این صورت با خطای 419 روبرو می شوید (همان طور که گفتیم این روش برای سناریوهای پیچیده ی اعتبارسنجی است. این متد هم کمک می کند که بر روی خود درخواست authorization انجام شود).

خطای 403 (unauthorized) لاراول در صورت تغییر ندادن مقدار بازگشتی متد authorize
خطای 403 (unauthorized) لاراول در صورت تغییر ندادن مقدار بازگشتی متد authorize


حالا متد store کنترلر خود را به این شکل تغییر می دهیم:

use App\Http\Requests\StorePostRequest;

public function store(StorePostRequest $request)
{
    $request->validated();
    return dd($request->all());
}

همان طور که می بینید در این کد، نوع آرگومان ورودی تابع store را به جای Request از نوع کلاس ریکوئستی که ساخته ایم قرار دادیم. استفاده از این روش باعث خلوت تر شدن کنترلر می شود اما برای مواردی که اعتبارسنجی زیاد پیچیده نیست، استفاده از این روش هم لازم نیست.

سخن پایانی

در این نوشته با استفاده از مستندات لاراول، درباره ی سه روش اعتبارسنجی فرم ها در این فریم ورک صحبت کردیم. این روش ها جزئیات و ریزه کاری هایی هم دارند که می توانید با مراجعه به مستندات درباره ی آنها بیشتر بدانید.