فصل سوم - مسیرها و کنترلرها

مسیرها و کنترلرها

وظیفه اساسی هر فریمورک اپلیکیشن وب این است که درخواست‌ها را از کاربر دریافت کرده و پاسخ‌ها را معمولاً از طریق HTTP(S) ارسال کند. این بدین معناست که تعریف مسیرهای یک اپلیکیشن اولین و مهم‌ترین پروژه‌ای است که هنگام یادگیری یک فریمورک وب باید به آن پرداخته شود؛ بدون مسیرها، شما توانایی کمی برای تعامل با کاربر نهایی دارید.
در این فصل، ما به بررسی مسیرها در لاراول خواهیم پرداخت؛ نحوه تعریف آن‌ها، نحوه ارجاع به کدی که باید اجرا شود و نحوه استفاده از ابزارهای مسیریابی لاراول برای مدیریت مجموعه‌ای متنوع از نیازهای مسیریابی را خواهید دید.

مقدمه‌ ای سریع به MVC، افعال HTTP و REST

بیشتر آنچه که در این فصل به آن اشاره خواهیم کرد مربوط به ساختار اپلیکیشن‌های مدل-نما-کنترلر (MVC) است و بسیاری از مثال‌هایی که خواهیم دید از نام‌ها و افعال مسیریابی مشابه REST استفاده می‌کنند، بنابراین بیایید یک نگاه سریع به هر دو داشته باشیم.

MVC چیست؟

در MVC، سه مفهوم اصلی وجود دارد:
مدل (Model)

 نمایانگر یک جدول خاص از پایگاه داده (یا یک رکورد از آن جدول) است—به طور مثال "شرکت" یا "سگ".

نما (View)

 نمایانگر الگوی (template) است که داده‌ها را به کاربر نهایی نمایش می‌دهد—به طور مثال "الگوی صفحه ورود با این مجموعه از HTML، CSS و JavaScript".

کنترلر (Controller)

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


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


شکل 3-1. یک تصویر ساده از معماری MVC
ما برخی از موارد استفاده لاراول را بررسی خواهیم کرد که با این نگاه نسبتاً ساده به معماری اپلیکیشن سازگار نیستند، بنابراین به MVC زیاد گیر نکنید، اما این حداقل شما را آماده می‌کند تا به باقی فصل نگاه کنید، وقتی که در مورد نماها و کنترلرها صحبت می‌کنیم.

انواع HTTP

انواع HTTPرایج‌ترین افعال HTTP عبارتند از GET و POST، به دنبال آن‌ها PUT و DELETE قرار دارند. همچنین HEAD، OPTIONS و PATCH نیز وجود دارند، و دو مورد دیگر که در توسعه وب معمولاً استفاده نمی‌شوند، TRACE و CONNECT.در اینجا یک توضیح سریع آورده شده است:GET  درخواست یک منبع (یا یک فهرست از منابع). HEAD  درخواست نسخه‌ای از پاسخ GET فقط با هدرها. POST  ایجاد یک منبع. PUT  بازنویسی یک منبع. PATCH  اصلاح یک منبع. DELETE حذف یک منبع. OPTIONS پرسش از سرور در مورد افعالی که در این URL مجاز هستند.   جدول 3-1 افعال موجود در کنترلر منابع...

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

REST چیست؟

ما REST را به طور مفصل‌تر در "مبانی APIهای شبیه به REST با فرمت JSON" در صفحه 345 پوشش خواهیم داد، اما به طور مختصر، این یک سبک معماری برای ساخت APIها است. زمانی که در این کتاب در مورد REST صحبت می‌کنیم، بیشتر به چند ویژگی اشاره خواهیم کرد، از جمله:

    • ساختار آن حول یک منبع اصلی در هر زمان (مثلاً tasks)

    • تعاملات با استفاده از ساختارهای URL قابل پیش‌بینی و افعال HTTP (همانطور که در جدول 3-1 مشاهده می‌کنید)

    • بازگشت داده‌ها به فرمت JSON و معمولاً درخواست آن‌ها با فرمت JSON

این موارد بیشتر هستند، اما معمولاً "RESTful" به معنای "الگوبرداری از این ساختارهای مبتنی بر URL به گونه‌ای که بتوانیم درخواست‌های قابل پیش‌بینی مانند GET /tasks/14/edit برای صفحه ویرایش داشته باشیم" است. این موضوع حتی زمانی که API نمی‌سازیم هم مهم است، زیرا ساختارهای مسیریابی لاراول بر اساس ساختارهای شبیه به REST است، همانطور که در جدول 3-1 مشاهده می‌کنید.

 

APIهای مبتنی بر REST عمدتاً از همین ساختار پیروی می‌کنند، با این تفاوت که آن‌ها مسیری برای ایجاد یا ویرایش ندارند، زیرا APIها فقط اقداماتی را نمایندگی می‌کنند، نه صفحاتی که برای این اقدامات آماده می‌شوند.

 

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

در یک اپلیکیشن لاراول، شما مسیریابی‌های وب خود را در فایل routes/web.php و مسیریابی‌های API خود را در فایل routes/api.php تعریف خواهید کرد. مسیریابی‌های وب، مسیریابی‌هایی هستند که توسط کاربران نهایی شما بازدید خواهند شد؛ مسیریابی‌های API، مسیریابی‌هایی هستند که برای API شما هستند، اگر چنین چیزی داشته باشید. در حال حاضر، ما بیشتر روی مسیریابی‌های موجود در routes/web.php تمرکز خواهیم کرد.ساده‌ترین راه برای تعریف یک مسیر، تطبیق یک مسیر (مثلاً /) با یک closure است، همانطور که در مثال 3-1 مشاهده می‌کنید.مثال 3-1. تعریف مسیر پایه // routes/web.php Route::get('/', function () { return 'Hello, World!'; });   Closure چیست؟...

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

افعال مسیریابی

شاید متوجه شده باشید که در تعریف مسیرها از Route::get() استفاده کرده‌ایم. این بدین معنی است که ما به لاراول گفته‌ایم که این مسیرها را تنها زمانی تطبیق دهد که درخواست HTTP از روش GET استفاده کند. اما اگر یک فرم با روش POST باشد، یا شاید برخی از درخواست‌های JavaScript با روش‌های PUT یا DELETE ارسال شوند؟ چند گزینه دیگر برای فراخوانی متدها در تعریف مسیر وجود دارد که در مثال 3-3 نشان داده شده است.

Route::get('/', function () {
    return 'Hello, World!';
});

Route::post('/', function () {
    // رسیدگی به زمانی که کسی یک درخواست POST به این مسیر ارسال کند
});

Route::put('/', function () {
    // رسیدگی به زمانی که کسی یک درخواست PUT به این مسیر ارسال کن
});

Route::delete('/', function () {
    // رسیدگی به زمانی که کسی یک درخواست DELETE به این مسیر ارسال کند
});

Route::any('/', function () {
    // رسیدگی به هر نوع درخواست (با هر متدی) به این مسیر
});

Route::match(['get', 'post'], '/', function () {
    // رسیدگی به درخواست‌های GET یا POST به این مسیر
});

مدیریت مسیرها

همانطور که احتمالاً حدس زده‌اید، ارسال یک closure به تعریف مسیر تنها راهی نیست که می‌توانید به لاراول بگویید چطور مسیر را حل کند. closures سریع و ساده هستند، اما هر چه اپلیکیشن شما بزرگتر شود، قرار دادن تمام منطق مسیریابی در یک فایل دشوارتر می‌شود. علاوه بر این، برنامه‌هایی که از route closures استفاده می‌کنند نمی‌توانند از کش مسیریابی لاراول بهره‌مند شوند (در مورد آن بعداً صحبت خواهیم کرد)، که می‌تواند تا صدها میلی‌ثانیه از زمان هر درخواست کم کند.
گزینه رایج دیگر این است که به جای closure، نام کنترلر و متد آن را به عنوان یک رشته ارسال کنید، مانند مثال 3-4.
مثال 3-4. مسیرهایی که متدهای کنترلر را فراخوانی می‌کنند

use App\Http\Controllers\WelcomeController;

Route::get('/', [WelcomeController::class, 'index']);

