فصل ۱۳-۱: فرم‌ها و اعتبارسنجی (Validation) آنها در لاراول

12 فروردین 1396
درسنامه درس 19 از سری لاراول
Laravel-Main-validation-part-one

همانطور که در جریان هستید فرم‌ها به عنوان یکی از اساسی‌ترین و مهم‌ترین بخش‌های هر وب‌سایت و اپلیکیشن به‌حساب می‌آیند. وظایفی که فرم‌ها بر عهده خواهند داشت عبارتند از:‌ ثبت‌نام و عضویت کاربران، ارسال اطلاعات توسط کاربران به سرور، نحوه‌ی ارائه‌ی درخواست‌های کاربران جهت دریافت پاسخ مناسب، ذخیره‌سازی اطلاعات درون دیتابیس، مدیریت یک سیستم مدیریت محتوا و ... . لذا بر آن شدیم که با دقت تمام و موشکافانه به این مبحث مهم بپردازیم و یک فصل کامل را به آن اختصاص دهیم. در این فصل به توضیح دقیق فرم‌ها در فریم‌ورک قدرتمند لاراول پرداخته و همچنین نحوه‌ی اعتبار‌سنجی فرم‌ها (Validation) را به طور مفصل شرح می‌دهیم. با ما همراه باشید.

مقدمه

همواره در تمام نرم‌افزارهای تحت وب و دسکتاپ و ... فرم‌ها ابزار بسیار مهمی بوده‌اند. دریافت اطلاعات و ارسال پاسخ مهم‌ترین وظیفه‌ی آنها می‌باشد. اما مهم‌ترین مسئله در بکارگیری فرم‌ها اعتبارسنجی آنهاست. لاراول امکانات بسیار مناسبی را برای اعتبارسنجی آنها در اختیار ما قرار داده است که متناسب با داده‌ی دریافتی و تنظیم محدودیت‌ها این اعتبارسنجی را به ساده‌ترین شکل ممکن اجرا می‌کند. لاراول از کلاس کنترلر پایه‌ی ValidationRequests استفاده می‌کند تا قیود و تنظیمات کافی را برای هر درخواست HTTP انجام دهد.

شروع سریع برای اعتبارسنجی

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

مرحله ۱: تعریف مسیرها (Routes)

ابتدا یک سری مسیر داخل فایل web.php تعریف می کنیم:

Route::get('post/create', 'PostController@create');

Route::post('post', 'PostController@store');

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

مرحله ۲: ایجاد کنترلر

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

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
    /**
     * Show the form to create a new blog post.
     *
     * @return Response
     */
    public function create()
    {
        return view('post.create');
    }

    /**
     * Store a new blog post.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // Validate and store the blog post...
    }
}

مرحله ۳: اعمال اعتبارسنجی

حال نوبت به تنظیم کردن متد store رسیده است. این متد را باید با استفاده از قوانین منطقی جهت ارزیابی و اعتبارسنجی فرم‌ها تنظیم کنیم. بنابراین برای فعال سازی اعتبار سنجی همواره باید از کلاس کنترلر ValidatesRequests استفاده کرد.  این کلاس یک متد مناسب به نام validate را برای کنترلر شما فراهم می‌کند. این متد انواع درخواست‌های ارسالی HTTP را پذیرفته و قوانین اعتبارسنجی را روی آنها تنظیم می‌کند. اگر نتیجه اعتبار سنجی منطبق بر قوانین اعمالی بود که نتیجه مثبت بوده و ادامه‎ی کدها اجرا خواهد شد. اما در صورتیکه اعتبارسنجی منطبق بر نتایج نباشد، پیامی به صفحه نمایش کاربر پرتاب شده که شامل خطاهای اعتبارسنجی است و کاربر را به صورت خودکار به صفحه قبلی هدایت می‌کند. بنابراین برای درک بهتر به متد store مراجعه می‌کنیم:

/**
 * Store a new blog post.
 *
 * @param  Request  $request
 * @return Response
 */
