فصل هفتم - جمع‌ آوری و پردازش داده‌ های کاربران

جمع‌ آوری و پردازش داده‌ های کاربران

وب‌سایت‌هایی که از فریم‌ورکی مثل لاراول استفاده می‌کنند، معمولاً فقط محتوای ایستا ارائه نمی‌دهند. خیلی از آن‌ها با منابع داده‌ای پیچیده و متنوع سروکار دارند. یکی از رایج‌ترین (و البته پیچیده‌ترین) این منابع، ورودی کاربر در شکل‌های مختلف آن است: مسیرهای URL، پارامترهای query، داده‌های POST و فایل‌هایی که کاربران آپلود می‌کنند.
لاراول مجموعه‌ای از ابزارها را برای جمع‌آوری، اعتبارسنجی، نرمال‌سازی و فیلتر کردن داده‌هایی که کاربران وارد می‌کنند فراهم کرده است. در اینجا به بررسی آن‌ها می‌پردازیم.

تزریق شی Request

رایج‌ترین ابزار برای دسترسی به داده‌های کاربر در Laravel، تزریق نمونه‌ای از شی Illuminate\Http\Request است. این شی دسترسی آسانی به تمام روش‌هایی که کاربران می‌توانند اطلاعات ارسال کنند فراهم می‌کند: داده‌های فرم POST یا JSON، درخواست‌های GET (پارامترهای کوئری) و بخش‌های URL.

گزینه‌های دیگر برای دسترسی به داده‌ی درخواست

همچنین یک هلپر سراسری به نام request() و یک facade به نام Request وجود دارد که هر دو همین متدها را ارائه می‌دهند. هرکدام از این گزینه‌ها شی کامل Illuminate Request را در اختیار شما قرار می‌دهند، اما فعلاً فقط متدهایی را بررسی می‌کنیم که مشخصاً مربوط به داده‌های کاربر هستند.

از آنجا که قصد داریم شی Request را تزریق کنیم، بیایید سریع ببینیم چطور می‌توانیم به شی $request که این متدها را روی آن صدا می‌زنیم، دسترسی داشته باشیم:

Route::post('form', function (Illuminate\Http\Request $request) {
    // $request->etc()
});

$request->all()

همان‌طور که از اسمش پیداست، $request->all() آرایه‌ای شامل تمام ورودی‌هایی که کاربر ارائه کرده است را، از تمام منابع، برمی‌گرداند. فرض کنید به هر دلیلی تصمیم گرفته‌اید یک فرم را به URLای با یک پارامتر کوئری ارسال کنید — مثلاً یک POST به http://myapp.com/signup?utm=12345. به مثال 7-1 نگاه کنید تا ببینید خروجی $request->all() چه خواهد بود. (توجه داشته باشید که $request->all() همچنین شامل اطلاعاتی درباره‌ی فایل‌های آپلود شده نیز هست، ولی آن را بعداً در همین فصل بررسی خواهیم کرد.)
مثال 7-1. $request->all()

<!-- GET route form view at /get-route -->
<form method="post" action="/signup?utm=12345">
    @csrf
    <input type="text" name="first_name">
    <input type="submit">
</form>
// routes/web.php
Route::post('signup', function (Request $request) {
    var_dump($request->all());
});

// Outputs:
/**
 * [
 *     '_token' => 'CSRF token here',
 *     'first_name' => 'value',
 *     'utm' => 12345,
 * ]
 */

 

$request->except() و $request->only()

متد $request->except() خروجی مشابه $request->all() ارائه می‌دهد، اما می‌توانید یک یا چند فیلد را حذف کنید — مثلاً _token. می‌توانید به آن یک رشته یا آرایه‌ای از رشته‌ها بدهید.
مثال 7-2 نشان می‌دهد که استفاده از $request->except() روی همان فرم مثال 7-1 چگونه خواهد بود.
مثال 7-2. $request->except()

Route::post('post-route', function (Request $request) {
    var_dump($request->except('_token'));
});

// Outputs:
/**
 * [
 *     'firstName' => 'value',
 *     'utm' => 12345
 * ]
 */

$request->only() برعکس $request->except() است، همان‌طور که در مثال ۷-۳ می‌بینید.

مثال 7-3. $request->only()

Route::post('post-route', function (Request $request) {
    var_dump($request->only(['firstName', 'utm']));
});

// Outputs:
/**
 * [
 *     'firstName' => 'value',
 *     'utm' => 12345
 * ]
*/

$request->has() و $request->missing()