این به لاراول می‌گوید که درخواست‌های مربوط به این مسیر را به متد index() از کنترلر App\Http\Controllers\WelcomeController ارجاع دهد. این متد همان پارامترها را دریافت خواهد کرد و دقیقاً مشابه closure که به‌جای آن ممکن بود قرار دهید، رفتار خواهد کرد.

 

نحوه ارجاع به متدهای کنترلر در لاراول

لاراول یک ساختار برای ارجاع به متد خاصی در یک کنترلر خاص دارد:
 [ControllerName::class, methodName] که به آن "نحو تاپل" یا "نحو آرایه قابل فراخوانی" گفته می‌شود. گاهی اوقات این فقط یک قرارداد ارتباطی ساده است، اما در موارد واقعی، مانند مثال 3-4، برای پیوندها نیز استفاده می‌شود. اولین آیتم در آرایه، کنترلر و دومین آیتم، متد را شناسایی می‌کند.
لاراول همچنین از نحو قدیمی‌تری به نام "رشته‌ای" پشتیبانی می‌کند (مثل Route::get('/', 'WelcomeController@index'))، و این هنوز یک روش رایج برای توصیف یک متد در ارتباطات نوشتاری است.

پارامترهای مسیر

اگر مسیری که تعریف می‌کنید دارای پارامترها باشد—یعنی بخش‌هایی از ساختار URL که متغیر هستند—تعریف آن‌ها در مسیر و ارسال آن‌ها به closure بسیار ساده است (مانند مثال 3-5). مثال 3-5. پارامترهای مسیر Route::get('users/{id}/friends', function ($id) { // }); شما همچنین می‌توانید پارامترهای مسیر خود را اختیاری کنید با اضافه کردن علامت سوال (?) بعد از نام پارامتر، همانطور که در مثال 3-6 نشان داده شده است. در این حالت، باید مقداری پیش‌فرض برای متغیر مربوط به مسیر تعیین کنید.مثال 3-6. پارامترهای اختیاری مسیر Route::get('users/{id?}', function ($id = 'fallbackId') { // }); همچنین می‌توانید از عبارات منظم (Regex) برای تعیین...

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

نام‌ گذاری مسیرها

ساده‌ترین روش برای ارجاع به این مسیرها در سایر نقاط برنامه، استفاده از مسیر آن‌ها است. Laravel یک تابع کمکی url() ارائه می‌دهد که این پیوندها را در نمایش‌هایتان ساده‌تر می‌کند، اگر به آن نیاز داشته باشید؛ به عنوان مثال در مثال 3-9 مشاهده کنید. این تابع کمکی مسیر شما را با دامنه کامل سایتتان پیشوند می‌دهد.
مثال 3-9. تابع کمکی url()

 

<a href="<?php echo url('/'); ?>">
// خروجی: <a href="http://myapp.com/">

با این حال، Laravel به شما این امکان را می‌دهد که برای هر مسیر یک نام مشخص کنید، که به شما اجازه می‌دهد به آن بدون نیاز به ارجاع صریح به URL، اشاره کنید. این کار مفید است زیرا به شما این امکان را می‌دهد که برای مسیرهای پیچیده نام‌های ساده بدهید و همچنین با ارجاع به آن‌ها با نام، دیگر نیازی به نوشتن دوباره پیوندهای فرانت‌اند نخواهید داشت اگر مسیرها تغییر کنند (به مثال 3-10 مراجعه کنید).
مثال 3-10. تعریف نام مسیرها

// تعریف یک مسیر با استفاده از name() در routes/web.php:
Route::get('members/{id}', [\App\Http\Controller\MemberController::class, 'show'])
    ->name('members.show');


// لینک دادن به مسیر در یک نمایش با استفاده از تابع کمکی route():
<a href="<?php echo route('members.show', ['id' => 14]); ?>">

این مثال چند مفهوم جدید را معرفی می‌کند. اولاً، ما از تعریف روان مسیرها استفاده می‌کنیم تا نام را با استفاده از روش name() پس از متد get() اضافه کنیم. این روش به ما اجازه می‌دهد تا نام مسیر را تعیین کنیم و آن را به یک مستعار کوتاه تبدیل کنیم تا ارجاع به آن در سایر بخش‌ها راحت‌تر باشد.
در مثال ما، نام این مسیر members.show است؛ استفاده از resourcePlural.action یک عرف رایج در Laravel برای نام‌گذاری مسیرها و نمایش‌ها است.
عرف‌های نام‌گذاری مسیرها
شما می‌توانید هر نامی برای مسیر خود انتخاب کنید، اما عرف رایج این است که از شکل جمع نام منبع استفاده کنید، سپس یک نقطه و در نهایت عمل مورد نظر را بنویسید. بنابراین، در اینجا مسیرهایی که معمولاً برای یک منبع با نام "photo" استفاده می‌شوند آورده شده است:


photos.index
photos.create
photos.store
photos.show
photos.edit
Photos.update
photos.destroy

برای یادگیری بیشتر در مورد این عرف‌ها، به فصل "Resource Controllers" در صفحه 45 مراجعه کنید.
این مثال همچنین معرفی کننده‌ی هلیپر route() است. درست مانند url()، این هلیپر برای استفاده در ویوها طراحی شده است تا لینک‌دهی به یک مسیر نام‌گذاری‌شده را ساده‌تر کند. اگر مسیر پارامتر نداشته باشد، می‌توانید به سادگی نام مسیر را به آن بدهید (route('members.index')) و یک رشته مسیر دریافت خواهید کرد ("http://myapp.com/members"). اگر مسیر پارامترهایی داشته باشد، آنها را به صورت یک آرایه در پارامتر دوم مانند مثال 3-10 ارسال کنید. به طور کلی، من توصیه می‌کنم که به جای استفاده از مسیرها از نام مسیرها برای ارجاع به مسیرها استفاده کنید و به همین دلیل از هلیپر route() به جای url() استفاده کنید. گاهی اوقات ممکن است کمی پیچیده شود—برای مثال، اگر با زیر دامنه‌های متعدد کار می‌کنید— اما این روش انعطاف‌پذیری بسیار زیادی فراهم می‌کند تا ساختار مسیریابی برنامه را بدون هزینه زیاد تغییر دهید.
انتقال پارامترهای مسیر به هلیپر route()
زمانی که مسیر شما پارامترهایی دارد (مثلاً users/id)، باید این پارامترها را هنگام استفاده از هلیپر route() برای تولید لینک به مسیر مشخص کنید.
چندین روش مختلف برای ارسال این پارامترها وجود دارد. بیایید یک مسیر تعریف شده به صورت users/userId/comments/commentId را فرض کنیم. اگر شناسه کاربر 1 و شناسه کامنت 2 باشد، چند گزینه مختلف که در اختیار داریم را بررسی می‌کنیم:

گزینه 1:

route('users.comments.show', [1, 2])
// http://myapp.com/users/1/comments/2


گزینه 2:

route('users.comments.show', ['userId' => 1, 'commentId' => 2])
// http://myapp.com/users/1/comments/2


گزینه 3:

route('users.comments.show', ['commentId' => 2, 'userId' => 1])
// http://myapp.com/users/1/comments/2

 

گزینه 4:

route('users.comments.show', ['userId' => 1, 'commentId' => 2, 'opt' => 'a'])
// http://myapp.com/users/1/comments/2?opt=a


همانطور که می‌بینید، مقادیر آرایه‌های بدون کلید به ترتیب اختصاص داده می‌شوند؛ مقادیر آرایه‌های با کلید با پارامترهای مسیر که با کلیدهای آنها مطابقت دارند، تطابق داده می‌شوند و هر چیزی که باقی می‌ماند به عنوان پارامتر پرس و جو (query parameter) اضافه می‌شود.

گروه‌های مسیر (Route Groups)

اغلب یک گروه از مسیرها ویژگی خاصی را به اشتراک می‌گذارند—مثلاً نیاز به احراز هویت خاصی، پیشوند مسیر، یا شاید فضای نام کنترلر. تعریف دوباره این ویژگی‌های مشترک در هر مسیر نه تنها خسته‌کننده به نظر می‌رسد بلکه می‌تواند شکل فایل مسیرهای شما را به هم بریزد و برخی از ساختارهای برنامه شما را مبهم کند.
گروه‌های مسیر این امکان را فراهم می‌کنند که با گروه‌بندی چندین مسیر با هم و اعمال تنظیمات پیکربندی مشترک به یکباره به کل گروه، این تکرار را کاهش دهید. علاوه بر این، گروه‌های مسیر نشانه‌های بصری برای توسعه‌دهندگان آینده (و برای مغز خودتان) هستند که نشان می‌دهند این مسیرها با هم گروه‌بندی شده‌اند.
برای گروه‌بندی دو یا چند مسیر، شما مسیرها را با یک گروه مسیر احاطه می‌کنید، همانطور که در مثال 3-11 نشان داده شده است. در واقع، شما یک closure به تعریف گروه می‌دهید و مسیرهای گروه‌بندی‌شده را در داخل آن closure تعریف می‌کنید.

مثال 3-11. تعریف یک گروه مسیر:

Route::group(function () {
    Route::get('hello', function () {
        return 'Hello';
    });
    Route::get('world', function () {
        return 'World';
    });
});

به طور پیش‌فرض، یک گروه مسیر در واقع کاری انجام نمی‌دهد. هیچ تفاوتی بین استفاده از گروه در مثال 3-11 و جدا کردن یک بخش از مسیرها با نظرات کد وجود ندارد.

میانه‌ افزار (Middleware)

احتمالاً رایج&zwnj;ترین استفاده از گروه&zwnj;های مسیر، اعمال میانه&zwnj;افزار به یک گروه از مسیرها است. شما بیشتر درباره میانه&zwnj;افزار در فصل ۱۰ خواهید آموخت، اما در میان سایر موارد، میانه&zwnj;افزارها ابزارهایی هستند که لاراول برای احراز هویت کاربران و محدود کردن دسترسی کاربران مهمان به بخش&zwnj;های خاصی از سایت استفاده می&zwnj;کند.در مثال 3-12، ما یک گروه مسیر حول داشبورد و صفحات حساب کاربری ایجاد کرده&zwnj;ایم و میانه&zwnj;افزار auth را به هر دو اعمال کرده&zwnj;ایم. در این مثال، این بدان معناست که کاربران باید وارد برنامه شوند تا بتوانند صفحه داشبورد یا صفحه حساب کاربری را مشاهده کنند.مثال 3-12. محدود کردن یک...

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

پیشوندهای مسیر

اگر یک گروه از مسیرها دارید که بخشی از مسیرشان مشابه است—for example, if your site’s dashboard is prefixed with /dashboard—می‌توانید از گروه‌های مسیر برای ساده‌سازی این ساختار استفاده کنید (مشاهده کنید مثال 3-13).
مثال 3-13. افزودن پیشوند به یک گروه از مسیرها

Route::prefix('dashboard')->group(function () {
    Route::get('/', function () {
        // Handles the path /dashboard
    });
    Route::get('users', function () {
        // Handles the path /dashboard/users
    });
});

توجه داشته باشید که هر گروهی که پیشوند دارد، یک مسیر / نیز دارد که نمایانگر ریشه پیشوند است—in Example 3-13 that’s /dashboard.

 

مسیر‌یابی زیر دامنه‌ها

مسیر&zwnj;یابی زیر دامنه&zwnj;ها مشابه افزودن پیشوند به مسیر است، اما این&zwnj;بار با توجه به زیر دامنه تعریف می&zwnj;شود. دو کاربرد اصلی برای این موضوع وجود دارد. اول، ممکن است بخواهید بخش&zwnj;های مختلفی از اپلیکیشن را (یا حتی اپلیکیشن&zwnj;های کاملاً متفاوت) برای زیر دامنه&zwnj;های مختلف نمایش دهید. مثال 3-14 نشان می&zwnj;دهد که چگونه می&zwnj;توانید این کار را انجام دهید.مثال 3-14. مسیر&zwnj;یابی زیر دامنه&zwnj;ها Route::domain('api.myapp.com')-&gt;group(function () { Route::get('/', function () { // }); }); دوم، ممکن است بخواهید بخشی از زیر دامنه را به عنوان پارامتر تنظیم کنید، همانطور که در مثال 3-15 نشان داده شده است. این بیشتر در موارد چند...

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

پیشوندهای نام مسیر

اینکه نام مسیرها بازتاب‌دهنده زنجیره ارث‌بری عناصر مسیر باشد امری رایج است، بنابراین مسیر users/comments/5 توسط مسیری به نام users.comments.show پاسخ داده می‌شود. در این حالت، معمول است که از یک گروه مسیر برای تمام مسیرهایی که زیرمنطقه منابع users.comments هستند استفاده شود.
دقیقا همانطور که می‌توانیم پیشوندهایی را برای بخش‌های URL اضافه کنیم، می‌توانیم پیشوندهایی را نیز به نام مسیر اضافه کنیم. با پیشوندهای نام گروه مسیر، می‌توانیم تعریف کنیم که هر مسیری در این گروه باید یک رشته خاص را به نام خود اضافه کند. در این زمینه، ما "users." را به هر نام مسیر اضافه می‌کنیم، سپس "comments." (مشاهده کنید مثال 3-16).
مثال 3-16. پیشوندهای نام گروه مسیر

Route::name('users.')->prefix('users')->group(function () {
    Route::name('comments.')->prefix('comments')->group(function () {
        Route::get('{id}', function () {
            // ...
        })->name('show');
    });
});

 

گروه مسیرهای کنترلر

زمانی که مسیرهایی را گروه‌بندی می‌کنید که توسط همان کنترلر پاسخ داده می‌شوند، مانند زمانی که ما در حال نمایش، ویرایش و حذف کاربران هستیم، می‌توانیم از متد controller() گروه مسیر استفاده کنیم، همانطور که در مثال 3-17 نشان داده شده است، تا نیازی به تعریف کامل زوج نام کنترلر و متد برای هر مسیر نباشد.
مثال 3-17. گروه مسیرهای کنترلر

use App/Http/Controllers/UserController;

Route::controller(UserController::class)->group(function () {
    Route::get('/', 'index');
    Route::get('{id}', 'show');
});

مسیرهای پیش‌فرض (Fallback Routes)

در لاراول می‌توانید یک "مسیر پیش‌فرض" تعریف کنید (که باید در انتهای فایل مسیرها قرار بگیرد) تا درخواست‌های غیرمستقیم را شناسایی کند:

Route::fallback(function () {
    //
});

مسیرهای امضا شده

بسیاری از برنامه&zwnj;ها به طور مرتب اطلاعیه&zwnj;هایی را در مورد اقدام&zwnj;های یک&zwnj;باره (مانند بازنشانی رمز عبور، پذیرش دعوت&zwnj;نامه و غیره) ارسال می&zwnj;کنند و لینک&zwnj;های ساده&zwnj;ای برای انجام آن اقدامات فراهم می&zwnj;آورند. فرض کنید یک ایمیل ارسال می&zwnj;کنیم که تأیید کند گیرنده موافقت کرده است که به یک لیست ایمیل اضافه شود.سه راه برای ارسال آن لینک وجود دارد: &bull; این URL را عمومی کنید و امیدوار باشید که هیچ&zwnj;کس URL تأیید را کشف نکند یا URL تأیید خود را تغییر ندهد تا شخص دیگری را تأیید کند. &bull; اقدام را پشت احراز هویت قرار دهید، به اقدام لینک دهید و...

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

امضای یک مسیر

برای ساخت یک URL امضاشده جهت دسترسی به یک مسیر خاص، مسیر باید نام داشته باشد:

Route::get('invitations/{invitation}/{answer}', InvitationController::class)->name('invitations');


برای تولید یک لینک عادی به این مسیر، شما می‌توانید از کمک‌فن‌آوری route() استفاده کنید، همانطور که قبلاً توضیح داده‌ایم، اما می‌توانید از فاساد URL نیز برای انجام همان کار استفاده کنید:

URL::route('invitations', ['invitation' => 12345, 'answer' => 'yes']);

برای تولید یک لینک امضاشده به این مسیر، به سادگی از متد signedRoute() استفاده کنید.
و اگر می‌خواهید یک مسیر امضاشده با تاریخ انقضا ایجاد کنید، از temporarySignedRoute() استفاده کنید:

// تولید یک لینک عادی
URL::route('invitations', ['invitation' => 12345, 'answer' => 'yes']);


// تولید یک لینک امضاشده
URL::signedRoute('invitations', ['invitation' => 12345, 'answer' => 'yes']);


// تولید یک لینک امضاشده موقت (با تاریخ انقضا)
URL::temporarySignedRoute(
    'invitations',
    now()->addHours(4),
    ['invitation' => 12345, 'answer' => 'yes']
);

 

استفاده از کمک‌فن‌آوری now()

لاراول یک کمک‌فن‌آوری now() ارائه می‌دهد که معادل Carbon::now() است؛ این کمک‌فن‌آوری یک شیء Carbon برمی‌گرداند که نمایان‌گر تاریخ و زمان کنونی است.

Carbon یک کتابخانه تاریخ و زمان است که با لاراول گنجانده شده است.

مدیریت تغییرات در مسیرها برای لینک‌های امضا شده

حالا که یک لینک به مسیر امضا شده تولید کرده‌اید، باید در برابر دسترسی‌های بدون امضا محافظت کنید. ساده‌ترین گزینه استفاده از میانه‌افزار signed است:

Route::get('invitations/{invitation}/{answer}', InvitationController::class)
    ->name('invitations')
    ->middleware('signed');

اگر ترجیح می‌دهید، می‌توانید به‌طور دستی از روش hasValidSignature() در شیء Request برای اعتبارسنجی استفاده کنید به جای استفاده از میانه‌افزار signed:

class InvitationController
{
    public function __invoke(Invitation $invitation, $answer, Request $request)
    {
        if (! $request->hasValidSignature()) {
            abort(403);
        }

        //
    }
}

ویوها

در چندین کلوژری که در مسیرها مشاهده کردیم، چیزی شبیه به return view('account') دیده&zwnj;ایم. این چیست؟در الگوی MVC (شکل 3-1)، ویوها (یا قالب&zwnj;ها) فایل&zwnj;هایی هستند که توصیف می&zwnj;کنند یک خروجی خاص چگونه باید باشد. شما ممکن است ویوهایی داشته باشید که خروجی JSON یا XML یا ایمیل تولید کنند، اما رایج&zwnj;ترین ویوها در یک فریم&zwnj;ورک وب، HTML هستند.در لاراول، دو فرمت ویو وجود دارد که به&zwnj;طور پیش&zwnj;فرض می&zwnj;توانید از آن&zwnj;ها استفاده کنید: PHP ساده و قالب&zwnj;های Blade (فصل 4). تفاوت آن&zwnj;ها در نام فایل است: about.php با موتور PHP رندر می&zwnj;شود و about.blade.php با موتور Blade رندر می&zwnj;شود. &nbsp; سه...

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

بازگشت مسیرهای ساده به صورت مستقیم با استفاده از Route::view()

 از آنجا که بسیار رایج است که یک مسیر فقط ویو را بدون داده‌های سفارشی بازگشت دهد، لاراول این امکان را می‌دهد که یک مسیر به عنوان مسیر "ویو" تعریف شود بدون اینکه حتی نیاز به ارسال یک بستن مسیر یا ارجاع به کنترلر/متد داشته باشد، مانند مثال 3-20.
مثال 3-20. استفاده از Route::view()

// بازگشت به resources/views/welcome.blade.php
Route::view('/', 'welcome');


// ارسال داده ساده به Route::view()
Route::view()
Route::view('/', 'welcome', ['User' => 'Michael']);

 

استفاده از View Composers برای اشتراک‌گذاری متغیرها با هر ویو

 گاهی اوقات ارسال مکرر یک متغیر می‌تواند دردسرساز شود. ممکن است متغیری وجود داشته باشد که بخواهید در تمام ویوهای سایت یا در دسته خاصی از ویوها یا یک زیرویو خاص در دسترس باشد—برای مثال، تمام ویوهای مربوط به وظایف یا partial مربوط به header.

امکان به‌اشتراک‌گذاری برخی متغیرها با تمام templateها یا فقط templateهای خاص وجود دارد، مانند کد زیر:

view()->share('variableName', 'variableValue');

برای یادگیری بیشتر، به بخش "View Composers and Service Injection"  مراجعه کنید.

کنترلرها

چندین بار به کنترلرها اشاره کرده&zwnj;ام، اما تا اینجا بیشتر مثال&zwnj;ها از route closures استفاده کرده&zwnj;اند. در الگوی MVC، کنترلرها در اصل کلاس&zwnj;هایی هستند که منطق مربوط به یک یا چند route را در یک مکان سازمان&zwnj;دهی می&zwnj;کنند. کنترلرها معمولاً routeهای مشابه را در کنار هم قرار می&zwnj;دهند، به&zwnj;ویژه اگر اپلیکیشن شما به&zwnj;صورت سنتی و شبیه به CRUD ساختار یافته باشد؛ در این صورت، یک کنترلر ممکن است تمام عملیات قابل انجام روی یک resource خاص را مدیریت کند. CRUD چیست؟ CRUD مخفف چهار عملیات اصلی است: create (ایجاد)، read (خواندن)، update (به&zwnj;روزرسانی)، delete (حذف)، که معمولاً متداول&zwnj;ترین عملیات&zwnj;هایی هستند...

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

گرفتن ورودی کاربر

دومین عمل رایج در متدهای کنترلر، گرفتن ورودی از کاربر و عمل کردن بر روی آن است. این موضوع چند مفهوم جدید را معرفی می‌کند، بنابراین بیایید نگاهی به یک کد نمونه بیندازیم و قطعات جدید را بررسی کنیم.
اول، مسیر خود را متصل می‌کنیم؛ به مثال 3-25 نگاه کنید.
مثال 3-25. اتصال عملیات فرم پایه

// routes/web.php
Route::get('tasks/create', [TaskController::class, 'create']);
Route::post('tasks', [TaskController::class, 'store']);

توجه کنید که ما عملیات GET برای tasks/create (که فرم ایجاد یک وظیفه جدید را نشان می‌دهد) و عملیات POST برای tasks (که داده‌های فرم ما به آنجا ارسال می‌شود زمانی که وظیفه جدیدی ایجاد می‌کنیم) را متصل کرده‌ایم. می‌توانیم فرض کنیم که متد create() در کنترلر ما فقط فرم را نمایش می‌دهد، پس بیایید نگاهی به متد store() در مثال 3-26 بیندازیم.
 مثال 3-26. متد کنترلر ورودی فرم رایج

// TaskController.php
...
public function store()
{
    Task::create(request()->only(['title', 'description']));

    return redirect('tasks');
}

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

ما از کمک‌فن request() برای نمایش درخواست HTTP استفاده می‌کنیم (و در ادامه بیشتر در مورد آن صحبت خواهیم کرد) و از متد only() آن برای دریافت فقط فیلدهای عنوان و توضیحات که کاربر ارسال کرده استفاده می‌کنیم.
سپس این داده‌ها را به متد create() مدل Task خود ارسال می‌کنیم که یک نمونه جدید از Task را ایجاد می‌کند با این تفاوت که عنوان به عنوان عنوان ورودی و توضیحات به عنوان توضیحات ورودی تنظیم می‌شود. در نهایت، ما کاربر را به صفحه‌ای که تمام وظایف را نمایش می‌دهد هدایت می‌کنیم.
در اینجا چند لایه انتزاع وجود دارد که در ادامه به آن‌ها خواهیم پرداخت، اما بدانید که داده‌هایی که از متد only() می‌آید از همان مجموعه داده‌ای است که تمامی متدهای رایج مورد استفاده در شیء Request از آن استفاده می‌کنند، از جمله all() و get(). مجموعه داده‌ای که هرکدام از این متدها از آن می‌کشند، تمامی داده‌های ارائه شده توسط کاربر را شامل می‌شود، چه از پارامترهای query یا مقادیر POST. پس کاربر دو فیلد در صفحه "افزودن وظیفه" پر کرده است: "عنوان" و "توضیحات."
برای شکستن کمی از این انتزاع، request()->only() یک آرایه انجمنی از نام‌های ورودی را گرفته و آن‌ها را بازمی‌گرداند:

request()->only(['title', 'description']);
// returns:
[
    'title' => 'Whatever title the user typed on the previous page',
    'description' => 'Whatever description the user typed on the previous page',
]


و Task::create() یک آرایه انجمنی را گرفته و یک وظیفه جدید از آن می‌سازد:

Task::create([
    'title' => 'Buy milk',
    'description' => 'Remember to check the expiration date this time, Norbert!',
]);

 ترکیب این‌ها با هم یک وظیفه با تنها فیلدهای "عنوان" و "توضیحات" ارائه شده توسط کاربر ایجاد می‌کند.

 

تزریق وابستگی‌ها به کنترلرها

فسادها و کمک&zwnj;فن&zwnj;های سراسری لاراول یک رابط ساده برای دسترسی به مفیدترین کلاس&zwnj;ها در کدبیس لاراول فراهم می&zwnj;کنند. شما می&zwnj;توانید اطلاعاتی در مورد درخواست کنونی و ورودی کاربر، جلسه&zwnj;ها، کش&zwnj;ها و بسیاری دیگر از موارد دریافت کنید.&nbsp;اما اگر ترجیح می&zwnj;دهید وابستگی&zwnj;های خود را تزریق کنید، یا اگر می&zwnj;خواهید از سرویسی استفاده کنید که فساد یا کمک&zwnj;فنی ندارد، باید روشی برای آوردن نمونه&zwnj;هایی از این کلاس&zwnj;ها به داخل کنترلر خود پیدا کنید.&nbsp;این اولین برخورد ما با کانتینر سرویس لاراول است. در حال حاضر، اگر با این موضوع ناآشنا هستید، می&zwnj;توانید آن را کمی جادو از لاراول بدانید؛ یا اگر می&zwnj;خواهید بیشتر...

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

کنترلرهای منبع (Resource Controllers)

گاهی اوقات نام‌گذاری متدها در کنترلرها می‌تواند سخت‌ترین بخش نوشتن یک کنترلر باشد. خوشبختانه، لاراول برای تمامی مسیرهای یک کنترلر REST/CRUD سنتی (که در لاراول به آن resource controller گفته می‌شود) یک سری قواعد نام‌گذاری دارد. علاوه بر این، لاراول یک تولیدکننده (generator) داخلی و یک روش تعریف مسیر راحت ارائه می‌دهد که به شما اجازه می‌دهد کل یک کنترلر منبع (resource controller) را به‌صورت یکجا متصل کنید.
برای مشاهده‌ی متدهایی که لاراول برای یک کنترلر منبع (resource controller) انتظار دارد، یک کنترلر جدید را از طریق خط فرمان ایجاد کنید:

php artisan make:controller MySampleResourceController --resource

اکنون فایل app/Http/Controllers/MySampleResourceController.php را باز کنید. خواهید دید که این فایل از پیش شامل چندین متد است. بیایید بررسی کنیم که هر یک از آن‌ها چه مفهومی دارند. به‌عنوان نمونه، ما از Task استفاده خواهیم کرد.

متدهای کنترلرهای منبع در لاراول

جدولی که قبلاً دیده‌ایم را به خاطر دارید؟ جدول 3-1 نشان می‌دهد که هر متد پیش‌فرض در کنترلرهای منبع لاراول، با چه فعل HTTP (HTTP verb)، آدرس URL، نام متد کنترلر و نام مسیر مرتبط است.

اتصال یک کنترلر منبع (Binding a Resource Controller)

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

یک ترفند برای این کار وجود دارد که اتصال کنترلر منبع (resource controller binding) نام دارد. به مثال 3-28 توجه کنید:

مثال 3-28. اتصال کنترلر منبع

// routes/web.php
Route::resource('tasks', TaskController::class);

این دستور به‌صورت خودکار تمامی مسیرهای فهرست‌شده در جدول 3-1 را برای این منبع (resource) به متدهای مناسب در کنترلر مشخص‌شده متصل می‌کند. همچنین این مسیرها را نام‌گذاری می‌کند؛ برای مثال، متد index() در کنترلر TaskController به‌صورت tasks.index نام‌گذاری خواهد شد.

 

artisan route:list

اگر در شرایطی قرار گرفتید که نمی‌دانستید چه مسیرهایی (routes) در برنامه‌ی شما در دسترس هستند، یک ابزار برای این کار وجود دارد:
از طریق خط فرمان، دستور زیر را اجرا کنید:

php artisan route:list

 

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

اما من دستور زیر را ترجیح می‌دهم:

php artisan route:list --exclude-vendor

 

این دستور باعث می‌شود مسیرهای اضافه‌ای که وابستگی‌های پروژه (dependencies) برای عملکرد خود ثبت کرده‌اند، نمایش داده نشوند (به شکل 3-2 مراجعه کنید).

 

 

 

 

کنترلرهای API Resource

هنگام کار با RESTful APIs، لیست عملیات‌های ممکن روی یک منبع (resource) مشابه کنترلرهای HTML معمولی نیست.
 به عنوان مثال، در API می‌توان یک درخواست POST برای ایجاد یک منبع ارسال کرد، اما نمایش یک فرم برای ایجاد منبع (مانند صفحات HTML) بی‌معنی است.
برای ایجاد یک API resource controller که دقیقاً مشابه resource controller معمولی است اما اکشن‌های create و edit را شامل نمی‌شود، می‌توانید از فلگ --api در هنگام ایجاد کنترلر استفاده کنید:


php artisan make:controller MySampleResourceController --api


برای اتصال (bind) یک API resource controller، به جای resource() از متد apiResource() استفاده کنید، همانطور که در مثال 3-29 نشان داده شده است.
مثال 3-29: اتصال API resource controller

// routes/web.php
Route::apiResource('tasks', TaskController::class);

 

 

کنترلرهای تک‌عملکردی (Single Action Controllers)

در برخی موارد، ممکن است نیاز باشد که یک کنترلر فقط یک مسیر (route) را مدیریت کند.&nbsp;در این صورت، ممکن است نام&zwnj;گذاری متد کنترلر برای این مسیر دشوار یا غیرضروری باشد.خوشبختانه، می&zwnj;توان یک مسیر خاص را به یک کنترلر خاص متصل کرد، بدون اینکه نیازی به نام&zwnj;گذاری یک متد خاص باشد.همان&zwnj;طور که احتمالاً می&zwnj;دانید، متد __invoke() یک متد جادویی در PHP است که به شما اجازه می&zwnj;دهد یک نمونه از کلاس را به&zwnj;صورت یک تابع اجرا کنید.این ابزار همان چیزی است که کنترلرهای تک&zwnj;عملکردی لاراول برای اختصاص یک مسیر به یک کنترلر واحد استفاده می&zwnj;کنند، همان&zwnj;طور که در مثال 3-30...

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

اتصال مدل به مسیر (Route Model Binding)

یکی از الگوهای رایج مسیریابی در لاراول این است که اولین خط هر متد کنترلر سعی می‌کند منبع (resource) موردنظر را بر اساس شناسه (ID) پیدا کند، همان‌طور که در مثال 3-31 مشاهده می‌کنید.
مثال 3-31: دریافت یک منبع بر اساس مسیر

Route::get('conferences/{id}', function ($id) {
    $conference = Conference::findOrFail($id);
});

 

لاراول ویژگی‌ای را فراهم می‌کند که این الگو را ساده می‌کند و به آن route model binding گفته می‌شود. این ویژگی به شما اجازه می‌دهد مشخص کنید که یک نام پارامتر خاص (مثلاً {conference}) به resolver مسیر اعلام کند که باید یک رکورد Eloquent با آن ID را در دیتابیس جست‌وجو کرده و آن را به‌عنوان پارامتر ارسال کند، نه فقط خود ID را.
دو نوع route model binding وجود دارد: implicit و custom (یا explicit).

اتصال مدل به مسیر به‌صورت ضمنی (Implicit Route Model Binding)

ساده‌ترین روش برای استفاده از Route Model Binding این است که نام پارامتر مسیر را متناسب با مدل تنظیم کنید (مثلاً به‌جای $id از $conference استفاده کنید)، سپس این پارامتر را در متد کنترلر یا Closure typehint کنید و از همان نام متغیر در داخل متد استفاده کنید.
 این روش در عمل ساده‌تر از توضیح آن است، پس به مثال 3-32 توجه کنید:
مثال 3-32: استفاده از Implicit Route Model Binding

Route::get('conferences/{conference}', function (Conference $conference) {
    return view('conferences.show')->with('conference', $conference);
});

 

چون پارامتر مسیر ({conference}) با پارامتر متد ($conference) یکسان است و پارامتر متد نیز با مدل Conference (Conference $conference) typehint شده است، لاراول این را به‌عنوان Route Model Binding در نظر می‌گیرد.
هر بار که این مسیر فراخوانی شود، لاراول فرض می‌کند که مقدار ارسال‌شده در URL به‌جای {conference}، یک شناسه (ID) است که باید برای جستجوی یک مدل Conference استفاده شود. سپس، نمونه‌ی بازیابی‌شده از مدل به‌صورت خودکار به متد کنترلر یا Closure ارسال می‌شود.

 

سفارشی‌سازی کلید مسیر برای یک مدل Eloquent

هر زمان که یک مدل Eloquent از طریق یک بخش URL جستجو شود (معمولاً به دلیل Route Model Binding)، به‌صورت پیش‌فرض Eloquent مقدار را از ستون کلید اصلی (ID) جستجو می‌کند.

برای تغییر ستونی که Eloquent برای جستجو در URL استفاده می‌کند، کافی است متدی به نام getRouteKeyName() را در مدل خود اضافه کنید:

public function getRouteKeyName()
{
    return 'slug';
}

اکنون، در مسیری مانند conferences/{conference}، لاراول مقدار دریافت‌شده را به‌جای ID از ستون slug گرفته و جستجو را بر اساس آن انجام خواهد داد.

 

سفارشی‌سازی کلید مسیر در یک مسیر خاص

 

در Laravel، به‌جای تغییر کلید مسیر به‌صورت کلی، می‌توانید آن را فقط در یک مسیر خاص تغییر دهید. برای این کار، کافی است در تعریف مسیر، بعد از نام متغیر مسیر، یک کولون (:) و نام ستونی که می‌خواهید جستجو بر اساس آن انجام شود را اضافه کنید:

Route::get(
    'conferences/{conference:slug}',
    function (Conference $conference) {
        return view('conferences.show')->with('conference', $conference);
});

 

اگر در URL خود دو بخش داینامیک داشته باشید (برای مثال: organizers/{organizer}/conferences/{conference:slug})، لاراول به‌طور خودکار تلاش می‌کند تا پرس‌وجوهای مدل دوم را فقط به موارد مرتبط با مدل اول محدود کند. بنابراین، لاراول مدل Organizer را برای رابطه conferences بررسی خواهد کرد و اگر این رابطه وجود داشته باشد، فقط کنفرانس‌هایی را برمی‌گرداند که با Organizer پیدا شده در بخش اول مرتبط هستند.

use App\Models\Conference;
use App\Models\Organizer;

Route::get(
    'organizers/{organizer}/conferences/{conference:slug}',
    function (Organizer $organizer, Conference $conference) {
        return $conference;
    });

 

 

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

مدیریت دستی مدل&zwnj;های مسیر برای پیکربندی دستی بایندینگ مدل مسیرها، باید خطی مانند آنچه در مثال 3-33 آمده را به متد boot() در App\Providers\RouteServiceProvider اضافه کنید.مثال 3-33. اضافه کردن بایندینگ مدل مسیر public function boot() { // انجام بایندینگ Route::model('event', Conference::class); } حالا شما مشخص کرده&zwnj;اید که هر زمان مسیری پارامتری به نام {event} داشته باشد، همانطور که در مثال 3-34 نشان داده شده است، حل&zwnj;کننده مسیر یک نمونه از کلاس Conference را با ID پارامتر URL مربوطه برمی&zwnj;گرداند.مثال 3-34. استفاده از بایندینگ مدل مسیر صریح Route::get('events/{event}', function (Conference $event) { return view('events.show')-&gt;with('event', $event); }); &nbsp;

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

کش کردن مسیرها

اگر به دنبال استفاده بهینه از زمان بارگذاری خود هستید، ممکن است بخواهید نگاهی به کش کردن مسیرها بیاندازید. یکی از قسمت&zwnj;های زمان&zwnj;بر در بوت&zwnj;استرپ لاراول، تجزیه فایل&zwnj;های routes/* است که ممکن است چند ده یا چند صد میلی&zwnj;ثانیه طول بکشد، و کش کردن مسیرها این فرآیند را به طور چشمگیری تسریع می&zwnj;کند.برای کش کردن فایل&zwnj;های مسیرها، باید از تمام مسیرهای کنترلر، ریدایرکت، ویو و منابع استفاده کنید (نه مسیرهای بسته&zwnj;بندی شده). اگر اپلیکیشن شما از هیچ مسیر بسته&zwnj;بندی شده&zwnj;ای استفاده نمی&zwnj;کند، می&zwnj;توانید دستور php artisan route:cache را اجرا کنید و لاراول نتایج فایل&zwnj;های routes/* شما را سریالایز خواهد کرد....

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

شبیه‌سازی روش فرم

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

افعال HTTP در لاراول

همانطور که قبلاً مشاهده کردیم، می&zwnj;توانید در تعریف مسیر، افعالی را که یک مسیر باید با آن&zwnj;ها تطابق داشته باشد، با استفاده از Route::get()، Route::post()، Route::any() یا Route::match() مشخص کنید. همچنین می&zwnj;توانید با Route::patch()، Route::put() و Route::delete() هم تطابق پیدا کنید.اما چگونه می&zwnj;توان درخواست&zwnj;هایی غیر از GET را با یک مرورگر وب ارسال کرد؟ ابتدا، ویژگی method در یک فرم HTML فعل HTTP آن را تعیین می&zwnj;کند: اگر فرم شما دارای متد "GET" باشد، از طریق پارامترهای query و متد GET ارسال خواهد شد؛ اگر فرم دارای متد "POST" باشد، از طریق بدنه پست و متد POST ارسال می&zwnj;شود.فریم&zwnj;ورک&zwnj;های جاوااسکریپت...

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

شبیه‌سازی روش HTTP در فرم‌های HTML

برای اطلاع دادن به لاراول که فرمی که در حال ارسال آن هستید باید به عنوان چیزی غیر از یک POST در نظر گرفته شود، یک متغیر مخفی با نام _method و مقدار "PUT"، "PATCH" یا "DELETE" اضافه کنید، و لاراول آن ارسال فرم را مانند یک درخواست با آن فعل تطبیق خواهد داد و مسیریابی خواهد کرد.
فرم در مثال 3-35، از آنجا که فعل "DELETE" را به لاراول ارسال می‌کند، با مسیرهای تعریف‌شده با Route::delete() تطبیق پیدا خواهد کرد، اما با مسیرهای Route::post() تطبیق پیدا نخواهد کرد.
مثال 3-35. شبیه‌سازی روش فرم

<form action="/tasks/5" method="POST">
    <input type="hidden" name="_method" value="DELETE">
    <!-- یا: -->
    @method('DELETE')
</form>

 

حفاظت در برابر CSRF

اگر تاکنون سعی کرده‌اید فرمی را در یک اپلیکیشن لاراول ارسال کنید، از جمله فرم موجود در مثال 3-35، احتمالاً با خطای TokenMismatchException مواجه شده‌اید. به‌طور پیش‌فرض، تمام مسیرها در لاراول به جز مسیرهای "فقط خواندنی" (آنهایی که از GET، HEAD یا OPTIONS استفاده می‌کنند) در برابر حملات جعل درخواست بین‌سایتی (CSRF) محافظت می‌شوند و به‌وسیله نیاز به یک توکن، به شکل یک ورودی به نام _token، که باید همراه با هر درخواست ارسال شود، محافظت می‌شوند. این توکن در ابتدای هر جلسه ایجاد می‌شود و هر مسیر غیر از مسیرهای فقط خواندنی، _token ارسال‌شده را با توکن جلسه مقایسه می‌کند.

 

CSRF چیست؟

حملات جعل درخواست بین‌سایتی (Cross-Site Request Forgery یا CSRF) زمانی رخ می‌دهد که یک وب‌سایت خود را به‌جای وب‌سایت دیگری جا می‌زند. هدف این است که کسی دسترسی کاربران شما به وب‌سایت شما را با ارسال فرم‌ها از وب‌سایت خود به وب‌سایت شما از طریق مرورگر کاربر وارد شده، تصرف کند. بهترین راه برای مقابله با حملات CSRF، محافظت از تمام مسیرهای ورودی—POST، DELETE و غیره—با استفاده از یک توکن است که لاراول به‌طور پیش‌فرض این کار را انجام می‌دهد.

 

شما دو گزینه برای رفع این خطای CSRF دارید. اولین و ترجیح داده‌شده‌ترین روش، افزودن ورودی _token به هر یک از ارسال‌های فرم‌های شما است. در فرم‌های HTML، راه ساده‌ای برای انجام این کار وجود دارد، همانطور که در مثال 3-36 می‌بینید.

مثال 3-36. توکن‌های CSRF

<form action="/tasks/5" method="POST">
    @csrf
</form>

 

در برنامه‌های JavaScript، کمی کار بیشتری نیاز است، اما نه خیلی زیاد. رایج‌ترین راه‌حل برای سایت‌های استفاده‌کننده از فریم‌ورک‌های JavaScript، ذخیره کردن توکن در هر صفحه در یک تگ مانند این است:

<meta name="csrf-token" content="<?php echo csrf_token(); ?>">

 

ذخیره کردن توکن در تگ این امکان را می‌دهد که آن را به هدر HTTP صحیح متصل کنید، که می‌توانید این کار را به‌صورت گلوبال برای تمام درخواست‌ها از فریم‌ورک JavaScript خود انجام دهید، همانطور که در مثال 3-37 نشان داده شده است.
مثال 3-37. اتصال گلوبال هدر برای CSRF

// در jQuery:
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});


// با Axios: این به‌طور خودکار آن را از یک کوکی می‌گیرد. هیچ کاری لازم نیست!

 

لاراول هدر X-CSRF-TOKEN (و X-XSRF-TOKEN که توسط Axios و سایر فریم‌ورک‌های JavaScript مانند Angular استفاده می‌شود) را در هر درخواست بررسی می‌کند و توکن‌های معتبر ارسال‌شده در آنجا باعث می‌شوند که محافظت CSRF به‌عنوان برآورده‌شده علامت‌گذاری شود.

 

اتصال توکن‌های CSRF با Vue Resource

 راه‌اندازی توکن CSRF در Vue Resource کمی متفاوت از لاراول است؛ برای مثال‌ها به مستندات Vue Resource مراجعه کنید.

ریدایرکت ها

تا اینجا، تنها چیزهایی که صراحتاً در مورد بازگشت از متد کنترلر یا تعریف مسیر صحبت کرده&zwnj;ایم، نماها بوده&zwnj;اند. اما چند ساختار دیگر وجود دارد که می&zwnj;توانیم بازگردانیم تا به مرورگر دستور بدهیم چگونه رفتار کند.اولاً، بیایید ریدایرکت را پوشش دهیم. شما قبلاً چند مورد از این&zwnj;ها را در مثال&zwnj;های دیگر دیده&zwnj;اید. دو روش رایج برای ایجاد ریدایرکت وجود دارد؛ ما در اینجا از هلپر global redirect() استفاده خواهیم کرد، اما ممکن است شما ترجیح دهید از فساد استفاده کنید. هر دو یک نمونه از Illuminate\Http\RedirectResponse ایجاد می&zwnj;کنند، برخی از متدهای راحتی را روی آن انجام می&zwnj;دهند، و سپس آن...

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

redirect()->to()

امضای متد برای متد to() برای ریدایرکت‌ها به این صورت است:

function to($to = null, $status = 302, $headers = [], $secure = null)

$to یک مسیر داخلی معتبر است، $status وضعیت HTTP است (که به‌طور پیش‌فرض 302 است)، $headers به شما این امکان را می‌دهد که مشخص کنید کدام هدرهای HTTP را همراه با ریدایرکت ارسال کنید، و $secure به شما این امکان را می‌دهد که انتخاب پیش‌فرض HTTP یا HTTPS (که معمولاً بر اساس URL درخواست فعلی شما تنظیم می‌شود) را تغییر دهید. مثال ۳-۳۹ استفاده از این متد را نشان می‌دهد.
مثال ۳-۳۹. redirect()->to()

Route::get('redirect', function () {
    return redirect()->to('home');

       // یا همان، استفاده از میانبر:

    return redirect('home');
});

 

redirect()->route()

متد route() مشابه متد to() است، اما به&zwnj;جای اشاره به یک مسیر خاص، به یک نام خاص از مسیر اشاره می&zwnj;کند (به&zwnj;طور مثال، در مثال ۳-۴۰). مثال ۳-۴۰. redirect()-&gt;route() Route::get('redirect', function () { return redirect()-&gt;route('conferences.index'); }); توجه داشته باشید که چون برخی از نام&zwnj;های مسیر نیاز به پارامتر دارند، ترتیب پارامترها کمی متفاوت است. متد route() یک پارامتر دوم اختیاری برای پارامترهای مسیر دارد: function route($to = null, $parameters = [], $status = 302, $headers = []) بنابراین، استفاده از آن ممکن است مشابه مثال ۳-۴۱ باشد.مثال ۳-۴۱. redirect()-&gt;route() با پارامترها Route::get('redirect', function () { return redirect()-&gt;route('conferences.show', ['conference' =&gt; 99]);...

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

redirect()->back()

به دلیل برخی از راحتی‌های داخلی پیاده‌سازی جلسه در لاراول، برنامه شما همیشه از صفحه‌ای که کاربر قبلاً بازدید کرده اطلاع دارد. این امکان فراهم می‌کند که از redirect()->back() برای هدایت کاربر به صفحه‌ای که قبلاً آمده استفاده کنید. همچنین یک میانبر جهانی برای این کار وجود دارد: back().

دیگر متدهای ریدایرکت

سرویس ریدایرکت متدهای دیگری نیز دارد که کمتر استفاده می&zwnj;شوند، اما همچنان در دسترس هستند:refresh() ریدایرکت به همان صفحه&zwnj;ای که کاربر در حال حاضر در آن است. away() امکان ریدایرکت به یک URL خارجی بدون اعتبارسنجی URL پیش&zwnj;فرض. secure() مشابه to() با پارامتر secure که برابر با "true" است. action() این متد به شما امکان می&zwnj;دهد به یک کنترلر و متد در یکی از دو روش لینک دهید: به صورت رشته (redirect()-&gt;action('MyController@myMethod')) یا به صورت یک تاپل (redirect()-&gt;action([MyController::class, 'myMethod'])). guest() به طور داخلی توسط سیستم احراز هویت استفاده می&zwnj;شود (که در فصل 9 بحث خواهد شد); زمانی که کاربری به...

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

redirect()->with()

در حالی که ساختار آن مشابه سایر متدهایی است که می‌توانید روی redirect() فراخوانی کنید، with() متفاوت است زیرا جایی که شما ریدایرکت می‌کنید را تعریف نمی‌کند، بلکه داده‌هایی که همراه با ریدایرکت ارسال می‌کنید را مشخص می‌کند. هنگامی که کاربران را به صفحات مختلف هدایت می‌کنید، معمولاً می‌خواهید داده‌های خاصی را همراه با آنها ارسال کنید. شما می‌توانید داده‌ها را به صورت دستی به جلسه ارسال کنید، اما لاراول برخی از متدهای راحتی برای کمک به شما دارد.
معمولاً می‌توانید یک آرایه از کلیدها و مقادیر یا یک کلید و مقدار تک ارسال کنید، همانطور که در مثال 3-42 نشان داده شده است. این داده‌ها را به جلسه ذخیره می‌کند تا فقط در بارگذاری صفحه بعدی قابل استفاده باشند.
مثال 3-42. ریدایرکت با داده‌ها

Route::get('redirect-with-key-value', function () {
    return redirect('dashboard')
        ->with('error', true);
});

Route::get('redirect-with-array', function () {
    return redirect('dashboard')
        ->with(['error' => true, 'message' => 'Whoops!']);
});

 

زنجیره‌کردن متدها در ریدایرکت‌ها

مانند بسیاری از فسادهای دیگر، بیشتر فراخوانی‌ها به فساد Redirect می‌توانند زنجیره‌ای از متدها را به‌صورت روان (fluent) بپذیرند، مانند فراخوانی‌های with() در مثال 3-42. در بخش “What Is a Fluent Interface?” بیشتر با مفهوم fluency آشنا خواهید شد.

 

شما همچنین می‌توانید از withInput() استفاده کنید، مانند مثال 3-43، تا ورودی فرم کاربر را که به صورت فلش شده است، با هدایت بازگردانید؛ این بیشتر در موارد خطای اعتبارسنجی رایج است که می‌خواهید کاربر را به فرمی که قبلاً از آن آمده است بازگردانید.
مثال 3-43. هدایت با ورودی فرم

Route::get('form', function () {
    return view('form');
});

Route::post('form', function () {
    return redirect('form')
        ->withInput()
        ->with(['error' => true, 'message' => 'Whoops!']);
});

 

راحت‌ترین روش برای دریافت ورودی فلش شده‌ای که با withInput() ارسال شده است، استفاده از کمک‌تابع old() است که می‌تواند برای دریافت تمام ورودی‌های قدیمی (با old()) یا فقط مقدار برای یک کلید خاص استفاده شود، همانطور که در مثال زیر نشان داده شده است، با پارامتر دوم به عنوان پیش‌فرض اگر مقداری برای ورودی قدیمی وجود نداشته باشد. معمولاً این را در نمای‌ها خواهید دید، که این HTML را می‌توان هم در نمای "ایجاد" و هم در نمای "ویرایش" برای این فرم استفاده کرد:

<input name="username" value="<?=
    old('username', 'Default username instructions here');?>">

 

در مورد اعتبارسنجی صحبت می‌کنیم، همچنین یک روش مفید برای ارسال خطاها همراه با پاسخ هدایت شده وجود دارد: withErrors() . شما می‌توانید هر "ارائه‌دهنده" خطا را به آن ارسال کنید، که ممکن است یک رشته خطا، آرایه‌ای از خطاها، یا معمولاً یک نمونه از Illuminate\Validator باشد که در فصل 10 پوشش داده خواهد شد. مثال 3-44 استفاده از آن را نشان می‌دهد.
مثال 3-44. هدایت با خطاها

Route::post('form', function (Illuminate\Http\Request $request) {
    $validator = Validator::make($request->all(), $this->validationRules);

    if ($validator->fails()) {
        return back()
            ->withErrors($validator)
            ->withInput();
    }
});

withErrors() به طور خودکار یک متغیر $errors را با نمای‌های صفحه‌ای که به آن هدایت می‌شود، به اشتراک می‌گذارد تا بتوانید آن را به هر شکلی که بخواهید مدیریت کنید.

متد validate() در درخواست‌ها

ظاهر مثال 3-44 را دوست ندارید؟ یک ابزار ساده و قدرتمند وجود دارد که به شما کمک می‌کند آن کد را تمیزتر کنید. در بخش “validate() on the Request Object” بیشتر بخوانید.

 

 

متوقف کردن درخواست

علاوه بر بازگشت به نمایه&zwnj;ها و هدایت&zwnj;ها، رایج&zwnj;ترین روش برای خروج از یک مسیر، استفاده از متد abort است. چند متد در دسترس جهانی وجود دارد (abort(), abort_if(), و abort_unless())، که به طور اختیاری می&zwnj;توانند کد وضعیت HTTP، پیغام و یک آرایه هدر را به عنوان پارامتر دریافت کنند.همانطور که در مثال 3-45 نشان داده شده است، abort_if() و abort_unless() یک پارامتر اول دارند که برای صحت آن ارزیابی می&zwnj;شود و با توجه به نتیجه، متوقف می&zwnj;شود.مثال 3-45. متوقف کردن با کد 403 (دسترسی ممنوع) Route::post('something-you-cant-do', function (Illuminate\Http\Request $request) { abort(403, 'You cannot do that!'); abort_unless($request-&gt;has('magicToken'), 403); abort_if($request-&gt;user()-&gt;isBanned, 403); });...

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

پاسخ‌ های سفارشی

چند گزینه دیگر برای بازگشت پاسخ‌ها وجود دارد، بنابراین بیایید رایج‌ترین پاسخ‌ها پس از نمایه‌ها، هدایت‌ها و متوقف کردن‌ها را مرور کنیم. همانطور که با هدایت‌ها دیدیم، شما می‌توانید این متدها را روی هر دو response() کمک‌کننده یا Response facade اجرا کنید.

response()->make()

اگر می&zwnj;خواهید یک پاسخ HTTP به طور دستی ایجاد کنید، داده&zwnj;های خود را به پارامتر اول متد response()-&gt;make() وارد کنید. برای مثال: return response()-&gt;make('Hello, World!'). مجدداً، پارامتر دوم کد وضعیت HTTP و پارامتر سوم هدرها هستند.

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

response()->json() و ->jsonp()

برای ایجاد یک پاسخ HTTP کدگذاری شده به صورت JSON به طور دستی، محتوای JSON-able خود (آرایه‌ها، مجموعه‌ها، یا هر چیز دیگری) را به متد json() ارسال کنید. برای مثال: return response()->json(User::all()). این متد شبیه به make() است، با این تفاوت که محتوای شما را به فرمت JSON کدگذاری می‌کند و هدرهای مناسب را تنظیم می‌کند.

response()->download(), ->streamDownload(), و ->file()

برای ارسال یک فایل برای دانلود توسط کاربر نهایی، می&zwnj;توانید یک نمونه از SplFileInfo یا یک نام فایل رشته&zwnj;ای را به متد download() ارسال کنید، با یک پارامتر دوم اختیاری برای نام فایل دانلودی. برای مثال: return response()-&gt;download('file501751.pdf', 'myFile.pdf') که فایل file501751.pdf را ارسال کرده و به آن نام myFile.pdf را هنگام ارسال می&zwnj;دهد.برای نمایش همان فایل در مرورگر (اگر فایل PDF یا تصویر یا هر چیز دیگری باشد که مرورگر بتواند آن را مدیریت کند)، به جای آن از response()-&gt;file() استفاده کنید که همان پارامترها را مانند response()-&gt;download() دریافت می&zwnj;کند.اگر بخواهید محتوای از یک سرویس خارجی را برای دانلود...

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

تست

در برخی جوامع دیگر، ایده تست واحد برای متدهای کنترلر رایج است، اما در لاراول (و بیشتر جوامع PHP) معمول است که از تست&zwnj;های برنامه&zwnj;نویسی برای تست عملکرد مسیرها استفاده شود.برای مثال، برای بررسی اینکه یک مسیر POST به درستی کار می&zwnj;کند، می&zwnj;توانیم تستی مانند مثال 3-47 بنویسیم. مثال 3-47. نوشتن یک تست ساده برای مسیر POST public function test_post_creates_new_assignment() { $this-&gt;post('/assignments', [ 'title' =&gt; 'My great assignment', ]); $this-&gt;assertDatabaseHas('assignments', [ 'title' =&gt; 'My great assignment', ]); } آیا ما مستقیماً متدهای کنترلر را فراخوانی کردیم؟ نه. اما اطمینان حاصل کردیم که هدف این مسیر&mdash;دریافت یک درخواست POST و ذخیره...

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

خلاصه

مسیرهای لاراول در فایل‌های routes/web.php و routes/api.php تعریف می‌شوند. شما می‌توانید مسیر مورد انتظار هر مسیر را تعریف کنید، بخش‌هایی که ثابت هستند و بخش‌هایی که پارامتر هستند، کدام متدهای HTTP می‌توانند به مسیر دسترسی داشته باشند و چگونه آن را حل کنید. همچنین می‌توانید میان‌افزارها را به مسیرها متصل کرده، آنها را گروه‌بندی کرده و به آنها نام بدهید.
آنچه از مسیر یا متد کنترلر برمی‌گردد، نحوه پاسخ‌دهی لاراول به کاربر را تعیین می‌کند. اگر یک رشته یا یک نمای باشد، به کاربر ارائه می‌شود؛ اگر داده‌های دیگری باشد، به JSON تبدیل شده و به کاربر نمایش داده می‌شود؛ و اگر یک ریدایرکت باشد، باعث ریدایرکت می‌شود.
لاراول مجموعه‌ای از ابزارها و امکانات را برای ساده‌سازی وظایف و ساختارهای رایج مربوط به مسیرها فراهم می‌کند. اینها شامل کنترلرهای منبع، اتصال مدل به مسیر و جعل متد فرم هستند.