public function store(Request $request)
{
    $this->validate($request, [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);

    // The blog post is valid, store in database...
}

همانطور که مشاهده می‌کنید اولین آرگومان متد validate شامل درخواست ارسالی HTTP است که از کلاس Request و با متغییر request$ ارسال می‌شود. سپس در آرگومان دوم قوانین موردنظر را برای هر فیلد ورودی موجود در فرم اعمال کرده‌ایم. برای اعتبارسنجی هر فیلد از علامت <= استفاده کرده و هر قانون را با استفاده از یک علامت | از هم جدا می‌کنیم. قبل از اینکه به جزئیات این قوانین بپردازیم یک سری روش‌ها را برای کار با این قوانین یا rules ها ایجاد می‌کنیم:

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

$this->validate($request, [
    'title' => 'bail|required|unique:posts|max:255',
    'body' => 'required',
]);

در این مثال اگر قانون required برای title اجرا و اعتبارسنجی آن با شکست مواجه شود، قانون بعدی آن که شامل unique است اجرا نخواهد شد.

حال اگر داده‌ها و درخواست‌های ارسالی HTTP شامل پارامترهای تودرتو بود می‌توان با استفاده از یک علامت دات (.) به زیرمجموعه‌های آن دست پیدا کرد:

$this->validate($request, [
    'title' => 'required|unique:posts|max:255',
    'author.name' => 'required',
    'author.description' => 'required',
]);

مرحله ۴: نمایش خطاها

حال این سوال پیش می‌آید که اگر اعتباسنجی به درستی انجام نشد و درخواست‌های ارسالی با قوانین موجود در اعتبارسنجی تداخل داشتند چگونه باید عمل کرد؟ همانطور که ذکر کردیم در این حالت لاراول به صورت خودکار کاربر را به صفحه قبلی باز می‌گرداند. علاوه بر این تمام خطاهای اعتبارسنجی داخل یک جلسه یا Session ذخیره خواهند شد.

مجددا در نظر دارید که خطاهای اعتبارسنجی به صورت خودکار به مسیر GET ارسال نمی‌شوند بلکه آنها ابتدا داخل یک Session ذخیره شده و سپس به صورت خودکار به ویو موردنظر در صورت وجود با استفاده از متغییر errors$ ارسال می‌شوند. این متغییر یک نمونه از کلاس Illuminate\Support\MessageBag است که در ادامه به صورت مفصل درمورد آن صحبت خواهیم کرد. 

بنابراین در مثال فوق کاربر ابتدا به متد create مراجعه کرده و در صورتیکه قوانین موردنظر را برای اعتبارسنجی، رعایت نکند، متغییری به نام errors$ خطاها را ذخیره کرده و به view ارسال می‌کند. حال برای نمایش این خطاها از دستور زیر استفاده خواهیم کرد:

<!-- /resources/views/post/create.blade.php -->

<h1>Create Post</h1>

@if (count($errors) > 0)
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

<!-- Create Post Form -->

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

چند نکته‌ی کلیدی

لاراول در حالت پیش‌فرض از میان‌افزارهای TrimStrings و ConvertEmptyStringsToNull استفاده کرده و هر آنچه به عنوان مقدار خالی باشد را به null‌ تبدیل می‌کند. اما همواره در دیتابیس شما برخی از فیلدها مقدار nullable دارند بنابراین برای اینکه یک اعتبارسنجی به این فیلدها بدهیم همواره باید مقدار nullable  را به عنوان یکی از قوانین به آنها اعمال کنیم تا درصورتیکه آن فیلد خالی بود خطایی که گرفته می‌شود برای null بودن نباشد بلکه برای سایر قوانین باشد.

$this->validate($request, [
    'title' => 'required|unique:posts|max:255',
    'body' => 'required',
    'publish_at' => 'nullable|date',
]);

در این مثال همانطور که ملاحظه می‌کنید فیلد publish_at به عنوان یک فیلد null و یا معتبر برای یک تاریخ date در نظر گرفته شده است. یعنی اگر nullable را به این فیلد اضافه نکرده باشیم همواره اعتبارسنج یا validator مقدار null را یک خطا درنظر می‌گیرد.

شخصی‌سازی فرمت خطاهای ذخیره‌شده

برای شخصی‌‌سازی شیوه نمایش اطلاعات ذخیره شده در session می‌توان کلاس formatValidationErros را در کنترلر اصلی تغییر دهید. همواره به یاد داشته باشید که باید کلاس Illuminate\Contracts\Validation\Validator در ابتدای فایل کنترلر شما تعریف شده باشد:

<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;

abstract class Controller extends BaseController
{
    use DispatchesJobs, ValidatesRequests;

    /**
     * {@inheritdoc}
     */
    protected function formatValidationErrors(Validator $validator)
    {
        return $validator->errors()->all();
    }
}

بسیار عالی! با انجام مثال فوق، اعتبارسنجی در سیستم شما فعال شد. حال به توضیح دقیق تر مبحث اعتبارسنجی می‌پردازیم.

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

ایجاد درخواست‌های اعتبارسنجی

برای سناریوهای پیشرفته با اعتبارسنجی خاص همواره باید یک «درخواست فرم» ایجاد کرد. درخواست‌‎های یک فرم به عنوان کلاس‌های درخواست شخصی‌سازی شده‌ای هستند که شامل قوانین و منطق‌های اعتبارسنجی هستند. برای ساختن یک کلاس درخواست فرم، از دستور make:request در Artisan Cli یا خط فرمان آرتیسن استفاده می‌شود:

php artisan make:request StoreBlogPost

با اجرای دستور بالا یک کلاس در مسیر app/Http/Request ایجاد می‌شود. اگر این مسیر به صورت پیش‌فرض وجود نداشته باشد، ایجاد خواهد شد. داخل این کلاس یک متد به نام rule وجود دارد که تمام قوانین موردنظر برای اعتبارسنجی درون آن نوشته می‌شود. حال با متد rule کار می‌کنیم:

/**
 * Get the validation rules that apply to the request.
 *
 * @return array
 */
public function rules()
{
    return [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ];
}

این سوال برای شما پیش می‌آید که این اعتبار سنجی چگونه کار می‌کند؟ برای استفاده از اعتبارسنجی در کنترلر همواره باید درون متد موردنظر (مثلا متد store) از کلاس Request‌ استفاده کنید. تا تمام درخواست‌های کاربران را دریافت کرده و مورد ارزیابی قرار دهید. اما هنگامیکه شما یک درخواست فرم ایجاد می‌کنید می‌توانید به جای استفاده از Request از نام کلاسی که ایجاد کرده‌اید بهره ببرید. مثلا از نام کلاس StoreBlogPost به عنوان یک کلاس درخواست استفاده می‌کنیم. با اجرای این دستور، اطلاعات ارسالی توسط کاربر ابتدا وارد کلاس StoreBlogPost شده و مورد ارزیابی قرار می‌گیرند، سپس متد store کنترلر اجرا می‌شود. این بدین معنی‌ست که نیازی به شلوغ کردن متدهای کنترلر نیست و به راحتی می‌توان کلاس اعتبارسنجی فوق را برای تمام متدهایی که مد نظرمان هست استفاده کنیم:

/**
 * Store the incoming blog post.
 *
 * @param  StoreBlogPost  $request
 * @return Response
 */
public function store(StoreBlogPost $request)
{
    // The incoming request is valid...
}

اگر اعتبارسنجی با خطا مواجه شود یک پاسخ ریدایرکتی برای بازگرداندن کاربر به صفحه و مکان قبلی‌اش ارسال خواهد شد و کاربر به صفحه قبلی یا همان فرم باز می‌گردد. این خطاها درون جلسه یا session ذخیره شده و برای نمایش قابل دسترس خواهند بود. اگر این درخواست به صورت AJAX باشد، یک پاسخ HTTP به همراه وضعیت ۴۴۲ به کاربر بازگردانده خواهد شد که خطاها را به صورت JSON نمایش می‌دهد.

شخصی‌سازی فرمت خطاها

اگر تمایل دارید که تمام خطاهای اعتبارسنجی را به هنگام مواجه شدن با شکست در اعتبارسنجی، ویرایش کنید می‌توانید متد formatErrors را رونویسی کنید که از مسیر App\Http\Requests\Request همواره ارث‌بری می‌کند. فراموش نکنید که کلاس Illuminate\Contracts\Validation\Validator در بالای فایل شما باید فراخوانی شود:

/**
 * {@inheritdoc}
 */
protected function formatErrors(Validator $validator)
{
    return $validator->errors()->all();
}

ساخت یک ارزیاب شخصی

در صورتیکه نمی‌خواهید از متد validate ویژگی ValidateRequest استفاده کنید، باید یک نمونه‌ی ارزیاب در کنترلر خود با استفاده از Validator Facade ایجاد کنید. متد make در Facade یک ارزیاب جدید ایجاد می‌کند:

<?php

namespace App\Http\Controllers;

use Validator;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
    /**
     * Store a new blog post.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'title' => 'required|unique:posts|max:255',
            'body' => 'required',
        ]);

        if ($validator->fails()) {
            return redirect('post/create')
                        ->withErrors($validator)
                        ->withInput();
        }

        // Store the blog post...
    }
}

اولین آرگومانی که به متد make ارسال می‌شود شامل داده‌هایی‌ست که باید مورد ارزیابی قرار بگیرند و آرگومان دوم شامل قوانینی‌ست که باید روی این داده‌ها اعمال شوند. سپس برای چک کردن اعتبارسنجی از یک دستور شرطی به همراه متد fail استفاده شده است. پس از چک کردن می‌توان از متد withErrors برای ذخیره‌سازی خطاها داخل جلسه یا Session استفاده کرد. هنگامیکه از این متد استفاده می‌کنیم. متغییر errors$ به صورت خودکار پس از ریدایرکت شدن کاربر، در ویو share (به اشتراک گذاشته) می‌شود. این متغییر به شما اجازه می‌دهد که به سادگی هرچه تمام تر خطاها را در هنگام بازگشت کاربر به صفحه قبلی به نمایش بگذارید. متد withErrors یک ارزیاب یا validator یا یک MessageBag یا آرایه‌ی خام PHP را به عنوان آرگومان می‌پذیرد.

ریدایرکت شدن خودکار

برای اینکه کاربر پس از انجام اعتبارسنجی به صورت خودکار به صفحه‌ی موردنظر انتقال پیدا کند همواره باید از متد validate پس از نمونه‌ی ایجاد شده استفاده کرد. اگر اعتبارسنجی با خطا مواجه شود کاربر به صورت خودکار به صفحه موردنظر انتقال پیدا می‌کند:

Validator::make($request->all(), [
    'title' => 'required|unique:posts|max:255',
    'body' => 'required',
])->validate();

نام‌گذاری متن خطاها

فرض کنید چندین فرم در یک صفحه داشته باشید و بخواهید هر یک از خطاها را برای جلوگیری از گیج شدن نام‌گذاری کنید. در این صورت می‌توان از نام‌گذاری خطاهای MessageBag استفاده کرد. این نام به عنوان آرگومان دوم به متد withErrors ارسال می‌شود:

return redirect('register')
            ->withErrors($validator, 'login');

بنابراین برای دسترسی به خطاهای یک فرم باید فراخوانی را روی متغییر errors$ به صورت زیر انجام داد:

{{ $errors->login->first('email') }}

کار کردن با پیام‌های خطا

پس از فراخوانی متد errors در یک نمونه‌ی Validator، یک نمونه‌ از کلاس Illuminate\Support\MessageBag دریافت خواهید کرد. این نمونه شامل انواع متدها و فیلترها برای نمایش متن خطاها است. متغییر errors$ به صورت خودکار در سرتاسر ویو شما به عنوان یک نمونه از کلاس MessageBag در دسترس خواهد بود.

بازیابی اولین متن خطا برای یک فیلد خاص

با استفاده از متد first می‌توان به اولین متن خطا در یک فیلد مشخص دست پیدا کرد:

$errors = $validator->errors();

echo $errors->first('email');

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

برای بازگردانی و بازیابی تمام خطاها در یک آرایه می‌توان از متد get استفاده کرد:

foreach ($errors->get('email') as $message) {
    //
}
در صورتیکه فرم شما به صورت آرایه‌ای تعریف شده است (یعنی هر فیلد را به صورت آرایه تعریف کرده باشیم) می‌توان از علامت * برای دسترسی به تمام خطاهای آن فیلد خاص و آرایه خاص دست پیدا کرد:
foreach ($errors->get('attachments.*') as $message) {
    //
}

بازیابی تمام خطاها برای تمام فیلد‌ها

برای بازیابی تمام خطاهای همه‌ی فیلدها از متد all استفاده می‌کنیم:

foreach ($errors->all() as $message) {
    //
}

بررسی وجود خطا در یک فیلد خاص

برای چک کردن وجود و یا عدم وجود خطا در یک فیلد خاص از متد has استفاده می‌شود:

if ($errors->has('email')) {
    //
}

شخصی‌سازی متن خطاها

برای شخصی‌سازی متن پیام‌هایی که به عنوان خطاها نمایش‌داده خواهد شد می‌توان متد messages را رونویسی کرد. این متد آرایه‌ای از صفات و قوانین و همچنین متن خطاها را باز می‌گرداند:

/**
 * Get the error messages for the defined validation rules.
 *
 * @return array
 */
public function messages()
{
    return [
        'title.required' => 'A :attribute is required',
        'body.required'  => 'A message is required',
    ];
}

در مثال فوق همانطور که ملاحظه می‌کنید از یک فضای نگه‌دارند به نام attribute: استفاده کرده‌ایم. این فضای نگه‌دارنده با نام واقعی فیلد جایگزین خواهد شد. همچنین می‌توان از سایر فضاهای نگه‌دارنده استفاده کرد:

$messages = [
    'same'    => 'The :attribute and :other must match.',
    'size'    => 'The :attribute must be exactly :size.',
    'between' => 'The :attribute must be between :min - :max.',
    'in'      => 'The :attribute must be one of the following types: :values',
];

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

این نمونه در مثال فوق ارائه شده است که شما می‌توانید با استفاده از یک علامت دات (.) متن خطای دلخواه خود را به آرایه‌ی message$ ارسال کنید:

$messages = [
    'email.required' => 'We need to know your e-mail address!',
];

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

در بسیاری از موارد نیاز دارید که یک متن خطای مشخص را برای یک زبان مشخص درست کرده و به Validator ارسال کنید. برای انجام این‌کار می‌توانن آرایه custom را به فایل زبان در مسیر resources/lang/xx/validation.php اضافه کنید:

'custom' => [
    'email' => [
        'required' => 'We need to know your e-mail address!',
    ],
],

معادل‌سازی صفات دلخواه برای استفاده در زبان‌های مختلف

در صورتیکه نیاز دارید دستور attribute: با نام صفت دلخواه شما که متناسب با یک زبان مشخص است، جایگزین شود، باید یک عبارت به نام attribute در مسیر resources/lang/xx/validation.php اضافه کرده و نام فیلد به همراه نام صفت را در آن جایگزین کنیم:

'attributes' => [
    'email' => 'email address',
],

 

با مطالعه‌ی این فصل با روش ایجاد اعتبارسنجی و ارسال و دریافت خطاها و متن‌ آنها آشنا شدید. حال می‌توانید فرم‌های هوشمند خود را ایجاد کرده و قوانین مشخصی را روی آنها وضع کنید. در بخش ۱۳-۲ به توضیح تمام متدها برای اعتبارسنجی می‌پردازیم. با ما همراه باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری لاراول توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

مرتضی
09 مهر 1396
بسیار عالی موفق و پیروز باشید

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.