با استفاده از $request-&gt;has() می&zwnj;توانید بررسی کنید که آیا یک ورودی خاص از طرف کاربر در دسترس است یا نه، صرف&zwnj;نظر از اینکه مقداری داشته باشد یا نه. مثال ۷-۴ را برای نمونه&zwnj;ای تحلیلی با پارامتر query string به نام utm از مثال&zwnj;های قبلی ببینید. مثال 7-4. $request-&gt;has() // POST route at /post-route if ($request-&gt;has('utm')) { // Do some analytics work } متد $request-&gt;missing() برعکس آن است.

برای دیدن ادامه محتوا وارد شوید

$request->whenHas()

با متد $request->whenHas() می‌توانید رفتار زمانی که یک فیلد وجود دارد یا وجود ندارد را مشخص کنید. اگر فیلد موجود باشد، کلوزر اول اجرا می‌شود، و اگر وجود نداشته باشد، کلوزر دوم اجرا می‌شود.
برای نمونه‌ای از این موضوع با پارامتر utm، به مثال ۷-۵ توجه کنید.
مثال 7-5. $request->whenHas()

// POST route at /post-route
$utm = $request->whenHas('utm', function($utm) {
    return $utm;
}, function() {
    return 'default';
});

$request->filled()

با استفاده از متد $request-&gt;filled() می&zwnj;توان بررسی کرد که آیا یک فیلد خاص در درخواست وجود دارد و مقدار هم دارد یا نه. متد filled() مشابه has() است، با این تفاوت که فیلد باید دارای مقدار واقعی نیز باشد. در مثال ۷-۶ می&zwnj;توانید نحوه استفاده از این متد را ببینید.مثال 7-6. $request-&gt;filled() // POST route at /post-route if ($request-&gt;filled('utm')) { // Do some analytics work }

برای دیدن ادامه محتوا وارد شوید

$request->whenFilled()

مشابه متد whenHas()، متد $request->whenFilled() به شما اجازه می‌دهد تا مقدارها را هم در حالتی که فیلد پر شده است و هم در حالتی که پر نشده تعریف کنید. کلوزر اول زمانی اجرا می‌شود که فیلد مقدار داشته باشد و کلوزر دوم زمانی اجرا می‌شود که نداشته باشد. مثال ۷-۷ نحوه استفاده از این متد را نشان می‌دهد.
مثال 7-7. $request->whenFilled()

// POST route at /post-route
$utm = $request->whenFilled('utm', function($utm) {
    return $utm;
}, function() {
    return 'default';
});

$request->mergeIfMissing()

با متد mergeIfMissing() می‌توانید در صورتی که فیلدی در درخواست وجود نداشته باشد، آن را همراه با مقدار مورد نظر اضافه کنید. این کار می‌تواند مفید باشد، مثلاً وقتی فیلدی از یک چک‌باکس می‌آید و فقط زمانی وجود دارد که تیک خورده باشد. مثال ۷-۸ یک پیاده‌سازی از این حالت را نشان می‌دهد.
مثال 7-8. $request->mergeIfMissing()

// POST route at /post-route
$shouldSend = $request->mergeIfMissing('send_newsletter', 0);

$request->input()

در حالی که متدهای $request->all()، $request->except() و $request->only() روی آرایه کامل داده‌های ورودی کار می‌کنند، متد $request->input() به شما اجازه می‌دهد فقط مقدار یک فیلد خاص را دریافت کنید. مثال ۷-۹ یک نمونه از این استفاده را نشان می‌دهد. توجه داشته باشید که پارامتر دوم مقدار پیش‌فرض است؛ بنابراین اگر کاربر مقداری وارد نکرده باشد، یک مقدار جایگزین مناسب و بدون خطا دریافت می‌کنید.
مثال 7-9. $request->input()

Route::post('post-route', function (Request $request) {
    $userName = $request->input('name', 'Matt');
});

$request->method() و $request->isMethod()

متد $request-&gt;method() نوع HTTP درخواست (مثلاً POST، GET، PATCH و ...) را برمی&zwnj;گرداند و متد $request-&gt;isMethod() بررسی می&zwnj;کند که آیا نوع درخواست با مقدار مشخص&zwnj;شده تطابق دارد یا نه. مثال ۷-۱۰ استفاده از این دو را نشان می&zwnj;دهد.مثال 7-10. $request-&gt;method() و $request-&gt;isMethod() $method = $request-&gt;method(); if ($request-&gt;isMethod('patch')) { // Do something if request method is PATCH }

برای دیدن ادامه محتوا وارد شوید

$request->integer()، $request->float()، $request->string() و $request->enum()

این متدها ورودی‌ها را مستقیماً به نوع عدد صحیح، عدد اعشاری، رشته‌ای یا enum تبدیل می‌کنند. برای نمونه‌هایی از نحوه استفاده، به مثال ۷-۱۱ مراجعه کنید.
مثال 7-11. $request->integer()، $request->float()، $request->string() و $request->enum()

dump(is_int($request->integer('some_integer'));
// true

dump(is_float($request->float('some_float'));
// true

dump(is_string($request->string('some_string'));
// true

dump($request->enum('subscription', SubscriptionStatusEnum::class));
// 'active', assuming that's a valid status for the SubscriptionStatusEnum

$request->dd() و $request->dump()

$request->dump() و $request->dd() متدهای کمکی برای dump کردن درخواست هستند. برای هر دو، می‌توانید با عدم ارسال هیچ پارامتری، کل درخواست را dump کنید یا با ارسال یک آرایه، فقط فیلدهای انتخاب‌شده را dump نمایید. $request->dump() اطلاعات را dump کرده و سپس ادامه می‌دهد، در حالی که $request->dd() اطلاعات را dump کرده و سپس اجرای اسکریپت را متوقف می‌کند. مثال 7-12 استفاده از آن‌ها را نشان می‌دهد.

مثال ۷-۱۲. استفاده از $request->dd() و $request->dump()

// dump کردن کل درخواست
$request->dump();
$request->dd();

// فقط نمایش دو فیلد
$request->dump(['name', 'utm']);
$request->dd(['name', 'utm']);

ورودی‌های آرایه‌ای

لاراول همچنین متدهای کمکی راحتی برای دسترسی به داده&zwnj;ها در ورودی&zwnj;های آرایه&zwnj;ای ارائه می&zwnj;دهد. کافی است از نشان&zwnj;گذاری نقطه&zwnj;ای (dot notation) استفاده کنید تا مسیر دسترسی به داده&zwnj;های درون آرایه را مشخص نمایید، مانند مثال ۷-۱۳.مثال ۷-۱۳. استفاده از dot notation برای دسترسی به مقادیر آرایه در داده&zwnj;های کاربر &lt;!-- GET route form view at /employees/create --&gt; &lt;form method="post" action="/employees/"&gt; @csrf &lt;input type="text" name="employees[0][firstName]"&gt; &lt;input type="text" name="employees[0][lastName]"&gt; &lt;input type="text" name="employees[1][firstName]"&gt; &lt;input type="text" name="employees[1][lastName]"&gt; &lt;input type="submit"&gt; &lt;/form&gt; // POST route at /employees Route::post('employees', function (Request $request) { $employeeZeroFirstName = $request-&gt;input('employees.0.firstName'); $allLastNames = $request-&gt;input('employees.*.lastName'); $employeeOne = $request-&gt;input('employees.1'); var_dump($employeeZeroFirstname, $allLastNames, $employeeOne); }); //...

برای دیدن ادامه محتوا وارد شوید

ورودی JSON (و $request->json())

تا به اینجا ورودی‌هایی از رشته‌های کوئری (GET) و ارسال فرم‌ها (POST) را بررسی کرده‌ایم. اما نوع دیگری از ورودی کاربر وجود دارد که با پیشرفت JavaScript SPAs رایج‌تر شده است: درخواست JSON. این اساساً یک درخواست POST است که بدنه آن به جای ارسال فرم سنتی، به JSON تنظیم شده است.
بیایید نگاهی به این بیندازیم که ارسال JSON به یک مسیر لاراول چگونه ممکن است باشد و چگونه از $request->input() برای استخراج این داده‌ها استفاده کنیم (مثال 7-14).
مثال 7-14. گرفتن داده‌ها از JSON با $request->input()

POST /post-route HTTP/1.1
Content-Type: application/json

{
    "firstName": "Joe",
    "lastName": "Schmoe",
    "spouse": {
        "firstName": "Jill",
        "lastName":"Schmoe"
    }
}
// Post-route
Route::post('post-route', function (Request $request) {
    $firstName = $request->input('firstName');
    $spouseFirstname = $request->input('spouse.firstName');
});


از آنجا که $request->input() به اندازه کافی هوشمند است که داده‌های کاربر را از GET، POST یا JSON استخراج کند، ممکن است از خود بپرسید چرا لاراول حتی $request->json() را ارائه می‌دهد. دو دلیل برای ترجیح دادن $request->json() وجود دارد. اول اینکه ممکن است بخواهید به طور مشخص‌تر به سایر برنامه‌نویسانی که روی پروژه شما کار می‌کنند، بگویید که از کجا انتظار دارید داده‌ها بیایند. دوم اینکه اگر POST هدرهای صحیح application/json را نداشته باشد، $request->input() آن را به عنوان JSON شناسایی نمی‌کند، اما $request->json() این کار را خواهد کرد.

 

فضاهای نام فسادها، کمک‌کننده سراسری request() و تزریق $request 

هر زمان که از فسادها در کلاس‌های دارای فضای نام استفاده می‌کنید (مثلاً کنترلرها)، باید مسیر کامل فساد را به بلوک واردات در بالای فایل خود اضافه کنید (مثلاً use Illuminate\Support\Facades\Request).
به همین دلیل است که چندین فساد همچنین یک تابع کمک‌کننده جهانی همراه دارند. اگر این توابع کمک‌کننده بدون پارامتر اجرا شوند، همان سینتکس فساد را نمایش می‌دهند (مثلاً request()->has() معادل Request::has() است). آن‌ها همچنین یک رفتار پیش‌فرض دارند زمانی که یک پارامتر به آن‌ها داده شود (مثلاً request('firstName') میانبری به request()->input('firstName') است).
با Request، ما تزریق یک نمونه از شیء Request را بررسی کرده‌ایم، اما شما همچنین می‌توانید از فساد Request یا کمک‌کننده جهانی request() استفاده کنید. برای اطلاعات بیشتر به فصل 10 مراجعه کنید.

داده‌های مسیر

&nbsp;ممکن است این اولین چیزی نباشد که وقتی به "داده&zwnj;های کاربر" فکر می&zwnj;کنید به ذهنتان می&zwnj;آید، اما URL همان&zwnj;طور که هر چیز دیگری در این فصل داده کاربر است. دو روش اصلی برای گرفتن داده&zwnj;ها از URL وجود دارد: از طریق اشیاء Request و از طریق پارامترهای مسیر.

برای دیدن ادامه محتوا وارد شوید

از طریق Request

اشیاء Request تزریق شده (و فساد Request و کمک‌کننده request()) چندین متد دارند که وضعیت URL صفحه جاری را نمایش می‌دهند، اما در حال حاضر بیایید روی گرفتن اطلاعات از بخش‌های URL تمرکز کنیم.
اگر با این ایده آشنا نیستید، هر گروه از حروف بعد از دامنه در یک URL به عنوان یک بخش شناخته می‌شود. پس، http://www.myapp.com/users/15/ دو بخش دارد: users و 15.
همانطور که احتمالا حدس می‌زنید، دو متد در دسترس داریم: $request->segments() یک آرایه از همه بخش‌ها برمی‌گرداند و $request->segment($segmentId) به ما این امکان را می‌دهد که مقدار یک بخش خاص را دریافت کنیم. توجه کنید که بخش‌ها با اندیس 1 شروع می‌شوند، بنابراین در مثال قبلی، $request->segment(1) مقدار users را برمی‌گرداند.
اشیاء Request، فساد Request و کمک‌کننده جهانی request() متدهای دیگری نیز برای کمک به استخراج داده‌ها از URL فراهم می‌کنند. برای یادگیری بیشتر، به فصل 10 مراجعه کنید.

از طریق پارامترهای مسیر

روش اصلی دیگر برای گرفتن داده‌ها از URL، پارامترهای مسیر هستند که به روش یا بستن کنترلری که مسیر جاری را سرویس می‌دهد، تزریق می‌شوند، همانطور که در مثال 7-15 نشان داده شده است.
مثال 7-15. گرفتن جزئیات URL از پارامترهای مسیر

// routes/web.php
Route::get('users/{id}', function ($id) {
       // اگر کاربر به myapp.com/users/15/ مراجعه کند، $id برابر 15 خواهد بود
});

برای یادگیری بیشتر در مورد مسیرها و اتصال مسیرها، به فصل 3 مراجعه کنید.

آپلود فایل‌ها

&nbsp;ما در مورد روش&zwnj;های مختلف تعامل با ورودی متنی کاربران صحبت کردیم، اما موضوع دیگری که باید به آن توجه کنیم، آپلود فایل&zwnj;ها است. اشیاء Request دسترسی به فایل&zwnj;های آپلود شده را از طریق متد $request-&gt;file() فراهم می&zwnj;کنند، که نام ورودی فایل را به عنوان پارامتر می&zwnj;گیرد و یک نمونه از Symfony\Component\HttpFoundation\File\UploadedFile را برمی&zwnj;گرداند. بیایید از یک مثال استفاده کنیم. ابتدا فرم ما، در مثال 7-16.مثال 7-16. یک فرم برای آپلود فایل&zwnj;ها &lt;form method="post" enctype="multipart/form-data"&gt; @csrf &lt;input type="text" name="name"&gt; &lt;input type="file" name="profile_picture"&gt; &lt;input type="submit"&gt; &lt;/form&gt; حالا بیایید نگاهی به چیزی که از اجرای $request-&gt;all() به دست می&zwnj;آوریم بیندازیم، همانطور که...

برای دیدن ادامه محتوا وارد شوید

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

 راه‌های مختلفی برای اعتبارسنجی داده‌های ورودی دارد. در بخش بعدی درخواست‌های فرم را پوشش خواهیم داد، بنابراین دو گزینه اصلی برای ما باقی می‌ماند: اعتبارسنجی دستی یا استفاده از متد validate() روی شیء Request. بیایید با validate() که ساده‌تر و رایج‌تر است شروع کنیم.

validate() روی شیء Request

شیء Request دارای متد validate() است که یک میانبر راحت برای جریان کار اعتبارسنجی معمول فراهم می‌کند. به مثال 7-19 نگاهی بیندازید.
مثال 7-19. استفاده پایه از اعتبارسنجی درخواست

// routes/web.php
Route::get('recipes/create', [RecipeController::class, 'create']);
Route::post('recipes', [RecipeController::class, 'store']);
// app/Http/Controllers/RecipeController.php
class RecipeController extends Controller
{
    public function create()
    {
        return view('recipes.create');
    }

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

        // Recipe is valid; proceed to save it
    }
}

ما تنها چهار خط کد برای اجرای اعتبارسنجی داریم، اما این خطوط کار زیادی انجام می‌دهند.

اول، ما به طور صریح فیلدهایی که انتظار داریم را تعریف کرده و قوانین (که در اینجا با کاراکتر لوله | جدا شده‌اند) را به هرکدام به طور جداگانه اعمال می‌کنیم.

بعد، متد validate() داده‌های ورودی از $request را بررسی کرده و تعیین می‌کند که آیا داده‌ها معتبر هستند یا خیر.

اگر داده‌ها معتبر باشند، متد validate() تمام می‌شود و می‌توانیم به متد کنترلر ادامه دهیم، داده‌ها را ذخیره کنیم یا هر کار دیگری.

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

 

بیشتر در مورد قوانین اعتبارسنجی لاراول

در مثال&zwnj;های ما (مثل مستندات) از نحو "لوله" استفاده می&zwnj;کنیم: 'fieldname': 'rule|otherRule|anotherRule'. اما شما می&zwnj;توانید از نحو آرایه&zwnj;ای برای انجام همین کار استفاده کنید: 'fieldname': ['rule', 'otherRule', 'anotherRule']. علاوه بر این، می&zwnj;توانید خواص تو در تو را اعتبارسنجی کنید. این موضوع زمانی مهم است که از نحو آرایه&zwnj;ای HTML استفاده می&zwnj;کنید، که به شما این امکان را می&zwnj;دهد که به عنوان مثال چندین "کاربر" در یک فرم HTML داشته باشید که هرکدام یک نام مرتبط دارند. اینگونه می&zwnj;توانید آن را اعتبارسنجی کنید: $request-&gt;validate([ 'user.name' =&gt; 'required', 'user.email' =&gt; 'required|email', ]); ما فضای کافی برای پوشش تمام قوانین اعتبارسنجی ممکن نداریم،...

برای دیدن ادامه محتوا وارد شوید

اعتبارسنجی دستی

اگر در حال کار با یک کنترلر نیستید یا به هر دلیلی جریان کاری که قبلاً توصیف شد برای شما مناسب نیست، می‌توانید به صورت دستی یک نمونه از Validator ایجاد کرده و موفقیت یا شکست آن را مانند مثال 7-20 بررسی کنید.
مثال 7-20. اعتبارسنجی دستی

Route::get('recipes/create', function () {
    return view('recipes.create');
});

Route::post('recipes', function (Illuminate\Http\Request $request) {
    $validator = Validator::make($request->all(), [
        'title' => 'required|unique:recipes|max:125',
        'body' => 'required'
    ]);

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

    // Recipe is valid; proceed to save it
});

همانطور که می‌بینید، ما با ارسال ورودی به عنوان پارامتر اول و قوانین اعتبارسنجی به عنوان پارامتر دوم، یک نمونه از یک اعتبارسنج ایجاد می‌کنیم. اعتبارسنج متد fails() را در اختیار می‌گذارد که می‌توانیم آن را بررسی کنیم و آن را به متد withErrors() در ریدایرکت ارسال کنیم.

استفاده از داده‌های معتبر

پس از اینکه داده‌های خود را اعتبارسنجی کردید، می‌توانید آن‌ها را از درخواست استخراج کنید به طوری که فقط با داده‌های معتبر کار کنید. دو گزینه اصلی وجود دارد: validated() و safe(). شما می‌توانید این متدها را یا روی شیء $request اجرا کنید، یا اگر اعتبارسنجی دستی ایجاد کرده‌اید، روی نمونه $validator.
متد validated() یک آرایه از تمام داده‌هایی که اعتبارسنجی شده‌اند برمی‌گرداند.
مثال 7-21. گرفتن داده‌های معتبر با validated()

// هر دو آرایه‌ای از ورودی‌های معتبر کاربر را برمی‌گردانند
$validated = $request->validated();
$validated = $validator->validated();

از طرفی متد only() یک شیء برمی‌گرداند که به شما دسترسی به متدهای all()، only() و except() می‌دهد، همانطور که در مثال 7-22 می‌بینید.
مثال 7-22. گرفتن داده‌های معتبر با safe()

$validated = $request->safe()->only(['name', 'email']);

$validated = $request->safe()->except(['password']);

$validated = $request->safe()->all();

اشیاء قانون سفارشی

اگر قانون اعتبارسنجی که نیاز دارید در لاراول موجود نیست، می&zwnj;توانید خودتان آن را ایجاد کنید. برای ایجاد یک قانون سفارشی، دستور php artisan make:rule RuleName را اجرا کنید و سپس آن فایل را در مسیر app/Rules/{RuleName}.php ویرایش کنید.شما به طور پیش&zwnj;فرض متد validate() را در کلاس قانون خود دریافت خواهید کرد. متد validate() باید نام ویژگی را به عنوان پارامتر اول، مقدار ارائه&zwnj;شده توسط کاربر را به عنوان پارامتر دوم و یک Closure را به عنوان پارامتر سوم دریافت کند که زمانی که اعتبارسنجی شکست می&zwnj;خورد اجرا می&zwnj;شود؛ می&zwnj;توانید از :attribute به عنوان جایگزین برای نام ویژگی در پیام...

برای دیدن ادامه محتوا وارد شوید

نمایش پیام‌های خطای اعتبارسنجی

ما قبلاً بسیاری از این موارد را در فصل 6 پوشش داده‌ایم، اما در اینجا یک مرور سریع در مورد چگونگی نمایش خطاها از اعتبارسنجی آورده شده است.
متد validate() روی درخواست‌ها (و متد withErrors() روی ریدایرکت‌هایی که به آن وابسته است) هر گونه خطا را به جلسه می‌فرستد. این خطاها برای نمایی که به آن ریدایرکت می‌شوید، در متغیر $errors در دسترس خواهند بود. و به یاد داشته باشید که به عنوان بخشی از جادوی لاراول، متغیر $errors هر بار که نمای را بارگذاری می‌کنید در دسترس خواهد بود، حتی اگر خالی باشد، بنابراین نیازی به بررسی وجود آن با isset() ندارید.
این بدین معنی است که شما می‌توانید چیزی مانند مثال 7-24 را در هر صفحه‌ای انجام دهید.
مثال 7-24. نمایش خطاهای اعتبارسنجی

@if ($errors->any())
    <ul id="errors">
        @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
        @endforeach
    </ul>
@endif

شما همچنین می‌توانید به طور شرطی پیام خطای یک فیلد خاص را نمایش دهید. برای این کار از دستور Blade @error استفاده خواهید کرد تا بررسی کنید که آیا خطایی برای فیلد خاصی وجود دارد یا نه.

@error('first_name')
    <span>{{ $message }}</span>
@enderror

درخواست‌ های فرم

هنگامی که در حال ساخت برنامه&zwnj;های خود هستید، ممکن است شروع به مشاهده برخی الگوها در متدهای کنترلر خود کنید. الگوهایی وجود دارند که تکرار می&zwnj;شوند&mdash;​برای مثال، اعتبارسنجی ورودی، احراز هویت و مجوز کاربر، و ریدایرکت&zwnj;های احتمالی. اگر خود را در موقعیتی پیدا کنید که بخواهید یک ساختار برای نرمال&zwnj;سازی و استخراج این رفتارهای رایج از متدهای کنترلر خود ایجاد کنید، ممکن است به درخواست&zwnj;های فرم لاراول علاقه&zwnj;مند شوید.یک درخواست فرم یک کلاس درخواست سفارشی است که به&zwnj;طور خاص برای ارسال یک فرم طراحی شده است، و مسئولیت اعتبارسنجی درخواست، احراز هویت کاربر و در صورت نیاز، ریدایرکت کردن کاربر...

برای دیدن ادامه محتوا وارد شوید

ایجاد یک درخواست فرم

شما می‌توانید یک درخواست فرم جدید را از خط فرمان ایجاد کنید:

php artisan make:request CreateCommentRequest

اکنون یک شیء درخواست فرم در مسیر app/Http/Requests/CreateCommentRequest.php در دسترس شما است.

هر کلاس درخواست فرم حداقل یکی یا دو متد عمومی دارد. اولی متد rules() است که باید یک آرایه از قوانین اعتبارسنجی برای این درخواست را برگرداند. متد دوم (اختیاری) متد authorize() است؛ اگر این متد true را برگرداند، کاربر مجاز به انجام این درخواست است و اگر false باشد، کاربر رد می‌شود. به مثال 7-25 نگاه کنید تا یک درخواست فرم نمونه را ببینید.
مثال 7-25. درخواست فرم نمونه

<?php

namespace App\Http\Requests;

use App\BlogPost;
use Illuminate\Foundation\Http\FormRequest;

class CreateCommentRequest extends FormRequest
{
    public function authorize(): bool
    {
        $blogPostId = $this->route('blogPost');

        return auth()->check() && BlogPost::where('id', $blogPostId)
            ->where('user_id', auth()->id())->exists();
    }

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

بخش rules() در مثال 7-25 کاملاً واضح است، اما بیایید به طور مختصر به authorize() نگاه کنیم.
ما بخش مسیری را با نام blogPost می‌گیریم. این به این معنی است که احتمالاً تعریف مسیر اینطور به نظر می‌رسد:

Route::post('blogPosts/blogPost', function () { // Do stuff });

همانطور که می‌بینید، ما پارامتر مسیر را blogPost نام‌گذاری کرده‌ایم که باعث می‌شود در درخواست خود با استفاده از $this->route('blogPost') به آن دسترسی داشته باشیم.
سپس بررسی می‌کنیم که آیا کاربر وارد شده است و اگر بله، آیا پست‌های وبلاگی با آن شناسه وجود دارند که متعلق به کاربر وارد شده فعلی باشند. شما قبلاً روش‌های ساده‌تری برای بررسی مالکیت را در فصل 5 یاد گرفته‌اید، اما برای تمیزی بیشتر، اینجا روش واضح‌تری را نگه می‌داریم. ما به زودی تبعات این را پوشش خواهیم داد، اما نکته مهم این است که اگر true برگشت داده شود، یعنی کاربر مجاز به انجام عمل مشخص شده (در این مورد ایجاد یک نظر) است و اگر false برگشت داده شود، یعنی کاربر مجاز نیست.

استفاده از درخواست فرم

حالا که یک شیء درخواست فرم ایجاد کرده‌ایم، چگونه از آن استفاده کنیم؟ این کمی جادوی لاراول است. هر مسیری (بسته یا متد کنترلر) که یک درخواست فرم را به عنوان یکی از پارامترهای خود تایپ کرده باشد، از تعریف آن درخواست فرم بهره‌مند خواهد شد.
بیایید آن را در مثال 7-26 امتحان کنیم.
مثال 7-26. استفاده از درخواست فرم

Route::post('comments', function (App\Http\Requests\CreateCommentRequest $request) {
    // Store comment
});

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

 

تخصیص انبوه مدل Eloquent

تا حالا، ما به اعتبارسنجی در سطح کنترلر نگاه کرده&zwnj;ایم که قطعاً بهترین مکان برای شروع است. اما شما همچنین می&zwnj;توانید داده&zwnj;های ورودی را در سطح مدل فیلتر کنید.این یک الگوی رایج (اما نه توصیه&zwnj;شده) است که تمام ورودی یک فرم را مستقیماً به یک مدل پایگاه داده ارسال کنید. در لاراول، این ممکن است مانند مثال 7-27 به نظر برسد.مثال 7-27. ارسال کامل یک فرم به یک مدل Eloquent Route::post('posts', function (Request $request) { $newPost = Post::create($request-&gt;all()); }); ما اینجا فرض می&zwnj;کنیم که کاربر نهایی مهربان است و قصد مخربانه ندارد، و تنها فیلدهایی را که می&zwnj;خواهیم ویرایش کنند،...

برای دیدن ادامه محتوا وارد شوید

{{ در مقابل {!!

هر زمان که محتوایی را که توسط کاربر ایجاد شده است در یک صفحه وب نمایش می‌دهید، باید در برابر ورودی‌های مخرب، مانند تزریق اسکریپت محافظت کنید.
فرض کنید به کاربران خود اجازه می‌دهید تا پست‌های وبلاگ بنویسند. احتمالاً نمی‌خواهید که آنها بتوانند جاوااسکریپت مخربی را تزریق کنند که در مرورگرهای بازدیدکنندگان شما به‌طور غیرمنتظره اجرا شود، درست است؟ بنابراین، باید هر ورودی کاربر را که در صفحه نمایش می‌دهید، از نظر امنیتی_escape کنید تا از این موضوع جلوگیری کنید.
خوشبختانه، این تقریباً به‌طور کامل برای شما انجام شده است. اگر از موتور قالب‌بندی Blade لاراول استفاده کنید، دستور پیش‌فرض "echo" ({{ $stuffToEcho }}) به‌طور خودکار خروجی را از طریق htmlentities() (بهترین روش PHP برای ایمن کردن محتوای کاربر جهت نمایش) می‌برد. در واقع شما باید کار اضافی انجام دهید تا از فرار دادن خروجی جلوگیری کنید، با استفاده از دستور {!! $stuffToEcho !!}.

تست

اگر به آزمایش تعاملات خود با ورودی‌های کاربر علاقه‌مند هستید، احتمالاً بیشتر به شبیه‌سازی ورودی‌های معتبر و نامعتبر کاربر و اطمینان از این که اگر ورودی نامعتبر باشد، کاربر ریدایرکت شود، و اگر ورودی معتبر باشد، در مکان مناسب قرار گیرد (برای مثال، در پایگاه داده) علاقه دارید.
چارچوب آزمایش‌های اپلیکیشن لاراول این کار را ساده می‌کند.

لاراول داسک برای آزمایش تعاملات کاربر

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

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

بیایید با یک مسیر نامعتبر شروع کنیم که انتظار داریم رد شود، مانند مثال 7-29.
مثال 7-29. آزمایش اینکه ورودی نامعتبر رد می‌شود

public function test_input_missing_a_title_is_rejected()
{
    $response = $this->post('posts', ['body' => 'This is the body of my post']);
    $response->assertRedirect();
    $response->assertSessionHasErrors();
}

در اینجا ما بررسی می‌کنیم که پس از ورودی نامعتبر، کاربر ریدایرکت می‌شود، با خطاهایی که پیوست شده‌اند. می‌بینید که ما از برخی از تأییدهای PHPUnit سفارشی که لاراول اینجا اضافه کرده است استفاده می‌کنیم.
پس، چگونه موفقیت مسیر خود را آزمایش کنیم؟ به مثال 7-30 نگاه کنید.
مثال 7-30. آزمایش اینکه ورودی معتبر پردازش می‌شود

public function test_valid_input_should_create_a_post_in_the_database()
{
    $this->post('posts', ['title' => 'Post Title', 'body' => 'This is the body']);
    $this->assertDatabaseHas('posts', ['title' => 'Post Title']);
}

توجه داشته باشید که اگر در حال آزمایش چیزی با استفاده از پایگاه داده هستید، باید بیشتر در مورد مایگریشن‌ها و تراکنش‌های پایگاه داده یاد بگیرید. بیشتر در این مورد در فصل 12 بخوانید.

خلاصه

راه‌های زیادی برای به‌دست آوردن همان داده‌ها وجود دارد: استفاده از فساد Request، استفاده از هلپر سراسری request() و تزریق یک نمونه از Illuminate\Http\Request. هرکدام از این‌ها قابلیت دریافت تمام ورودی، برخی ورودی‌ها، یا بخش‌های خاصی از داده‌ها را ارائه می‌دهند، و ممکن است برخی ملاحظات ویژه برای فایل‌ها و ورودی‌های JSON وجود داشته باشد.
بخش‌های مسیر URI نیز می‌توانند منبع ورودی کاربر باشند و این‌ها نیز از طریق ابزارهای درخواست قابل دسترسی هستند.
اعتبارسنجی می‌تواند به‌صورت دستی با استفاده از Validator::make() یا به‌طور خودکار با استفاده از متد validate() درخواست یا درخواست‌های فرم انجام شود. هر ابزار خودکار، پس از شکست اعتبارسنجی، کاربر را به صفحه قبلی با ورودی‌های قدیمی ذخیره‌شده و خطاها منتقل می‌کند.
ویوها و مدل‌های Eloquent نیز باید در برابر ورودی‌های مخرب کاربر محافظت شوند. ویوهای Blade را با استفاده از دستور براکت‌های دوگانه ({{ }}) که ورودی کاربر را فرار می‌دهند، محافظت کنید و مدل‌ها را با ارسال تنها فیلدهای خاص به متدهای انبوه با استفاده از $request->only() و تعریف قوانین تخصیص انبوه در خود مدل محافظت کنید.