<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های عارف</title>
        <link>https://virgool.io/feed/@maslak</link>
        <description></description>
        <language>fa</language>
        <pubDate>2026-06-07 11:13:16</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/88861/avatar/yiwP6A.jpeg?height=120&amp;width=120</url>
            <title>عارف</title>
            <link>https://virgool.io/@maslak</link>
        </image>

                    <item>
                <title>درک عمیق‌تر Service Container و Service Provider در لاراول</title>
                <link>https://virgool.io/laravel-community/%D8%AF%D8%B1%DA%A9-%D8%B9%D9%85%DB%8C%D9%82-%D8%AA%D8%B1-service-container-%D9%88-service-provider-%D8%AF%D8%B1-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-e0x79s8ckovl</link>
                <description>اول از همه باید بدونیم که تمامی مفاهیم زیر به یک معنا هستند:Service ContainerDependency Injection Container (DI)Inversion of Control Container (IoC)Application Containerدر واقع Service Container لاراول یکی از مهم‌ترین بخش‌های این فریمورکه که کمتر بهش توجه میشه، ۲ دلیل مهم برای این کم توجهی Developerها اینه:۱- بعضی‌ها فکر می‌کنند که درک DI سخته، پس بهتره که بیخیال مفهوم IoC و IoC container بشیم.۲- درکی از این ندارن که آیا قراره از container استفاده کنند یا خیر (در واقع ما همیشه داریم از این‌ها استفاده می‌کنیم؛ چرا که سنگ بنای لاراول روی Service Container بنا شده).تزریق وابستگی (Dependency Injection) و IoC:یک تعریف خیلی ساده از DI اینه: فرآیند پاس دادن وابستگی‌های یک کلاس به شکل آرگومان(هایی) به یکی از متدهای اون کلاس (معمولا setter یا constructor).کد زیر، نمونه‌ای از کدی هست که توش DI رعایت نشده و فراخوانی کلاس پایه داخل کلاس سطح بالاتر اتفاق افتاده:متد share مربوط به کلاس TwitterService می‌تونه یک چنین چیزی باشه:توی کد بالا، داخل سازندهٔ کلاس Publication یک شی جدید از کلاس TwitterService ایجاد شده و به عبارتی کلاس Publication حالا به کلاس توییتر سرویس وابسته شده، به جای اینکه این کلاس رو داخل بدنهٔ سازنده فراخوانی کنیم؛ می‌تونیم شی‌ای از این کلاس رو به صورت یک آرگومان به سازندهٔ Publication پاس بدیم:برای درک بهتر قطعه کدهای بالا، داخل web route میاییم و می‌گیم که هر وقت وارد uri روت یا / شدیم؛ یک توییت ارسال کن:این ساده‌ترین حالتی بود که میشد DI رو پیاده‌سازی کرد؛ اعمال کردن DI به یک کلاس باعث IoC یا وارونگی کنترل میشه، یعنی کلاس سطح بالاتر (اینجا Publication) دیگه وابسته به کلاس سطح پایین‌تر (اینجا TwitterService) نیست. قبل از اعمال DI، کلاس وابسته (Publication) در کنترل یک وابستگی سطح پایین‌تر بود. اما با اعمال DI، کنترل این پروسه به فریمورک واگذار شد.IoC Containerدر بخش قبل دیدیم که چطور میشه با استفاده از DI کنترل نمونه‌سازی (ساخت یک شی از یک کلاس) رو تحت کنترل فریمورک در آورد و وابستگی بین دو کلاس سطح بالاتر و پایین‌تر رو حذف کرد.با استفاده از IoC Container میشه پروسهٔ DI رو خیلی ساده تر کرد. قطعه کد زیر یک کلاسه که می‌تونه دیتایی که می‌خوایم رو ذخیره و در وقت نیاز بازیابی کنه (به ما برگردونه). یک نمونهٔ خیلی ساده از IoC Container می‌تونه به شکل زیر پیاده‌سازی بشه:اسم این کلاس رو گذاشتیم Container، چرا؟ چون مثل یک کانتینر یا ظرفی هست که میشه توش وابستگی‌ها رو تعریف کرد.ما می‌تونیم هر دیتایی که دلمون خواست رو توی این کانتینر bind کنیم؛ متد bind دو تا آرگومان می‌گیره، کلید و مقدار، کلید و مقدار توی یک آرایه به اسم bindings ذخیره میشن و وقتی قرار باشه که ازشون استفاده کنیم کافیه کلید رو به متد make پاس بدیم تا اگر مقدار متناظرش یک تابع بود؛ اون رو رن کنه و اگر غیر اون بود، مقدار درخواستی رو برگردونه.برای درک بهتر کلاس بالا، ازش توی web route استفاده می‌کنیم:توی مثال بالا، اول یک شی از کلاس Container یا در واقع همون IoC Container خودمون ساختیم؛ بعدش با استفاده از متد bind یک key value رو جفت کردیم (در واقع داخل آرایهٔ bindings ذخیره کردیم)؛ حالا که به اون مقدار نیاز پیدا کردیم؛ کافیه key رو به متد make این کلاس پاس بدیم.حالا خیلی راحت می‌تونیم با استفاده از یک callback function یک کلاس رو به IoC Container بایند کنیم؛ این callback function میاد و یک شی از کلاسی که بهش دادیم ایجاد می‌کنه و مسیر کامل کلاس (namespace) رو هم به عنوان کلید داخل bindings ذخیره می‌کنه.مثلا توی قطعه کد بالا، کلید ما TwitterService::class هست که معادلش میشه App\Service\TwitterService (به شکل رشته).حالا بیایین فرض کنیم که کلاس TwitterService نیاز به یک کلید API داره تا بتونه اهراز هویت رو انجام بده و بعدش توییت رو ارسال کنه؛ توی این حالت می‌تونیم کلاسمون رو به شکل زیر پیاده‌سازی کنیم:چند نکتهٔ مهم در مورد قطعه کد بالا:۱- یک شی از کلاس Container ایجاد کردیم.۲- کلید API رو با متد bind به container متصل کردیم.۳- اینجا مهمه: کلاس TwitterService رو به عنوان کلید در نظر گرفتیم و آرگومان دوم یک closure function هست که توش از use استفاده شده! خب این دیگه چه جور syntaxای هست؟یک closure در واقعی تابعی هست که می‌تونیم اون رو به یک متغیر نسبت بدیم.یک closure یک namespace جدا محسوب میشه، بنابراین به طور معمول امکان دسترسی به متغیرهایی که بیرون این عبارت تعریف شدن وجود نداره و باید از کلمهٔ کلیدی use برای دسترسی به اون مقادیر خارج از فضای نام استفاده کرد.وقتی که ما یک دیتا رو به container بایند می‌کنیم؛ هر زمان که بهش نیاز داشته باشیم می‌تونیم صداش بزنیم بدون اینکه نیاز باشه تا همیشه از new استفاده کنیم؛ در واقع ما با استفاده از IoC Container فقط یک بار از new استفاده می‌کنیم.به خودی خود استفاد چند باره از new بد نیست؛ اما این رو باید در نظر گرفت با هر بار صدا زدن new ما باید وابستگی‌های اون کلاسی که داریم ازش یک نمونه (instant) ایجاد می‌کنیم رو بهش بدیم که نیاز به دقت داره و به مرور زمان حوصله‌سر بر و تکراری میشه. اما با IoC Container دیگه نیاز نیست نگران باشیم؛ چرا که container مراقبت تزریق وابستگی های ما خواهد بود.یک IoC Container کد ما رو خیلی زیاد منعطف می‌کنه. وضعیتی رو فرض کنید که قرار باشه کلاس TwitterService رو با یک کلاس دیگه (برای پست کردن مطلب توی یک سایت دیگه به غیر از توییتر) عوض کنیم؛ مثلا LinkedInService؛ حالا تکلیف چیه؟ کدهایی که بالا نوشتیم اصلا برای یک چنین سناریویی مناسب نیستن؛ برای جایگزین کردن کلاس TwitterService باید یک کلاس جدید ایجاد و به container متصلش کنیم؛ و تمامی مرجع‌ها به کلاس قبلی رو جایگزین کنیم.اما کافیه که بریم سراغ interface ها توی php تا این مشکل رو حل کنیم و به راحتی سرویس‌های مختلف یا درایورهای مختلف رو عوض کنیم؛ به جای اینکه مجبور به بازنویسی مجدد کدها باشیم. برای حل معضل بالا، کافیه یک interface به اسم SocialMediaServiceInterface ایجاد کنیم:حالا می‌تونیم کلاس TwitterService خودمون که اینترفیس SocialMediaServiceInterface رو implement می‌کنه پیاده‌سازی کنیم:***نکته: داخل interface نمیشه property تعریف کرد و فقط مجاز به تعریف تابع هستیم.یک اصلاح مهم: concrete class یعنی کلاسی که یک interface رو implement می‌کنه. این کلاس باید تمامی متدهای تعریف شده داخل اینترفیس رو پیاده‌سازی کنه.خب حالا ما باید به جای کلاسی که interface رو implement کرده خود interface رو به container بایند کنیم. توی callback یک شی از کلاس TwitterService رو به شکل قبلی برمی‌گردونیم.کد بالا دقیقا مثل کد قبلی که بدون اینترفیس بود؛ کار می‌کنه. ماجرا از جایی جالب میشه که ما قراره از LinkedIn به جای توییتر استفاده کنیم. به لطف وجود interface این کار رو میشه تو ۲ مرحلهٔ ساده انجام داد:۱- پیاده‌سازی کلاس LinkedInService که اینترفیس رو implement می‌کنه:۲- آپدیت کردن فراخوانی TwitterService با LinkedInService توی آرگومان دوم bind:حالا ما یک شی از کلاس LinkedInService داریم که به container بایند شده. زیبایی این روش اینکه که تمامی کدهای قبلی ما سر جای خودشون هستن و با یک تغییر کوچیک درایور شبکه اجتماعی رو عوض کردیم. تا زمانی که یک کلاس اینترفیس SocialMediaServiceInterface رو implement کنه، می‌تونه به عنوان یک سرویس معتبر شبکهٔ اجتماعی به کانتینر bind بشه.مبحث Service Container و Service Provider:لاراول با یک IoC Container خیلی قوی‌تری نسبت به پیاده‌سازی سادهٔ ما (کلاس Container) ارائه میشه. اسم IoC Container لاراول Service Container هست؛ پس عملا Service Container یا موارد زیر فرقی با هم ندارند و فقط یک اسم هستن:DI ContainerIoC ContainerApplication ContainerService Containerاگر بخوایم از IoC Container لاراول به جای IoCC خودمون (کلاس Container) استفاده کنیم؛ باید به شکل زیر عمل کنیم؛ شکل بازنویسی شده کدهای ما اینطوریه:توی هر برنامهٔ لاراولی app در واقع نمونه‌ای از container ما هست. در واقع این helper function میاد و یک شی یا نمونه از Service Container (اسم لاراول برای IoC Container خودش) برمی‌گردونه.دقیقا مثل container سفارشی و ساده که خودمون توسعه دادیم؛ Service Container لاراول یک متد bind و make داره که برای اتصال و دسترسی به سرویس‌ها کاربرد داره.این کانتینر لاراول (Service Container) یک متد دیگه هم داره به اسم singleton که در واقع اشاره داره به design pattern معروف سینگلتون؛ وقتی یک کلاسی رو به عنوان singleton به کانتینر بایند می‌کنیم؛ در این صورت تنها و تنها یک شی از اون کلاس در هر درخواست ایجاد میشه.برای درک تفاوت bind و singleton bind بهتره که نگاهی به کدهای زیر بندازیم:توی قطعه کد بالا، به Service Container لاراول (app) گفتیم که ۲ تا شی برای ما make کنه؛ دقت داشته باشید که این کلاس به صورت ساده به container بایند شده؛ مقادیر برگشتی ۲۶۲ و ۲۶۹ نشون میده که instanceهای متفاوتی ایجاد شده، اما اگر کلاس رو به شکل singleton بایند کنیم؛ نتیجه متفاوت خواهد بود:حالا برای هر دو تا instance که گفتیم make بشه؛ فقط یک عدد (۲۶۲) برگشت داده شده و این نشون میده که بایند کردن کلاس به شکل singleton باعث میشه که فقط و فقط یک شی از اون کلاس ایجاد بشه.مبحث Service Provider:حالا که با Service Container لاراول، تابع کمکی app و متدهای bind singleton و make آشنا شدیم؛ حالا وقتشه که یاد بگیریم؛ این متدها رو کجا فراخوانی کنیم؟! مطمئنا نمی‌تونیم از کنترلرها و مدل‌ها برای این کار (فراخوانی این متدها) استفاده کنیم.محل درست برای قرار دادن binding ها Service Provider هست؛ Service Providerها در واقع کلاس‌هایی هستند که داخل پوشهٔ app/Providers قرار گرفتن و ما می‌تونیم SPهای سفارشی خودمون رو هم ایجاد کنیم. SP ها در واقع زیربنا و چارچوب فریمورک لاراول هستن؛ این کلاس ها مسئول راه‌اندازی اکثر سرویس‌هایی هستن که فریمورک ارائه میده.هر پروژهٔ جدید با ۵ Service Provider به شکل default ارائه میشه. بین این‌ها، کلاس AppServiceProvider به شکل خالی ارائه به همراه ۲ متد boot و register ارائه میشه. متد register برای ثبت سرویس‌های جدید برای application استفاده میشه؛ این متد جایی هست که ما عملیات binding رو انجام می‌دیم:داخل Service Providerها ما با استفاده از this به app دسترسی داریم و نیازی نیست که که تابع کمکی app استفاده کنیم؛ چرا که هر Provider وارث ServiceProvider هست پس میشه بهش دسترسی داشت.متد boot برای منطق مورد نیاز برای راه‌اندازی سرویس‌های ثبت شده به کار میره. یک مثال خوب برای درک این قضیه کلاس BroadcastingServiceProvider هست که به شکل دیفالت با هر پروژهٔ لاراولی نصب میشه:همون‌طور که می‌بینید؛ متد boot میاد و متد routes از فساد Broadcast رو صدا می‌زنه و همین‌طور فایل routes/channels.php رو require می‌کنه؛ با این کار مسیرهای broadcasting رو توی این پروسه فعال می‌کنه.برای یک یا دو binding ساده، مثل چیزی که خودمون پیاده‌سازی کردیم؛ استفاده از کلاس AppServiceProvider منطقی به نظر می‌رسه؛ اما برای سرویس‌هایی که نیاز به منطق پیچیده‌تری برای اجرا شدن دارن، ما باید یک Provider جدید ایجاد کنیم:artisan make:provider &lt;provider name&gt;تصویر کامل!توی بخش‌های قبلی مفاهیم مختلف زیر رو یاد گرفتیم:۱- تزریق وابستگی (DI)۲- اصل وارونگی کنترل (IoC)۳- Service Container۴- Service Providersتوی این بخش، تمامی مفاهیم بالا رو کنار هم قرار می‌دیم تا به یک تصویر کامل و جامع از عملکرد اون‌ها دست پیدا کنیم.دوباره برمی‌گردیم سراغ کلاس Publication که اوایل این آموزش باهاش کار کرده بودیم. اگر به خاطر داشته باشید کلاس Publication به کلاس TwitterService وابسته بود. اما با سرکار اومدن interfaceها، بهتره که کدهای قبلی رو آپدیت کنیم و به جای کلاس TwitterService اینترفیس این کلاس رو بهش پاس بدیم:حالا کلاس Publication عوض اینکه وابسته به یک کلاس خاص باشه؛ می‌تونه هر نوع کلاسی که اینترفیس SocialMediaServiceInterface رو implement کرده؛ به عنوان آگومان ورودی دریافت کنه. قبل از این ما اینترفیس SocialMediaServiceInterface رو داخل Service Provider به کلاس LinkedInService که SocialMediaServiceInterface رو implement کرده بود bind کردیم؛ بنابراین با اجرای کد زیر؛ انتظار میره که یک شی از کلاس LinkedInService برگشت داده بشه:app()-&gt;make(SocialMediaServiceInterface::class);اما یک کلاسی که هنوز به Service Container لاراول bind نشده؛ خود کلاس Publication هست؛ اما اگر بیاییم و بدون bind کردن مستقیم کد زیر رو ران کنیم؛ چه اتفاقی می‌افته؟app()-&gt;make(Publication::class);می‌بینیم که به شکل عجیبی بدون bind کردن این کلاس؛ یک شی ازش ایجاد شد! توی نگاه اول این اتفاق عجیب و شبیه جادو هست؛ اما در واقع این اتفاق نتیجهٔ در کنار هم قرار گرفتن تمامی مفاهیمی هست که پیش از این گفتیم.زمانی که لاراول به خط زیر می‌رسه:app()-&gt;make(Publication::class);میره دنبال مقدار متناظر با این کلید داخل Service Container؛ اما وقتی کلید رو پیدا نمی‌کنه؛ میره و یه نگاه می‌ندازه به constructor تا ببینه که این کلاس چه ورودی(هایی) می‌گیره:لاراول متوجه میشه که کلاس Publication یک ورودی می‌گیره به اسم $socialMediaService که نوعش (که با type hint مشخص شده) SocialMediaServiceInterface اینه؛ بنابراین میره داخل Service Container و دنبال کلیدی می‌گرده که از این interface باشه؛ حالا لاراول وقتی که کلید SocialMediaServiceInterface::class رو پیدا کرد؛ یک شی از نوع LinkedInService برمی‌گردونه:الان باید برای شما مشخص شده باشه که لاراول می‌تونه به صورت اتوماتیک یک شی از وابستگی‌هایی که مجهز به Type Hint هستن (قبل از اسم متغیر نوع اون هم مشخص شده)؛ ایجاد کنه (تا زمانی که اون وابستگی ها هیچ interfaceای رو implement نکرده باشن).حالا می تونیم routes/web.php رو به شکل زیر آپدیت کنیم:حالا پروژه داره خیلی راحت کار می‌کنه؛ همون‌طور که می‌بینید؛ مطابق قابلیت تفکیک اتوماتیک container شما خیلی به ندرت به شکل دستی یک شی از container بیرون می‌کشید! تا زمانی که شما interfaceها رو به شکل مناسب bind می‌کنید و برای نیازمندها و وابستگی ها type hint قرار می دید؛ لاراول کارهای دشوار رو برای شما به صورت اتوماتیک انجام میده.منبع: farhan.dev (مطلب اصلی از این سایته؛ اما گاهی نکاتی از stackoverflow و... هم اضافه شده)</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Thu, 06 Oct 2022 23:17:25 +0330</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پروژهٔ Todo</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%B1%D9%88%DA%98%D9%87%D9%94-%D9%86%D9%87%D8%A7%DB%8C%DB%8C-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-rncr4kbibjz6</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.قسمت ۱۵۸ تا ۱۷۴منبع: ویدئوهای آموزشی وب‌پروگپیاده‌سازی Todo App با لاراول:اول از همه لاراول رو نصب می‌کنیم:composer create-project --prefer-dist laravel/laravel todoAppاول یه دور پکیج‌های موجود در package.json رو نصب می‌کنیم:npm iحالا پکیج‌های مورد نیازمون رو نصب می‌کنیم:npm i bootstrap jquery sass sass-loader
npm install @popperjs/core --saveحالا باید پکیج‌های جاوااسکریپتی مورد نیاز رو به فایل bootstrap.js (راه انداز ما که داخل resources هست) اضافه کنیم؛ تکلیف فایل‌های css مربوط به بوت استرپ (کتابخانه) چی میشه؟ باید داخل پوشهٔ resources یک پوشه به اسم sass ایجاد کنیم و یک فایل به اسم app.scss که داخلش فایل sass بوت استرپ رو import کنیم؛ این require و import کردن داخل فایل bootstrap.js و app.scss از node_modules صورت می‌گیره که با دستور npm i ایجاد شده:محتویات فایل bootstrap.js:try{
    window.Popper = require(&#039;popper.js&#039;).default;
    window.$ = window.jQuery = require(&#039;jquery&#039;);
    require(&#039;bootstrap&#039;)
 } catch(e){
 }نحوه import داخل فایل app.scss:@import &#039;~bootstrap/scss/bootstrap&#039;;توی قطعه کد بالا، علامت ~ نشان گر پوشهٔ node_module هست.از اونجایی که از لاراول ۸ استفاده می کنیم (بر خلاف لاراول ۹ که از vite استفاده می کنه)، باید داخل فایل webpack.mix.js راهنمایی کامپایل sass رو اضافه کنیم؛ چرا js رو اضافه نکنیم؟ چون از قبل خودش اون رو داره:mix.js(&#039;resources/js/app.js&#039;, &#039;public/js&#039;)
    .sass(&#039;resources/sass/app.scss&#039;, &#039;public/css&#039;, [
        //
    ]);در نهایت هم برای کامپایل شدن فایل‌های js و sass باید دستور زیر رو بزنیم:npm run devحالا راحت می تونیم با asset helper function از این فایل‌ها داخل فایل های blade خودمون استفاده کنیم.خب برای ادامهٔ کار و ایجاد صفحات خودمون، اول از همه یک master layout درست می‌کنیم که بقیهٔ صفحات ما از اون ارث‌بری کنن، داخل پوشهٔ public/layouts می‌تونیم یک فایل master حالا با هر اسمی که خواستیم ایجاد کنیم:// public/layouts/app.blade.php
&lt;!doctype html&gt;
&lt;html lang=&amp;quoten&amp;quot&gt;
  &lt;head&gt;
    &lt;title&gt;Title&lt;/title&gt;
    &lt;!-- Required meta tags --&gt;
    &lt;meta charset=&amp;quotutf-8&amp;quot&gt;
    &lt;meta name=&amp;quotviewport&amp;quot content=&amp;quotwidth=device-width, initial-scale=1, shrink-to-fit=no&amp;quot&gt;
    &lt;!-- Bootstrap CSS v5.2.0-beta1 --&gt;
    &lt;link rel=&amp;quotstylesheet&amp;quot href=&amp;quot{{ asset(&#039;css/app.css&#039;) }}&amp;quot &gt;
  &lt;/head&gt;
  &lt;body&gt;
    @yield(&#039;content&#039;)
    &lt;script src=&amp;quot{{ asset(&#039;js/app.js&#039;) }}&amp;quot &gt;
  &lt;/body&gt;
&lt;/html&gt;همون‌طور که توی کد بالا داریم می‌بینیم؛ با asset helper function به فایل app.css و app.js داخل پوشهٔ public دسترسی پیدا کردیم؛ از طرفی با yield directive اومدیم و به سایر صفحاتی که از این صفحه ارث‌بری می‌کنن امکان این رو دادیم که به یک section به اسم content داشته باشن و محتوای اون‌ها به جای این بخش yield شده نمایش داده بشه.حالا می‌تونیم اینطوری از صفحه بالا ارث‌بری کنیم://about.blade.php
@extends(&#039;layouts.app&#039;)

@section(&#039;content&#039;)
...
@endsectionساخت Model و Migration پروژهٔ todo:php artisan make:model Todo -mدستور بالا برای ما Model و migration (با آپشن -m) رو ایجاد می‌کنه.محتوای فایل migration ما به صورت زیره، ما سه تا فیلد برای پروژهٔ todo خودمون نیاز داشتیم که بهش اضافه کردیم؛ به ترتیب شامل عنوان، توضیحات و یک فلگ برای تشخیص اینکه اون تسک انجام شده یا خیر.&lt;?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTodosTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create(&#039;todos&#039;, function (Blueprint $table) {
            $table-&gt;id();
            $table-&gt;string(&#039;title&#039;);
            $table-&gt;text(&#039;description&#039;);
            $table-&gt;boolean(&#039;completed&#039;)-&gt;default(0);
            $table-&gt;timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists(&#039;todos&#039;);
    }
}حالا می‌زنیم:php artisan migrateخب بریم سراغ Controller:php artisan make:controller TodoControllerحالا باید برای عملکردهای مختلف برنامهٔ خودمون متدهای مختلفی تعریف کنیم و همین‌طور به routeها سر و سامان بدیم:Route::get(&#039;/&#039;, [TodoController::class, &#039;index&#039;])-&gt;name(&#039;todos.index&#039;);البته حتما باید کلاس TodoController رو هم use کنیم:use App\Http\Controllers\TodoController;حالا میریم سراغ اولین متد از کلاس TodoController:&lt;?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TodoController extends Controller
{
    public function index()
    {
        return &#039;Home Page!&#039;;
    }
}حالا قصد داریم که index خودمون (صفحه اصلی) رو پیاده‌سازی کنیم؛ صفحه index ما اینطوری میشه:طبیعتا برای نمایش یک چنین صفحه‌ای باید view اون رو پیاده‌سازی کنیم و توی قطعه کد بالا به جای return کردن &quot;Home Page&quot; باید اون view پیاده‌سازی شده blade رو برگردونیم؛ ما می‌تونیم view مربوط به index خودمون رو داخل پوشهٔ todos و فایل index.blade.php ایجاد کنیم و مقدار زیر رو داخل controller برگشت بدیم:return view(&#039;todos.index&#039;);خب، ما قبلا یک پوشه ایجاد کرده بودیم به اسم layouts و داخل اون یک master layout به اسم app.blade.php ایجاد کرده بودیم که ساختار فایل html ما رو شامل میشد و یک section به اسم content داخل body ایجاد کردیم که باید توی view مربوط به index استفاده بشه؛ محتوای فایل index که در مسیر resources/views/todos/index.blade.php قرار می‌گیره:@extends(&#039;layouts.app&#039;)
@section(&#039;content&#039;)
...
@endsectionخب، فرم خیلی ابتدایی view صفحهٔ index ما اینطوری میشه:&lt;div class=&amp;quotcontainer&amp;quot&gt;
    &lt;div class=&amp;quotrow justify-content-center&amp;quot&gt;
        &lt;div class=&amp;quotcol-md-8&amp;quot&gt;
            &lt;h4&gt;تسک ها&lt;/h4&gt;
            &lt;div class=&amp;quotcard&amp;quot&gt;
                &lt;div class=&amp;quotcard-header&amp;quot&gt;
                    تسک ها
                &lt;/div&gt;
                &lt;div class=&amp;quotcard-body&amp;quot&gt;
                    &lt;ul class=&amp;quotlist-group&amp;quot&gt;
                        @foreach ($todos as $todo)
                            &lt;li class=&amp;quotlist-group-item&amp;quot&gt;
                                {{ $todo-&gt;title }}
                            &lt;/li&gt;
                        @endforeach
                    &lt;/ul&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;باید دقت داشته باشیم که مقدار متغیر todos رو باید داخل controller از مدل Todo بخونیم و با آرایه یا compact پاس بدیم به view:&lt;?php

namespace App\Http\Controllers;

use App\Models\Todo;
use Illuminate\Http\Request;

class TodoController extends Controller
{
    public function index()
    {
        $todos = Todo::all();
        return view(&#039;todos.index&#039;, compact(&#039;todos&#039;));
    }
}اگر بخوایم کد css به پروژه اضافه کنیم؛ مثلا فونت رو فارسی کنیم؛ باید اون‌ها رو داخل resources/sass/app.scss وارد کنیم؛ بعد که npm run dev یا npm run watch رو ران کردیم؛ این کدها داخل فایل css پوشهٔ public کامپایل میشن:// Bootstrap
@import &#039;~bootstrap/scss/bootstrap&#039;;

@font-face {
    font-family: &amp;quotVazir&amp;quot
    src: url(&amp;quot../fonts/Vazir.eot&amp;quot);
    /* IE9 Compat Modes */
    src: url(&amp;quot../fonts/Vazir.eot?#iefix&amp;quot) format(&amp;quotembedded-opentype&amp;quot),
        url(&amp;quot../fonts/Vazir.woff2&amp;quot) format(&amp;quotwoff2&amp;quot),
        url(&amp;quot../fonts/Vazir.woff&amp;quot) format(&amp;quotwoff&amp;quot),
        url(&amp;quot../fonts/Vazir.ttf&amp;quot) format(&amp;quottruetype&amp;quot);
    /* Safari, Android, iOS */
}

body{
    direction: rtl;
    text-align: right !important;
    font-family: &amp;quotVazir&amp;quot !important;
}فایل‌های فونت ما هم داخل resources/fonts قرار می‌گیرن. باید توجه داشته باشیم که اگر حتی اگر یکی از فونت های بالا توی مسیر مشخص شده قرار نداشته باشن، با مشکل مواجه می‌شیم و کامپایل صورت نمی گیره.حالا اگر تعداد تسک‌های داخل جدول todos زیاد بود تکلیف چی میشه؟ باید paginate رو بهش اضافه کنیم؛ برای این کار داخل TodoController باید متد paginate رو روی مدل ران کنیم:&lt;?php

namespace App\Http\Controllers;

use App\Models\Todo;
use Illuminate\Http\Request;

class TodoController extends Controller
{
    public function index()
    {
        $todos = Todo::paginate(5);
        return view(&#039;todos.index&#039;, compact(&#039;todos&#039;));
    }
}حالا باید فرانت pagination رو به پروژه اضافه کنیم؛ برای این کار یک div به صورت زیر به view خودمون اضافه می‌کنیم:&lt;div class=&amp;quotmt-2 d-flex justify-content-center&amp;quot&gt;{{ $todos-&gt;links() }}&lt;/div&gt;از اونجایی که این پروژه روی لاراول ۸ هست و این نسخه از لاراول از Tailwindcss برای فرانت استفاده می‌کنه، و ما داریم از bootstrap استفاده می کنیم؛ ظاهر paginate ما بهم ریخته هست؛ برای حل این مشکل باید قطعه کد زیر رو به متد boot فایل AppServiceProvider اضافه کنیم:Paginator::useBootstrap();حتما هم باید Paginator رو use کنیم:use Illuminate\Pagination\Paginator;اما از اونجایی که پروژهٔ ما rtl هست؛ باید مشکل جهت buttonهای راست و چپ رو اوکی کنیم:.page-item:first-child .page-link {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;

    border-top-right-radius: 0.35rem;
    border-bottom-right-radius: 0.35rem;
}

.page-item:last-child .page-link {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;

    border-top-left-radius: 0.35rem;
    border-bottom-left-radius: 0.35rem;
}مبحث Route Model Binding:در حالت عادی وقتی می‌خوایم یک تسک (todo) خاص رو نشون بدیم؛ میاییم و یک route تعریف می‌کنیم که مثلا id اون تسک رو می‌گیره که به یک متد داخل controller پاس میده و اونجا با متد find یا findOrFail اون تسک رو پیدا می کنیم و به view پاس می‌دیم تا نشون بده. اما توی مبحث Roue Model Binding توی آرگومان ورودی متد کنترلر خودمون، یک متغیر از نوع همون مدل خاص ایجاد می‌کنیم و دقیقا به همون نام، در این حالت خود لاراول میاد و اون تسک رو از table میکشه بیرون و بعد می‌تونیم اون رو به view پاس بدیم.سناریوی اول:// web.php
Route::get(&#039;/todos/{id}&#039;, [TodoController::class, &#039;show&#039;])-&gt;name(&#039;todos.index&#039;);


// TodoController
&lt;?php
namespace App\Http\Controllers;

use App\Models\Todo;
use Illuminate\Http\Request;

class TodoController extends Controller
{
    public function index()
    {
        $todos = Todo::paginate(1);
        return view(&#039;todos.index&#039;, compact(&#039;todos&#039;));
    }
    public function show($val)
    {
        $todo = Todo::findOrFail($val);
        dd($todo);
    }
}توی قطعه کد بالا، وقتی بزنیم localhost:8000/todos/2 مقدار ۲ برای تابع show داخل TodoController ارسال میشه و اونجا dd می‌کنیم یا حالا مثلا وقتی تسک رو با مدل استخراج کردیم هر بلایی که خواستیم سرش میاریم.اما سناریوی Route Model Binding به صورت زیر هست:// web.php 
Route::get(&#039;/todos/{id}&#039;, [TodoController::class, &#039;show&#039;])-&gt;name(&#039;todos.index&#039;);  

// TodoController
&lt;?php
namespace App\Http\Controllers;

use App\Models\Todo;
use Illuminate\Http\Request;

class TodoController extends Controller
{
    public function index()
    {
        $todos = Todo::paginate(1);
        return view(&#039;todos.index&#039;, compact(&#039;todos&#039;));
    }
    public function show(Todo $id)
    {
        dd($id);
    }
}توی کد بالا باید دقت کنیم که پارامتر ورودی تابع show دقیقا باید هم نام با متغیری باشه که توی route دریافت میشه.حالا با توجه به این چیزی که یاد گرفتیم؛ می‌خوایم صفحهٔ show رو هم ایجاد کنیم تا بتونیم یک تسک خاص رو ببینیم تا بعدا روش عملیاتی مثل ویرایش و... رو انجام بدیم:@extends(&#039;layouts.app&#039;)

@section(&#039;content&#039;)
    &lt;div class=&amp;quotcontainer&amp;quot&gt;
        &lt;div class=&amp;quotrow justify-content-center&amp;quot&gt;
            &lt;div class=&amp;quotcol-md-8&amp;quot&gt;
                &lt;h4 class=&amp;quottext-center mt-5 mb-3&amp;quot&gt;{{ $todo-&gt;title }}&lt;/h4&gt;
                &lt;div class=&amp;quotcard&amp;quot&gt;
                    &lt;div class=&amp;quotcard-header&amp;quot&gt;
                        توضیحات
                    &lt;/div&gt;
                    &lt;div class=&amp;quotcard-body&amp;quot&gt;
                        {{ $todo-&gt;description }}
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
@endsectionقطعه کد بالا که مربوط میشه به show.blade.php کلیات رو از master layout ما به اسم app.blade.php به ارث برده.قطعه کد زیر که مربوط به TodoController میشه، بعد از اینکه route آی‌دی رو به تابع show پاس داد، اینجا با تکنیک Route Model Binding مقدار اون تسک رو از table می‌خونیم و به view پاس می‌دیم تا برامون نشونش بده:&lt;?php

namespace App\Http\Controllers;

use App\Models\Todo;
use Illuminate\Http\Request;

class TodoController extends Controller
{
    public function index()
    {
        $todos = Todo::paginate(5);
        return view(&#039;todos.index&#039;, compact(&#039;todos&#039;));
    }
    public function show(Todo $todo)
    {
        return view(&#039;todos.show&#039;, compact(&#039;todo&#039;));
    }
}حالا باید بریم سراغ کامل کردن index خودمون، چطوری؟  باید به ازای هر تسکی که داریم؛ یک دکمهٔ نمایش قرار بدیم که وقتی کاربر روش کلیک کرد اون تسک نمایش داده بشه؛ شکل ظاهری چیزی که می‌خوایم اینطوری میشه:بنابراین یک تگ a با کلاس button به صورت زیر برای صفحهٔ index خودمون ایجاد می‌کنیم:@extends(&#039;layouts.app&#039;)

@section(&#039;content&#039;)
&lt;div class=&amp;quotcontainer&amp;quot&gt;
    &lt;div class=&amp;quotrow justify-content-center&amp;quot&gt;
        &lt;div class=&amp;quotcol-md-8&amp;quot&gt;
            &lt;h4&gt;تسک ها&lt;/h4&gt;
            &lt;div class=&amp;quotcard&amp;quot&gt;
                &lt;div class=&amp;quotcard-header&amp;quot&gt;
                    تسک ها
                &lt;/div&gt;
                &lt;div class=&amp;quotcard-body&amp;quot&gt;
                    &lt;ul class=&amp;quotlist-group&amp;quot&gt;
                        @foreach ($todos as $todo)
                            &lt;li class=&amp;quotlist-group-item d-flex justify-content-between&amp;quot&gt;
                                {{ $todo-&gt;title }}
                            &lt;a class=&amp;quotbtn btn-sm btn-dark&amp;quot href=&amp;quot{{ route(&#039;todos.show&#039; , [&#039;todo&#039; =&gt; $todo-&gt;id]) }}&amp;quot&gt;نمایش&lt;/a&gt;
                            &lt;/li&gt;
                        @endforeach
                    &lt;/ul&gt;
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div class=&amp;quotd-flex justify-content-center mt-5&amp;quot&gt;{{ $todos-&gt;links() }}&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
@endsectionتوی کد بالا، route helper function در واقع داره اسم روتی که به:/todos/{todo}اشاره داره رو کال می‌کنه و آرگومان دومش یک آرایه هست که داریم می‌گیم عمو جان این todo که داخل curly braces نیاز داری، در واقع id هر تسک توی table ما داخل database هست:route(&#039;todos.show&#039; , [&#039;todo&#039; =&gt; $todo-&gt;id])ایجاد view و منطق مربوط به Create Task:خب توی index باید یه دکمه بذاریم برای ایجاد تسک، اینطوری:و باید یک روت تعریف کنیم برای صفحهٔ ایجاد تسک:پس باید توی web.php روت خودمون رو اضافه کنیم:Route::get(&#039;/todos/create&#039;, [TodoController::class, &#039;create&#039;])-&gt;name(&#039;todos.create&#039;);باید متدش رو هم داخل کنترلر بسازیم:    public function create()
    {
        return view(&#039;todos.create&#039;);
    }نکتهٔ مهم:به روت‌های زیر خوب نگاه کنید:Route::get(&#039;/&#039;, [TodoController::class, &#039;index&#039;])-&gt;name(&#039;todos.index&#039;);
Route::get(&#039;/todos/{todo}&#039;, [TodoController::class, &#039;show&#039;])-&gt;name(&#039;todos.show&#039;);
Route::get(&#039;/todos/create&#039;, [TodoController::class, &#039;create&#039;])-&gt;name(&#039;todos.create&#039;);وقتی که ما می‌خوایم یک تسک جدید create کنیم و به view مربوط به اضافه کردن پست بریم؛ می‌زنیم localhost:8000/todos/create اما می‌بینیم که ۴۰۴ برای ما میاد! چرا؟ چون اگر به ترتیب روت‌ها توجه کنیم می‌بینیم که اول روت /todos/{todo} اجرا میشه و در واقع ما داریم متد show رو کال می‌کنیم؛ برای حل این مشکل می‌تونیم روت create رو بالای روت show قرار بدیم یا اینکه مسیر دسترسی رو عوض کنیم؛ مثلا:/todos/show/{todo}خب، حالا وقتی کاربر بزنه localhost:8000/todos/create باید view زیر نمایش داده بشه:@extends(&#039;layouts.app&#039;)

@section(&#039;content&#039;)
    &lt;div class=&amp;quotcontainer&amp;quot&gt;
        &lt;div class=&amp;quotrow justify-content-center&amp;quot&gt;
            &lt;div class=&amp;quotcol-md-8 mt-5&amp;quot&gt;
                &lt;div class=&amp;quotcard&amp;quot&gt;
                    &lt;div class=&amp;quotcard-header&amp;quot&gt;
                        ایجاد تسک جدید
                    &lt;/div&gt;
                    &lt;div class=&amp;quotcard-body&amp;quot&gt;
                        &lt;form action=&amp;quot{{ route(&#039;todos.store&#039;) }}&amp;quot method=&amp;quotPOST&amp;quot&gt;
                            @csrf
                            &lt;div class=&amp;quotform-group&amp;quot&gt;
                                &lt;label for=&amp;quottitle&amp;quot&gt;عنوان&lt;/label&gt;
                                &lt;input type=&amp;quottext&amp;quot id=&amp;quottitle&amp;quot name=&amp;quottitle&amp;quot class=&amp;quotform-control&amp;quot&gt;
                            &lt;/div&gt;
                            &lt;div class=&amp;quotform-group&amp;quot&gt;
                                &lt;label for=&amp;quotdescription&amp;quot&gt;توضیحات&lt;/label&gt;
                                &lt;textarea id=&amp;quotdescription&amp;quot name=&amp;quotdescription&amp;quot class=&amp;quotform-control&amp;quot&gt;&lt;/textarea&gt;
                            &lt;/div&gt;
                            &lt;button class=&amp;quotbtn btn-dark&amp;quot type=&amp;quotsubmit&amp;quot&gt;ارسال&lt;/button&gt;
                        &lt;/form&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
@endsectionقسمت مهم توی view بالا فرمی هست که با متد post داره مقادیرش رو به یک روتی به اسم todos.store سابمیت می‌کنه، این روت هم یک متدی به store رو داخل کنترلر صدا می‌زنه و اونجا با Request مقادیر ارسالی از فرم رو می‌خونیم و در نهایت کاری که نیازه رو باهاش انجام می‌دیم، مثلا ذخیره داخل دیتابیس و...، نکته مهم دیگه‌اش هم استفاده از دایرکتیو @csrf هست که حتما باید برای جلوگیری از حملات قرار بگیره:// Route:
Route::post(&#039;/todos&#039;, [TodoController::class, &#039;store&#039;])-&gt;name(&#039;todos.store&#039;);

// TodoController Method:
    public function store(Request $rerquest)
    {
        dd($request-&gt;all());
    }اینم کد مربوط به اضافه شدن دکمهٔ «ایجاد تسک» داخل صفحهٔ index (کامل نذاشتم و فقط کد دکمه هست):@extends(&#039;layouts.app&#039;)

@section(&#039;content&#039;)
    &lt;div class=&amp;quotcontainer&amp;quot&gt;
        &lt;div class=&amp;quotrow justify-content-center&amp;quot&gt;
            &lt;div class=&amp;quotcol-md-8&amp;quot&gt;
                &lt;div class=&amp;quotd-flex justify-content-between align-items-center my-3&amp;quot&gt;
                    &lt;h4&gt;تسک ها&lt;/h4&gt;
                    &lt;a class=&amp;quotbtn btn-sm btn-outline-dark&amp;quot href=&amp;quot{{ route(&#039;todos.create&#039;) }}&amp;quot&gt;ایجاد تسک&lt;/a&gt;
                &lt;/div&gt;دکمه بالا، ما رو به روت todos.create هدایت می کنه که بالاتر تعریف کردیم؛ این روت در واقع متد store رو داخل کنترلر صدا می‌زنه و مقادیر ارسالی از طریق فرم با post رو با Request دریافت می‌کنیم.خب قبل از اینکه بریم سراغ ذخیره کردن تسک داخل database بریم سراغ validation دیتای ورودی:خب، ما می‌تونیم validation رو داخل متد store انجام بدیم؛ برای این کار باید روی request متد validate رو اجرا کنیم:public function store(Request $request)
    {
        $request-&gt;validate([
            &#039;title&#039; =&gt; &#039;required&#039;,
            &#039;description&#039; =&gt; &#039;required&#039;
        ]);
        dd(&#039;Done&#039;);
    }طبق قطعه کد بالا، فرم بعد از اینکه submit شد، ما می‌تونیم به دیتای اون از طریق Request داخل متد store دسترسی داشته باشیم و متد validate رو روش اجرا کنیم؛ اگر اوکی بود که ادامهٔ دستور ران میشه و اگر خیر، error به همون برگشت داده میشه که می‌تونیم هندلش کنیم.یکی از راه‌های هندل error اینه که یک فایل blade جدا براش ایجاد کنیم و کدهای بررسی و نمایش error رو توش قرار بدیم و توی blade اصلی خودمون یعنی create اون رو include کنیم. برای این کار یک پوشه به اسم sections داخل پوشهٔ views ایجاد می‌کنیم و فایل error.blade.php رو اونجا ایجاد می‌کنیم:@if (count($errors) &gt; 0)
    &lt;div class=&amp;quotalert alert-danger&amp;quot&gt;
        &lt;ul class=&amp;quotmb-0&amp;quot&gt;
            @foreach ($errors-&gt;all() as $error)
                &lt;li class=&amp;quotalert-text&amp;quot&gt;{{ $error }}&lt;/li&gt;
            @endforeach
        &lt;/ul&gt;
    &lt;/div&gt;
@endifخب توی قطعه کد بالا، با استفاده از if directive چک کردیم که آیا اندازهٔ آرایهٔ errors ما که در صورت validate نشدن به همون view برگشت داده میشه بزرگ تر از صفر هست یا خیر! اگر بزرگتر باشه یعنی اینکه خطا(هایی) داریم. حالا بعدش با استفاده از html و bootstrap سعی کردیم که اون خطا(ها) رو داخل یک ul نمایش بدیم.حالا این قطعه کد رو چیکار کنیم؟ باید include بشه داخل create view:اما متن error برگشتی انگلیسی هست و نیاز به فارسی‌سازی داره قبلا در موردش بحث کردیم و واردش نمی‌شیم. به طور خلاصه باید فایل‌های ترجمه فارسی رو داخل پوشهٔ fa داخل پوشهٔ lang بذاریم و مقادیر locale رو fa  و همین‌طور timezone رو به Asia/Tehran تغییر بدیم (داخل config/app.php).فایل error.balde.php ما error رو به صورت alert بوت استرپ نشون میده، اگر بخوایم error رو زیر اون فیلد مربوطه نشون بدیم؛ می تونیم از @error directive داخل فایل create.blade.php استفاده کنیم:&lt;form action=&amp;quot{{ route(&#039;todos.store&#039;) }}&amp;quot method=&amp;quotPOST&amp;quot&gt;
                            @csrf
                            &lt;div class=&amp;quotform-group&amp;quot&gt;
                                &lt;label for=&amp;quottitle&amp;quot&gt;عنوان&lt;/label&gt;
                                &lt;input type=&amp;quottext&amp;quot id=&amp;quottitle&amp;quot name=&amp;quottitle&amp;quot class=&amp;quotform-control&amp;quot value=&amp;quot{{ old(&#039;title&#039;) }}&amp;quot @error(&#039;title&#039;) style=&amp;quotborder-color: red;&amp;quot @enderror&gt;
                                @error(&#039;title&#039;)
                                    &lt;p class=&amp;quotinvalid-feedback d-flex&amp;quot&gt;{{ $message }}&lt;/p&gt;
                                @enderror
                            &lt;/div&gt;
                            &lt;div class=&amp;quotform-group&amp;quot&gt;
                                &lt;label for=&amp;quotdescription&amp;quot&gt;توضیحات&lt;/label&gt;
                                &lt;textarea id=&amp;quotdescription&amp;quot name=&amp;quotdescription&amp;quot class=&amp;quotform-control&amp;quot @error(&#039;title&#039;) style=&amp;quotborder-color: red;&amp;quot @enderror&gt;{{ old(&#039;description&#039;) }}&lt;/textarea&gt;
                                @error(&#039;description&#039;)
                                    &lt;p class=&amp;quotinvalid-feedback d-flex&amp;quot&gt;{{ $message }}&lt;/p&gt;
                                @enderror
                            &lt;/div&gt;
                            &lt;button class=&amp;quotbtn btn-dark&amp;quot type=&amp;quotsubmit&amp;quot&gt;ارسال&lt;/button&gt;
                        &lt;/form&gt;البته توی قطعه کد بالا، علاوه بر اینکه اومدیم و خطا رو زیر هر input نمایش دادیم، با استفاده از دایرکتیو error رنگ input رو هم تغییر دادیم و از old() هم برای حفظ مقادیر وارد شده در صورت بروز خطا و بازگشت به همون view استفاده کردیم.حالا چطوری دیتایی که از فرم گرفتیم رو وارد دیتابیس کنیم؟خب برای این کار خیلی ساده می‌تونیم از دستور create مربوط به مدل خودمون استفاده کنیم؛ یعنی وقتی که اطلاعات فرم رو پاس دادیم به متد store کنترلر و بعد از validate شدن، کافیه که با create اون ریکورد رو توی دیتابیس ایجاد کنیم:&lt;?php
    public function store(Request $request)
    {
        $request-&gt;validate([
            &#039;title&#039; =&gt; &#039;required&#039;,
            &#039;description&#039; =&gt; &#039;required&#039;,
        ]);
        Todo::create([
            &#039;title&#039; =&gt; $request-&gt;title,
            &#039;description&#039; =&gt; $request-&gt;description
        ]);
        return redirect()-&gt;route(&#039;todos.index&#039;);
    }
?&gt;
در آخر هم با استفاده از دستورز redirect دوباره کاربر رو به view اصلی منتقل می‌کنیم. برای ذخیره‌سازی باید حواسمون به mass assign error باشه، برای جلوگیری از این error وقتی می‌خوایم یک recored جدید رو توی دیتابیس ایجاد کنیم باید یا متغیر fillable و یا guarded رو داخل مدل ست کنیم:protected $fillable = [&#039;title&#039;, &#039;description&#039; , &#039;completed&#039;];
protected $guarded = [];تفاوت متغیر fillable و guarded در اینه که توی fillable مقادیری رو می‌ذاریم که می‌تونن توی جدول ذخیره بشن و توی guarded مقادیری که نمی‌تونن! پس اگر از guarded استفاده کنیم و خالیش بذاریم؛ همهٔ ستون ها اجازه ذخیره شدن دارن.اگر اسم مدل و جدول داخل دیتابیس متفاوت باشه، باید متغیر table رو هم داخل مدل ست کنیم:protected $table = &#039;todos&#039;;نکته: توی index ترتیب نمایش تسک‌ها از قدیمی‌ترین به جدیدترین هست؛ یعنی همون فرمی که داخل table دیتابیس می‌بینیم؛ برای اینکه جدیدترین تسک رو اول ببینیم؛ کافیه از متد latest() مربوط به مدل استفاده کنیم:&lt;?php
    public function index()
    {
        $todos = Todo::latest()-&gt;paginate(3);
        return view(&#039;todos.index&#039;, compact(&#039;todos&#039;));
    }
?&gt;اضافه کردن Sweet Alert به پروژه:با استفاده از این کتابخونه می‌تونیم همچین پیام‌هایی رو به برنامهٔ خودمون اضافه کنیم:برای نصب و استفاده از این کتابخونه کافیه که قدم به قدم docی که توی گیت‌هاب اومده رو دنبال کنیم:۱- نصب backend کتابخونه با composer:composer require uxweb/sweet-alert۲- نصب فرانت کتابخونه با npm (میشه cdn هم اضافه کرد):npm install sweetalert --save-dev۳- لود کردن کدهای فرانت داخل bootstrap.js:// resources/js/bootstrap.js
require(&amp;quotsweetalert&amp;quot);۴- کامپایل فایل‌های js:npm run dev۵- استفاده از کدهای sweet alert داخل پروژه، برای این کار باید اون رو داخل view با include directive اضافه کنیم:&lt;!DOCTYPE html&gt;
&lt;html lang=&amp;quoten&amp;quot&gt;
  &lt;head&gt;
    &lt;!-- Scripts --&gt;
    &lt;script src=&amp;quot{{ asset(&#039;js/app.js&#039;) }}&amp;quot&gt;
  &lt;/head&gt;
  &lt;body&gt;
    @include(&#039;sweet::alert&#039;)
  &lt;/body&gt;
&lt;/html&gt;حالا می‌تونیم از SweetAlert که فساد این کتابخونه هست یا از alert helper function داخل کنترلر خودمون دقیقا قبل از redirect استفاده کنیم:use SweetAlert
public function store()
{
    SweetAlert::message(&#039;Robots are working!&#039;);
    return Redirect::home();
}استفاده از توابع کمکی:public function destroy()
{
    Auth::logout();
    alert&#40;&#41;-&gt;success(&#039;You have been logged out.&#039;, &#039;Good bye!&#039;);
    return home();
}حالا به راحتی بریم برای اضافه کردن sweet alert به بخش create:    public function store(Request $request)
    {
        $request-&gt;validate([
            &#039;title&#039; =&gt; &#039;required&#039;,
            &#039;description&#039; =&gt; &#039;required&#039;,
        ]);

        Todo::create([
            &#039;title&#039; =&gt; $request-&gt;title,
            &#039;description&#039; =&gt; $request-&gt;description
        ]);

        alert&#40;&#41;-&gt;success(&#039;تسک با موفقیت ایجاد شد&#039;, &#039;باتشکر&#039;);
        return redirect()-&gt;route(&#039;todos.index&#039;);
    }ویرایش تسک‌ها:برای ادیت کردن یک todo اول از همه یک route ایجاد می‌کنیم:Route::get(&#039;/todos/{todo}/edit&#039;, [TodoController::class , &#039;edit&#039;])-&gt;name(&#039;todos.edit&#039;);و حالا متد edit رو توی TodoController ایجاد می‌کنیم:public function edit(Todo $todo)
    {
        return view(&#039;todos.edit&#039;, compact(&#039;todo&#039;));
    }توی متد بالا از تکنیک Route Model Binding برای دسترسی به ریکورد توی دیتابیس استفاده کردیم و مقدار اون تسک یا todo رو به view ویرایش یا edit پاس دادیم. همین‌طور که داریم می‌بینیم ما داریم به یک view به اسم edit که داخل پوشهٔ resources/views/todos قرار داره منتقل می‌شیم؛ پس باید این view رو ایجاد کنیم://edit.blade.php
@extends(&#039;layouts.app&#039;)

@section(&#039;content&#039;)
    &lt;div class=&amp;quotcontainer&amp;quot&gt;
        &lt;div class=&amp;quotrow justify-content-center&amp;quot&gt;
            &lt;div class=&amp;quotcol-md-8 mt-5&amp;quot&gt;
                {{-- @include(&#039;sections.errors&#039;) --}}
                &lt;div class=&amp;quotcard&amp;quot&gt;
                    &lt;div class=&amp;quotcard-header&amp;quot&gt;
                        ویرایش تسک
                    &lt;/div&gt;
                    &lt;div class=&amp;quotcard-body&amp;quot&gt;
                        &lt;form action=&amp;quot{{ route(&#039;todos.update&#039; , [&#039;todo&#039; =&gt; $todo-&gt;id]) }}&amp;quot method=&amp;quotPOST&amp;quot&gt;
                            @csrf
                            @method(&#039;put&#039;)
                            &lt;div class=&amp;quotform-group&amp;quot&gt;
                                &lt;label for=&amp;quottitle&amp;quot&gt;عنوان&lt;/label&gt;
                            &lt;input type=&amp;quottext&amp;quot id=&amp;quottitle&amp;quot name=&amp;quottitle&amp;quot class=&amp;quotform-control @error(&#039;title&#039;) form-control-invalid @enderror&amp;quot value=&amp;quot{{ $todo-&gt;title }}&amp;quot&gt;
                                @error(&#039;title&#039;)
                                    &lt;p class=&amp;quotinvalid-feedback d-block&amp;quot&gt;
                                        &lt;strong&gt;{{ $message }}&lt;/strong&gt;
                                    &lt;/p&gt;
                                @enderror
                            &lt;/div&gt;
                            &lt;div class=&amp;quotform-group&amp;quot&gt;
                                &lt;label for=&amp;quotdescription&amp;quot&gt;توضیحات&lt;/label&gt;
                                &lt;textarea id=&amp;quotdescription&amp;quot name=&amp;quotdescription&amp;quot class=&amp;quotform-control @error(&#039;description&#039;)form-control-invalid @enderror&amp;quot&gt;{{$todo-&gt;description }}&lt;/textarea&gt;
                                @error(&#039;description&#039;)
                                    &lt;p class=&amp;quotinvalid-feedback d-block&amp;quot&gt;
                                        &lt;strong&gt;{{ $message }}&lt;/strong&gt;
                                    &lt;/p&gt;
                                @enderror
                            &lt;/div&gt;
                            &lt;button class=&amp;quotbtn btn-dark&amp;quot type=&amp;quotsubmit&amp;quot&gt;ویرایش&lt;/button&gt;
                        &lt;/form&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
@endsection
توی فرم بالا داریم مقادیر ویرایش شده و همین‌طور id اون تسک رو به یک روت به اسم todos.update پاس می‌دیم؛ این روت در واقع از نوع put هست (از این نوع برای آپدیت استفاده میشه)، همین‌طور داخل خود فرم با method directive نوع ارسال رو از نوع put تعریف کردیم:Route::put(&#039;/todos/{todo}&#039;, [TodoController::class , &#039;update&#039;])-&gt;name(&#039;todos.update&#039;);علت استفاده از method directive چیه؟ما توی خود html متد فرم رو از نوع post قرار دادیم چون خود html فقط post و get رو می‌پذیره، پس چاره چیه؟ استفاده از این directive که میاد و یک input از نوع hidden ایجاد می‌کنه که توش نوع متد رو پاس میده و لاراول از این طریق می‌تونه متد رو تشخیص بده:خب بریم برای ایجاد متد update:public function update(Request $request, Todo $todo)
    {
        $request-&gt;validate([
            &#039;title&#039; =&gt; &#039;required&#039;,
            &#039;description&#039; =&gt; &#039;required&#039;,
        ]);

        $todo-&gt;update([
            &#039;title&#039; =&gt; $request-&gt;title,
            &#039;description&#039; =&gt; $request-&gt;description
        ]);

        alert&#40;&#41;-&gt;success(&#039;تسک با موفقیت ویرایش شد&#039;, &#039;باتشکر&#039;);
        return redirect()-&gt;route(&#039;todos.index&#039;);
    }توی متد بالا، پارامترهای request و todo دریافت شدن، وقتی ما فرم رو submit می‌کنیم؛ به مقادیر فرم از طریق request دسترسی داریم و به مقادیر فعلی (ویرایش نشده یا قبلی اون todo) با todo (تکنیک RMB)، حالا اول مقادیر ورودی از فرم رو validate می کنیم که یه وقت خالی ارسال نشده باشه و بعدش اون تسک رو با متد update ویرایش می کنیم؛ در نهایت با sweet alert یک پیام نمایش می‌دیم و ریدایرکت می‌کنیم به index.حالا باید توی صفحهٔ show، دکمه‌های ویرایش و حذف رو هم اضافه کنیم (در مورد حذف جلوتر صحبت میشه): @extends(&#039;layouts.app&#039;)

@section(&#039;content&#039;)
    &lt;div class=&amp;quotcontainer&amp;quot&gt;
        &lt;div class=&amp;quotrow justify-content-center&amp;quot&gt;
            &lt;div class=&amp;quotcol-md-8&amp;quot&gt;
                &lt;h4 class=&amp;quottext-center mt-5 mb-3&amp;quot&gt;{{ $todo-&gt;title }}&lt;/h4&gt;
                &lt;div class=&amp;quotcard&amp;quot&gt;
                    &lt;div class=&amp;quotcard-header&amp;quot&gt;
                        توضیحات
                    &lt;/div&gt;
                    &lt;div class=&amp;quotcard-body&amp;quot&gt;
                        {{ $todo-&gt;description }}
                    &lt;/div&gt;
                    &lt;hr&gt;
                    &lt;div class=&amp;quotd-flex mr-3 mb-3&amp;quot&gt;
                        &lt;a class=&amp;quotbtn btn-sm btn-outline-dark&amp;quot href=&amp;quot{{ route(&#039;todos.edit&#039;, [&#039;todo&#039; =&gt; $todo-&gt;id]) }}&amp;quot&gt;
                            ویرایش
                        &lt;/a&gt;
                        &lt;form class=&amp;quotmr-2&amp;quot action=&amp;quot{{ route(&#039;todos.delete&#039;, [&#039;todo&#039; =&gt; $todo-&gt;id]) }}&amp;quot method=&amp;quotPOST&amp;quot&gt;
                            @csrf
                            @method(&#039;delete&#039;)
                            &lt;button class=&amp;quotbtn btn-sm btn-danger&amp;quot&gt;حذف&lt;/button&gt;
                        &lt;/form&gt;

                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
@endsectionحذف تسک:اول از همه روتش رو ایجاد می‌کنیم: Route::delete(&#039;/todos/{todo}&#039;, [TodoController::class , &#039;delete&#039;])-&gt;name(&#039;todos.delete&#039;);اینم از متدش توی کنترلر:public function delete(Todo $todo)
    {
        $todo-&gt;delete();
        alert&#40;&#41;-&gt;error(&#039;تسک با موفقیت حذف شد&#039;, &#039;دقت کنید&#039;);
        return redirect()-&gt;route(&#039;todos.index&#039;);
    }توی این متد هم از RMB برای دسترسی به اون تسک استفاده کردیم و متد delete رو روی اون todo ران کردیم.بخش view پروژه (دکمه حذف) رو هم بالاتر قرار دادیم؛ برای این کار یک فرم تعریف کردیم با روتی که به متد delete اشاره داره و آی‌دی اون تسک رو ارسال می‌کنه و با method directive بهش گفتیم که متد از نوع delete هست تا لاراول به درستی هندلش کنه.اینجا ما داریم hard delete می‌کنیم و برای soft delete باید use SoftDeletes رو به بدنه کلاس Todo (مدل) اضافه کنیم و همین‌طور مقدار زیر رو به migration و از نو migrate بزنیم:$table-&gt;softDeletes();برای migrate:php artisan migrate:freshحالا ستون deleted_at به table دیتابیس اضافه میشه و با ران کردن متد delete دیگه حذف فیزیکی صورت نمی‌گیره بلکه فقط این ستون مقدار می‌گیره و نمایش داده نمیشه.اضافه کردن دکمه «انجام شد»وقتی می‌خواستیم migration جدول todos رو ایجاد کنیم یک فیلد اضافه کرده بودیم به اسم complete که مقدار ۱ یا ۰ می‌گرفت؛ وقتی صفر باشه یعنی اون تسک هنوز انجام نشده و اگر ۱ باشه یعنی کامل شده؛ پس باید یک button به ازای هر تسک به index اضافه کنیم که وقتی تسکی انجام شد روی «انجام شد» کلیک بشه و بعدش دیگه این باتن رو نمایش نشده، حالا میشه ux بهتری هم متصور شد؛ اما ما اینجا این کار رو می‌خوایم بکنیم.@extends(&#039;layouts.app&#039;)

@section(&#039;content&#039;)
    &lt;div class=&amp;quotcontainer&amp;quot&gt;
        &lt;div class=&amp;quotrow justify-content-center&amp;quot&gt;
            &lt;div class=&amp;quotcol-md-8&amp;quot&gt;
                &lt;div class=&amp;quotd-flex justify-content-between align-items-center my-3&amp;quot&gt;
                    &lt;h4&gt;تسک ها&lt;/h4&gt;
                    &lt;a class=&amp;quotbtn btn-sm btn-outline-dark&amp;quot href=&amp;quot{{ route(&#039;todos.create&#039;) }}&amp;quot&gt;ایجاد تسک&lt;/a&gt;
                &lt;/div&gt;
                &lt;div class=&amp;quotcard&amp;quot&gt;
                    &lt;div class=&amp;quotcard-header&amp;quot&gt;
                        تسک ها
                    &lt;/div&gt;
                    &lt;div class=&amp;quotcard-body&amp;quot&gt;
                        &lt;ul class=&amp;quotlist-group&amp;quot&gt;
                            @foreach ($todos as $todo)
                                &lt;li class=&amp;quotlist-group-item d-flex justify-content-between&amp;quot&gt;
                                    {{ $todo-&gt;title }}
                                    &lt;div&gt;
                                        &lt;a class=&amp;quotbtn btn-sm btn-dark&amp;quot
                                            href=&amp;quot{{ route(&#039;todos.show&#039;, [&#039;todo&#039; =&gt; $todo-&gt;id]) }}&amp;quot&gt;
                                            نمایش
                                        &lt;/a&gt;
                                        {{-- @if (!$todo-&gt;completed) --}}
                                        @if ($todo-&gt;completed == 0)
                                        &lt;a class=&amp;quotbtn btn-sm btn-outline-info&amp;quot
                                            href=&amp;quot{{ route(&#039;todos.complete&#039;, [&#039;todo&#039; =&gt; $todo-&gt;id]) }}&amp;quot&gt;
                                            انجام شد
                                        &lt;/a&gt;
                                        @endif
                                    &lt;/div&gt;
                                &lt;/li&gt;
                            @endforeach
                        &lt;/ul&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class=&amp;quotd-flex justify-content-center mt-5&amp;quot&gt;{{ $todos-&gt;links() }}&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
@endsectionتوی view اصلی (index) دکمهٔ «انجام شد» رو اضافه کردیم؛ این دکمه به روت todos.complete اشاره می‌کنه و مقدار id اون todo رو برای این روت ارسال می‌کنه؛ شکل روت ما اینطوری میشه:Route::get(&#039;/todos/{todo}/complete&#039;, [TodoController::class , &#039;complete&#039;])-&gt;name(&#039;todos.complete&#039;);حالا این روت داره ما رو به متد complete کنترلر TodoController هدایت می‌کنه:public function complete(Todo $todo)
    {
        $todo-&gt;update([
            &#039;completed&#039; =&gt; 1
        ]);

        alert&#40;&#41;-&gt;success(&#039;تسک مورد نظر به وضعیت انجام شد تغییر پیدا کرد&#039;, &#039; باتشکر&#039;);
        return redirect()-&gt;route(&#039;todos.index&#039;);
    }توی این متد با استفاده از RMB اون متد رو پیدا می‌کنیم و عمل update اون فیلد رو انجام می‌دیم؛ بعدش پیام و ریدایرکت.توی index از if directive استفاده کردیم و گفتیم که اگر complete برابر با صفر بود؛ دکمه رو نشون بده؛ اگر نبود چیزی نشون نده.</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Sat, 27 Aug 2022 18:08:21 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت سیزدهم</title>
                <link>https://virgool.io/Rocket/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%B3%DB%8C%D8%B2%D8%AF%D9%87%D9%85-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF-cmqd67oqcf7f</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.قسمت ۱۷۵ تا ۱۸۹آشنایی با تغییرات و ویژگی های جدید Laravel 9:لاراول ۹ به php نسخه ۸ یا بالاتر نیاز داره.توی نسخهٔ ۹ پوشهٔ lang از پوشهٔ resources خارج و در root پروژه قرار گرفته.مبحث Controller Route Group:در واقع این ویژگی به ما کمک می‌کنه تا از تکرار اسم کنترل برای Routeهای مختلف جلوگیری بشه. فرض کنیم که یک کنترلر داریم به اسم PostController و براش Routeهای زیر رو تعریف کردیم:حالا برای سادگی کار توی ورژن ۹ چیزی اومده به اسم Controller Route Group که از تکرار اسم Controller جلوگیری می‌کنه:حالا اگر route:list بگیریم؛ این رو خواهیم داشت:مایگریشن‌های ناشناس:کلاس‌های ناشناس در ورژن ۷ php معرفی شدن، اما لاراول نسخهٔ ۹ اومده و از اون‌ها داخل migrationها استفاده کرده، این کلاس‌ها از نظر عملکردی هیچ تفاوتی با کلاس‌های با اسم ندارن و این حذف شدن اسم از ساختار این کلاس‌ها از بروز تداخل در پروژه‌های بزرگ جلوگیری می‌کنه.قبلا ساختار کلاس migration اینطوری بود:اما الان اینطوری شده:آشنایی با Helper Functionهای جدید:str(&#039;Aref&#039;)-&gt;append(&#039; Alikhani&#039;); //returns: &#039;Aref Alikhani&#039;
str(&#039;ArefAlikhani&#039;)-&gt;snake(); //returns: aref_alikhaniیک متد جدید هم اومده برای redirect کردن، برای redirect می‌تونیم از راه‌های زیر استفاده کنیم: و یا ست کردن یک name برای یک route و استفاده از متد route مربوط به redirect:اما با اومدن متد to_route به جای اینکه بنویسیم:return redirect()-&gt;route(&#039;home&#039;);کافیه بنویسیم:return to_route(&#039;home&#039;);در واقع تابع to_route دقیقا همون مقدار قبلی رو return می‌کنه:مبحث Rendering Inline Blade Templates:ایجاد یک template در کنترلر و پاس دادن به view:متد render فساد Blade دو تا آرگومان ورودی داره، اولی چیزی هست که قراره توی view نمایش داده بشه و می‌تونیم توش متغیر، دایرکتیوها و... رو قرار بدیم و توی آرگومان دوم متغیرها رو پاس می‌دیم.می‌تونیم اون string رندر شده رو داخل یک متغیر ذخیره کنیم و با view helper function پاس بدیم به view و اونجا نمایش بدیم:متد render یک آرگومان سومی هم می‌گیره که مربوط میشه به deleteCachedView و اگر true باشه دیگه فایل کش blade رو توی مسیر storagre/framework/views ایجاد نمی‌کنه.مبحث Scope Binding:برای درک این مبحث ۲ تا جدول posts و users ایجاد می کنیم و بینشون یک رابطه ایجاد می کنیم؛ به این صورت که هر پست متعلق به یک کاربره و هر کاربر می‌تونه چندین پست داشته باشه (رابطه one to many). توی تصویر زیر می‌تونیم migration مربوط به جدول posts رو ببینیم:اما نکتهٔ مهم در کد بالا استفاده از constrained هست؛ هدف استفاده از این متد در واقع خلاصه‌سازی تعریف id و ست کردن کلید خارجی هست که توی کد زیر می‌تونیم ببینم؛ در واقع کدی پایینی در تصویر زیر، خلاصهٔ کد بالایی در تصویر پایین هست:حالا باید migrate بزنیم:php artisan migrateحالا می‌ریم برای درست کردن یک factory برای پر کردن جدول posts:php artisan make:model Post -fکد بالا، هم مدل و هم factory رو برای ما ایجاد می‌کنه.حالا PostFactory رو کامل می‌کنیم:و باید این factory رو به database/seeders/DatabaseSeeder.php اضافه کنیم:حالا باید دستور زیر عملیات seeding رو انجام می‌دیم:php artisan db:seedحالا بریم سر اصل قضیه و اینکه scopeBinding چطوری کار می‌کنه:روتی که توی تصویر زیر نوشتیم؛ میاد و user شماره ۳ و پست شماره ۵ رو برای ما برمی‌گردونه، بدون اینکه چک کنه، آیا این پست شماره ۵ مربوط به user شماره ۳ میشه یا خیر. با استفاده از scopeBinding ما می‌تونیم این مشکل رو حل کنیم؛ یعنی سیستم اول چک می‌کنه اصلا این پست ۵ مربوط به user شماره ۳ هست یا خیر، اگر مربوط بهش بود که نشون میده، اما اگر مربوط بهش نبود، ۴۰۴ میده:خب برای پیاده‌سازی scope Binding اول باید رابطه بین posts و users رو ایجاد کنیم؛ داخل مدل User متد posts رو تعریف می کنیم و بهش می‌گیم که هر کاربری می‌تونه چندین پست داشته باشه: حالا با syntax زیر، scope Binding فعال میشه:حالا خیلی ساده‌تر می تونیم از متد scope Binding استفاده کنیم:مبحث Laravel Scout:یه پکیجی هست که به ما توی بحث Full Text Search کمک می‌کنه؛ در واقع از طریق این بسته می تونیم توی دیتا خودمون سرچ کنیم. وقتی دیتا بزرگ باشه، این پکیج ارزش خودش رو نشون میده.قبل از لاراول ۹، لاراول اسکات، درایوری برای کار با دیتابیس نداشت و توی این نسخه درایور دیتابیس هم اضافه شد (MySQL و PostgreSQL)، قبلا فقط از ۲ تا سرویس خارجی به اسم‌های Algolia و MeiliSearch ساپورت می‌کرد. برای نصب:composer install laravel/scoutست کردن کانفیگ لاراول اسکات:php artisan vendor:publish --provider=&amp;quotLaravel\Scout\ScoutServiceProvider&amp;quotحالا داخل پوشهٔ confug فایل scout.php اضافه شده. و باید داخل این فایل مقدار driver رو از روی algolia روی database ست کنیم (توی فایل env هم میشه):اینم تنظیم توی فایل env:حالا بریم برای استفاده از این پکیج و سرچ توی دیتابیس:اول از همه باید این Package رو داخل مدلی که قراره سرچ توش انجام بشه، use کنیم:حالا توی route با clouser function یا با استفاده از کنترلر، سرچ رو انجام میدیم:حالا می‌تونیم خیلی راحت با استفاده از تابع toSearchableArray مشخص کنیم توی چه ستون‌هایی سرچ رو انجام بده:برای سرچ سریع‌تر و index شدن محتوا با استفاده از الگوریتم‌های خاص، ما باید از field type ای به اسم fulltext توی migration استفاده کنیم تا عملیات سرچ با سرعت بیشتری صورت بگیره:حالا اینجا توی migration مربوط به posts اومدیم و نوع body رو fulltext ست کردیم و دوباره جداول رو پر و seed می‌کنیم:php artisan migrate:fresh --seedنمایی از جدول MySQL:حالا یکی از راه‌های سرچ با استفاده از whereFullText هست که آرگومان اول، ستونی هست که این تایپ رو داره و ستون دوم، مقداری هست که قراره سرچ بشه:خب، حالا اگر قرار باشه که ما ستون‌هایی از نوع fulltext رو با استفاده از scout سرچ کنیم؛ حتما باید فیلد رو توی مدل مد نظر به صورت زیر تعریف کنیم:حالا دوباره به راحتی می‌تونیم با استفاده از search این کار رو انجام بدیم:$posts = Post::search(&#039;text&#039;)-&gt;get();مبحث enum casting:این دیتاتایپ یک مقدار و تایپ ثابتی داره و یکی از مهم‌ترین کاربردهاش جلوگیری از اشتباهات تایپی هست. مثلا فرض کنیم که قراره برای جدول posts خودمون یک ستون status تعریف کنیم که وضعیت پست‌های ما رو نشون بده، مثلا فلان پست آرشیو شده، اون یکی منتشر شده و... .برای این کار یه enum تعریف می‌کنیم؛ این enumها رو توی پوشهٔ app/Enums و داخل یک فایل به اسم PostStatus.php ذخیره می‌کنیم؛ enum زیر اسمش PostStatus هست و نوع بازگشتی اون از نوع string هست:حالا ما می‌تونیم از این enum برای casting استفاده کنیم؛ فرض کنید ما توی migration خودمون اومدیم و یک ستون به اسم status از نوع enum تعریف کردیم که فقط یک مقادیر خاصی مثل draft و published رو پذیرا هست و حتی با متد default می‌تونیم براش یک مقدار پیش فرض هم تعریف کنیم و این ستون به غیر از این مقادیر حق نداره چیز دیگه ای رو داشته باشه:خب، اما تعریف enum به صورت بالا (در قالب آرایه) می‌تونه گاهی باعث ایجاد تداخل بشه، مثلا همزمان چندین دولوپر که روی پروژه کار می‌کنن، ممکنه که این مقادیر رو دستکاری کنن و داستان ایجاد بشه، برای همین ما اومدیم و داخل app پوشهٔ Enums رو ساختیم که شامل فایل‌های enum ما به عنوان مرجع میشه و حالا با casting به مدل خودمون می‌فهمونیم که فلان فیلد، حتما باید مقادیرش از فلان enum خونده بشه:حالا اگر یک مقدار invalid برای status ست کنیم؛ به error برخواهیم خورد.برای ست کردن enum به جای string می‌تونیم از خود enum ایجاد شده (باید داخل فایل مال use بشه) استفاده کنیم:$post-&gt;update([&#039;status&#039; =&gt; PostStatus::Archived]);از enum می‌تونیم به عنوان type hint هم استفاده کنیم و فقط مقادیر مجاز رو دریافت کنیم:خب حالا اگر بزنیم localhost/8000/posts/3/something چون something غیر مجازه، صفحهٔ 404 برمی‌گره ولی اگر بزنیم localhost/8000/posts/3/draft مقدار رو برای ما نمایش میده:روش جدید نوشتن accessor و  mutator:این روش جدید باعث سادگی کار ما میشه.فرض کنید با روش زیر یک پست رو گرفتیم:و این محتوای پست ما هست:حالا می‌خوایم یک مقداری رو به این مقدار اضافه کنیم؛ مثلا path رو، برای این کار باید از accessor استفاده کنیم؛ که فرم جدید نوشتن اون به شکل زیر هست؛ اما نکته مهم اینه که میشه اون رو به دو فرم نوشت؛ یکی با استفاده از متد get مربوط به Accessor (که حتما باید use بشه) و روش دیگه با استفاده از یک شی از Attribute:مبحث setter:ذخیره تمامی username ها با حروف کوچک در دیتابیس، ولی نمایش با حروف بزرگ در view:خب، از قبل جدول users ما فیلد username رو نداشت؛ برای همین باید به مایگریشن اون فیلد username رو اضافه کنیم:و باید به فکتوری هم مقدار بدیم تا پرش کنه:و باید migrate کنیم:php artisan migrate:fresh --seedحالا توی مدل User میاییم و mutator و accessor رو تعریف می‌کنیم؛ arrow function اول میشه getter و دومی میشه setter که البته توی این نسخه امکان اینکه بهشون اسم بدیم و جابه‌جا استفاده کنیم هم وجود داره که جلوتر توی تصویر میاد:اینطوری برای آرگومان‌های Attribute اسم انتخاب می‌کنیم:حالا اگر با استفاده روت زیر بخواییم به مقدار یک username دسترسی داشته باشیم؛ به صورت ucwords برای ما برگشت داده میشه (باید توجه داشته باشیم که Attribute رو حتما use کنیم).حالا اگر بخواییم یک user جدید ایجاد کنیم؛ mutator یا همون setter ما صدا زده میشه:خب، حالا username بالا به صورت حروف کوچیک توی db ذخیره میشه و پایین‌تر وقتی return میشه، به صورت Capitalize خواهد بود چرا که برای دریافت مقدار getter صدا زده میشه و حرف اولش رو بزرگ می‌کنه.آپدیت از لاراول از ورژن ۸ به ۹:وقتی قراره آپدیت کنیم؛ حتما باید سازگاری همهٔ بسته‌های مورد استفاده روی پروژه رو چک کنیم و باید صبر کنیم تا همه اون‌ها آپدیت بشن و بعد برای آپدیت کل پروژهٔ خودمون اقدام کنیم. حتما باید release note رو چک کنیم و اگر نسخهٔ لاراولی که به End of life نرسیده و مشکل خاصی نداره رو می‌تونیم آپدیت نکنیم.برای آپدیت هم معمولا از نسخهٔ پایین‌تر به بالاتر اینطوری عمل میشه که مثلا از n به n+1 نه n+m. ما باید طبق مستندات update اقدام کنیم که شامل تغییرات بزرگ و متوسط میشه و توی هر بخش میزان اثرگذاری هم کدوم از آپدیت‌ها و تغییرات رو میگه.قدم اول معمولا تغییر بسته‌ها و یا نسخه‌های اون ها داخل composer.json هست و بعدش باید composer update بزنیم.حالا تغییرات ریز و درشت دیگه‌ای هم ممکنه داشته باشیم که باید طبق مستندات اون‌ها رو اصلاح کنیم.تغییر باندلر فرانت از webpack به vite:باندلر در واقع یک ابزار و راهکار برای خروج از جهنم وابستگی‌ها هست؛ باندلرها اینطوری عمل می‌کنن که کتابخونه‌های مختلف یک زبان برنامه‌نویسی مثل js یا ruby رو به ترتیب اولویت و پیش‌نیاز برای بخش‌های مختلف در یک فایل یکتا بسته‌بندی‌اش می‌کنن (باندل یا پیچیدن درون بقچه!) و دیگه نیازی نیست نگران وابستگی های مختلف کدهامون به فایل‌ها و کتابخونه‌های مختلف باشیم.توی نسخهٔ جدید لاراول هم میشه از laravel mix استفاده کرد و هم از vite؛ اما بهتره که از vite استفاده کنیم؛ فایل کانفیک vite داخل root پروژه و به اسم vite.config.js قرار داره اینم محتوای اونه:خب طرز کار تقریبا مشابه همون webpack و laravel mix هست؛ اول باید npm i بزنیم تا پکیج‌های داخل package.json مثل خود vite نصب بشن؛ طبق تصویر بالا vite میاد و هر چیزی که توی بخش input گذاشتیم رو به عنوان فایل js یا css برای ما serve می‌کنه، ما در دو حالت build و  dev می‌تونیم از vite استفاده کنیم؛ اگر npm run dev رو اجرا کنیم؛ فایل‌های js و  css ما روی لوکال هاست و روی یک پورت خاصی serve میشن و صفحهٔ blade ما میاد و اون‌ها رو از اون ادرس می‌خونه، اما اگر دستور npm run build رو بزنیم؛ فایل app و style رو داخل پوشهٔ public برای ما ایجاد می‌کنه.برای دسترسی به این فایل‌ها داخل blade هم باید از directive زیر استفاده کنیم:استفاده از پیش پردازنده sass و less در vite:فرض کنیم که یک فایل sass در مسیر resources/scss/app.scss داریم و می‌خوایم کامپایلش کنیم؛ برای این کار، باید اول بستهٔ sass رو با دستور npm i -D sass نصب کنیم:حالا باید resources/scss/app.scss رو به input فایل vite اضافه کنیم و همین‌طور باید مسیر رو به vite directive اضافه کنیم؛ و در نهایت npm run dev و یا npm run build رو ران کنیم تا تغییرات اعمال بشه.نصب فریمورک bootstrap:npm install bootstrapاین فریمورک داخل node_modules نصب میشه؛ حالا ما باید این فایل رو به app.js داخل resources/js/app.js ایمپورت کنیم:نکته: دستور import به صورت پیش فرض فایل‌ها رو از node_modules می‌خونه به خاطر همین اول bootstrap مسیر دقیق رو ندادیم.یه خوبی vite اینه که این قابلیت رو به ما میده تا فایل css رو هم داخل یه فایل js مثل همون app.js ایمپورت کنیم:روش دیگه import کردن فایل css داخل فایل scss ما هست: </description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Fri, 26 Aug 2022 20:59:02 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت دوازدهم</title>
                <link>https://virgool.io/laravel-community/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%AF%D9%88%D8%A7%D8%B2%D8%AF%D9%87%D9%85-hm9bzwhdu3zm</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.قسمت ۱۴۴ تا ۱۵۷آشنایی با ویژگی‌های جدید لاراول ۸:لاراول هر ۶ ماه یک ورژن جدید میده، نسخه‌های LTS پشتیبانی ۲ ساله برای رفع باگ و ۳ ساله برای باگ های امنیتی داره، اما نسخه‌های غیر LTS پشتیبانی ۷ ماهه رفع باگ و ۱ ساله امنیتی دارن.توی لاراول ورژن ۸، ورژن php حتما باید برابر یا بزرگتر از ۷.۳ باشه، برای آپدیت می‌تونیم XAMPP رو آپدیت کنیم و حتما قبلش باید از دیتابیس‌های خودمون بک‌آپ بگیریم.وقتی composer نصب میشه، متغیرهای محیطی رو ست می‌کنه و به راحتی به php و... داخل CLI دسترسی داریم. پس می‌تونیم به جای پاک کردن XAMPP نسخه جدید php رو نصب کنیم و env جدید رو ست کنیم.مبحث Model Directory:توی نسخه‌های قبلی وقتی یک Model ایجاد می‌کردیم؛ می‌رفت و به صورت Name.php داخل پوشهٔ app قرار می‌گرفت؛ اما توی نسخهٔ جدید داخل app/Models قرار می‌گیرن و namespace ما میشه App/Models.بهبود artisan serve:قبلا اگر فایل .env رو تغییر می‌دادیم و server ما ران شده بود؛ حتما باید اون رو می‌بستیم و دوباره اجرا کنیم؛ اما توی نسخهٔ جدید به محض تغییر در فایل env، خود سرور restart میشه.مبحث Routing namespace:قبلا برای دسترسی به متد یک کنترلر می‌زدیم:Route::get(&#039;/home&#039;, &#039;HomeController@index&#039;);اما حالا باید اینطوری عمل کنیم:Route::get(&#039;/home&#039;, [HomeController::class, &#039;index&#039;]);که فضای نام HomeController رو باید use کنیم:use App\Http\Controllers\HomeControllerاز اونجایی که ::class برای ما string اون فضای نام رو برمی‌گردونه، پس می‌تونیم بنویسیم:Route::get(&#039;/home&#039;, [&#039;App\Http\Controllers\HomeController&#039;, &#039;index&#039;]);برای resource هم به شکل زیر عمل میشه:Route::resource(&#039;posts&#039;, PostController::class);توی ورژن‌های قبلی لاراول namespace ما use می‌شد؛ اما توی ورژن ۸ این اتفاق نمی‌افته، اما می‌تونیم لاراول رو طوری تنظیم کنیم که این اتفاق بیفته، برای این کار باید مقدار زیر رو در RouteServiceProvider.php از کامنت در بیاریم:protected $namespace = &#039;App\\Http\\Controllers&#039;;با این کار به راحتی می‌تونیم به همون روش قدیمی و فرمول زیر به متدهای کنترلر خودمون دسترسی داشته باشیم:Route::get(&#039;/routename&#039;, &#039;NameController@method&#039;);اما بهتره که namespace رو خودمون دستی صدا بزنیم و از اصول لاراول پیروی کنیم.مبحث Model Factory Class:همون‌طور که از قبل می‌دونیم؛ factory یک ویژگی‌ هست که کار با seeders رو برای ما راحت‌تر می‌کنه؛ Factoryهای ما توی پوشهٔ database/factories قرار دارن و به فرم زیر هستن:توی کد بالا، یک متغیر از نوع protected به اسم model داریم که مقدار User::class داره، به همین خاطر به Factory می‌گیم Model Factory.کلاس فکتوری یک متد داره به اسم definition که توش فیلدهای جدولی که قراره پر بشه رو میاریم و به کمک کتابخونهٔ faker پرش می‌کنیم.بریم برای ساخت یک Factory واقعی توی لاراول ۸:php artisan make:factory PostFactory --model=Postحالا از قبل باید Migration و Model رو داشته باشیم و ستون مد نظر رو توی Migration ایجاد کنیم و migrate بزنیم تا توی دیتابیس اعمال بشه، حالا برای پر کردن ستون می‌تونیم به روش زیر عمل کنیم:حالا برای صدا زدن Faker می‌تونیم به روش زیر عمل کنیم:PostFactory::new()-&gt;create();روش دیگه استفاده از مدل Post هست:Post::factory()-&gt;create();یک متدی داریم به اسم make و تفاوتش با create اینه که دادهٔ فیک ایجاد می‌کنه، اما توی دیتابیس و جدول ذخیره نمی کنه:Post::factory()-&gt;make();با متد count هم به راحتی می‌تونیم تعداد دیتای فیکی که قراره ایجاد بشه رو مشخص کنیم:Post:;factory()-&gt;count(5)-&gt;create();نمایی از جدول posts:تغییر بزرگ لاراول Jeststream:جت استریم در واقع میاد و یک scaffolding سمت فرانت برای ما ایجاد می‌کنه که کلی قابلیت داره، مثل:۱- لاگین۲- رجیستر کردن۳- تایید ایمیل۴- تایید هویت دو مرحله‌ای۵- مدیریت سشن‌ها۶- ساپورت از Laravel Santcum APIو...جت استریم به صورت دیفالت از فریمورک Tailwind CSS استفاده می‌کنه، اما میشه به جای اون از Livewire و یا Inertia هم استفاده کرد.در واقع Jetstream که به وسیله Taylor Otwell نوشته شده، کمک می‌کنه که بخش‌های مختلف فرانت پروژه رو خیلی سریع با کمک Tailwind CSS و Livewire و Inertia توسعه بدیم. اما طبیعتا اجباری در استفاده از اون نیست و ما همچنان می‌تونیم از بسته Laravel/ui برای توسعه فرانت کمک بگیریم.این بسته در بک‌اند از پکیج Fortify برای پیاده‌سازی auth و... کمک می‌گیره که این خیلی مهمه و ما می‌تونیم از این بسته با فرانت دلخهواه خودمون مثل ‌Bootstrap استفاده کنیم.آشنایی مقدماتی با فریمورک Tailwind:یک فریمورک برای CSS (مثل bootstarp و materialize) که به راحتی قابل کاستوم کردنه، علت اینکه خیلی راحت میشه یک طرح کاستوم باهاش زد، اینه که این یک فریمورک سطح پایین هست و چیزهایی مثل کامپوننت‌های آمادهٔ بوت استرپ رو اینجا نمی‌بینیم؛ مثلا دیگه کلاسی برای ایجاد card وجود نداره و این برنامه نویس هست که باید با کنار هم قرار دادن کلاس‌های مختلف (به صورت ماژولار)، اون چیزی که دنبالش هست رو ایجاد کنه.آشنایی مقدماتی با Livewire:خب livewire یک فریمورک فول استک برای لاراوله، این فریمورک فقط و فقط روی لاراول کار می‌کنه و هدفش ایجاد یک اینترفیس داینامیک برای ما هست تا بدون refresh شدن صفحه، اکشن‌های خاصی رو انجام بده (ارتباط با سرور بدون نیاز به رفرش کردن صفحه).در واقع این فریمورک بین فرانت و بک پروژهٔ لاراولی سیم‌کشی می‌کنه و بینشون ارتباط برقرار می‌کنه، بدون نیاز به ریلود یا رفرش کردن صفحه.آیا با اومدن چیزی مثل Livewire باید فریمورک‌های فرانت مثل Vue و React رو بذاریم کنار؟خیر، چرا که با توجه به هدف پروژه ما میاییم و ابزار رو انتخاب می‌کنیم؛ مثلا اگر قرار باشه فرانت پروژه کاملا از بک پروژه جدا باشه، بک پروژه ممکنه روی یک سرور باشه و فرانت روی یک سرور دیگه و... پس مجبوریم از فریمورک‌های فرانتی استفاده کنیم.اما livewire توی پروژه‌های کوچکتر کاربرد داره و Front و Back هر دو لاراولی هستن، اما این مزیت بزرگ رو ایجاد می‌کنه که بدون نیاز به دانش عمیق فرانت، اپلیکیشن‌های خوبی رو پیاده‌سازی کرد.آشنایی مقدماتی با Inertia:با استفاده از Inertiajs میشه یک single page application ایجاد کرد؛ در واقع این Inertia به ما کمک می‌کنه که Dynamic Interface ایجاد کنیم؛ بدون اینکه نیازی به API داشته باشیم. Inertia برخلاف Livewire که اصلا از فریمورک‌های فرانتی استفاده نمی‌کنه، از فریمورک‌های فرانتی استفاده می‌کنه، با این تفاوت که API دیگه وجود نداره.آشنایی با بستهٔ Fortify:بستهٔ Fortify لاراول یک Back-end برای سیستم Authentication هست که مستقل از frontend عمل می‌کنه. قبلا برای سیستم auth از laravel/ui استفاده میشد که مشکل اصلی اون این بود که فقط از bootstrap، React و Vue پشتیبانی می‌کرد و برای سایر فریمورک‌های باید یه سری customization انجام می‌دادیم؛ اما توی Fortify دیگه وابسته به فرانت نیستیم و این سیستم مستقل از فرانت در بک پیاده‌سازی میشه.توی Fortify کافیه که input های خودمون رو به روت‌های Fortify ارسال کنیم تا خودش باقی کارها رو انجام بده. ما یه سری داده رو ارسال می‌کنیم به روتی که نیاز داریم و بقیه کارها در سمت بک به وسیله این بسته هندل میشه.برای نصب این بسته:composer require laravel/fortifyبعد هم باید منابع Fortify رو publish کنیم:php artisan vendor:publish --provider=&amp;quotLaravel\Fortify\FortifyServiceProvider&amp;quotبا دستور بالا، Service Provider این بسته رو از vendor به پوشهٔ Providers پابلیش کردیم. و در نهایت باید یک بار migrate بزنیم تا ستون‌های مورد نیاز Fortify در جدول users اضافه بشه.php artisan migrateیکی از این ستون‌ها، two-factor_secret هست و دیگری two_factor_recovery_codes.نکته بعدی اینکه حتما باید FortifyServiceProvider رو به فایل app.php در config اضافه کنیم:&#039;providers&#039; =&gt; [ App\Providers\FortifyServiceProvider::class, ...];با نصب Fortify و اضافه کردن Service Provider اون به پروژه و عملیات migrate و تنظیم app.php حالا باید بریم ببینیم که چه بلایی بر سر پروژه اومده و چه چیزهایی اضافه شده:توی پوشهٔ app یک پوشه به اسم Actions به وجود اومده که داخل اون یک پوشه به اسم Fortify هست که یک سری از کلاس‌های این بسته درش قرار دارن:فایل FortifyServiceProvider.php هم که دو تا متد boot و register داره:توی همین متد boot می‌تونیم view لاگین، رجیستر و... رو تعیین کنیم.فایل مهم بعدی مربوط به کانفیگ‌های Fortify میشه که توی پوشهٔ config با نام fortify.php قرار گرفته. این کانفیگ شامل مواردی مثل لاگین با username یا email، تغییر فیلد email، فعال و غیرفعال‌سازی featureها و... میشه.خب، حالا بریم سراغ اینکه view مربوط به صفحهٔ لاگین رو تعریف کنیم و توی متد boot مربوط به FortifyServiceProvider اون view رو فراخوانی کنیم:خب یه فایل login.blade.php هم باید توی resources ایجاد کنیم و یه المنت فرضی توش بذاریم. نکتهٔ مهم بعدی هم اینه که برخلاف laravel/ui در fortify نیازی به پیاده‌سازی route برای صفحهٔ authentication نداریم. حالا با رفتن به localhost:8000/login وارد صفحهٔ لاگین می‌شیم.برای رجیستر هم می‌تونیم همین کار رو بکنیم:توی laravel/ui بسته‌های مورد نیاز به package.json اضافه میشدن و به راحتی می‌تونستیم نصبشون کنیم؛ اما توی بحث fortify باید این کار رو (یعنی نصب بوت استرپ) رو دستی انجام بدیم:نکته: اون خط‌های قرمز که نشون دهندهٔ خطا هست؛ مشکلی نداره و توی فیلم آموزشی بعد از اینکه vscode رو ری استارت کرد؛ حل شد، ظاهرا توی شناختن namespace مربوط به fortify مشکل داشت که حل شد.برقراری ارتباط بین فرانت و Fortify:برای استفاده از Bootstrap داخل پروژهٔ لاراولی خودمون چندین راه داریم:۱- نصب از طریق npm۲- استفاده از CDN۳- دانلود فایل‌ها و استفاده از اون داخل public (دسترسی با asset helper function)در واقع قبلا که با laravel/ui کار می‌کردیم؛ این بسته می‌اومد و با npm که یک پکیج منجر هست سه تا کتابخونهٔ Vue.js یا react و یا bootstrap رو برای ما نصب می‌کرد؛ در واقع بسته‌های مورد نیاز به package.json اضافه میشد؛ بعد ما نصبش می‌کردیم و با laravel mix ازش خروجی می‌گرفتیم.روش حرفه ای تر نسبت به CDN و استفاده از فایل‌های دانلودی در public، استفاده از package manager هست. یعنی با npm هر چیزی رو که نیاز داریم نصب کنیم و با laravel mix در نهایت compile کنیم و بزنیم تنگ پروژه. برای این کار یک پوشه به اسم css و js در public می‌سازیم و با mix خروجی نهایی رو اونجا قرار می‌دیم.برای نصب بوت استرپ و ملزوماتش با npm اول از همه یک npm install می‌زنیم تا بسته‌های زیر نصب بشه:بعدش نیازمندی‌های بوت استرپ رو نصب می‌کنیم:npm install bootstrap popper.js sass jquery sass-loaderبسته sass-loader برای webpack هست که میاد و sass رو برای ما compile می‌کنه. تمامی این بسته‌ها توی پوشهٔ node_modules توی root پروژه نصب میشن.خب حالا باید با laravel mix بیاییم و فایل‌های css و js رو کامپایل کنیم و توی js و css داخل public قرار بدیم؛ در واقع خود laravel mix میاد و از webpack برای این کار استفاده می‌کنه.توی تصویر بالا داریم فایل app.js رو داخل پوشهٔ resources/js می‌بینیم که اومده و فایل bootstrap رو require کرده اما باید توجه داشته باشیم که این بوت استرپ راه‌انداز ما هست و نه اون فریمورک css. اگر نگاهی به فایل bootstrap.js که در واقع راه‌انداز ما هست بندازیم می‌بینیم که میاد و یه سری ابزار و بسته رو require می‌کنه، در نهایت laravel mix با استفاده از webpack میاد و این بسته‌ها رو commpile و تبدیل به یک فایل واحد می‌کنه و داخل public/js قرار میده:برای اینکه بتونیم bootstrap و jquery و popper.js رو هم کامپایل کنیم؛ باید خطوط زیر رو به راه انداز اضافه کنیم:وقتی متغیر رو به صورت window.name تعریف می‌کنیم در واقع اون name به صورت global در دسترس هست.نکته: توی تصویر بالا که اومده مثلا popper.js یا jquery رو require کرده فقط و فقط اسم بسته رو آورده که منظورش اینه که از node_modules بخون، اما توی app.js که راه‌انداز رو require کرده دقیقا گفته که ./bootstrap یعنی توی پوشهٔ جاری فایل bootstrap که البته میشد ./bootstrap.js هم نوشت.برای کامپایل شدن فایل‌های sass مربوط به بوت استرپ هم باید داخل resources/sass فایل app.scss رو ایجاد کنیم و داخلش عبارت زیر رو قرار بدیم تا بیاد و فایل scss مربوط به بوت استرپ رو از node_modules لود کنه:حالا باید فایل‌های موجود در node_modules رو که توی resources فراخوانی شدن، با استفاده از لاراول میکس و webpack کامپایل کنیم تا به صورت یک فایل جاوااسکریپتی یا css یکتا در بیان و خیلی راحت وابستگی هاشون هندل بشه و توی پروژه لود بشن.در زیر تصویری از webpack.mix.js رو می‌بینیم که توش laravel-mix رو require کردیم؛ بعدش هم با استفاده از متدهای js و sass اومدیم و گفتیم که فایل‌های موجود در app.js و app.scss رو برای ما توی public کامپایل کنه:برای کامپایل کردن، میزنیم:npm run devبعد از کامپایل این رو می‌بینیم:حالا خیلی راحت توی فایل‌های blade اون‌ها رو صدا می‌زنیم:طراحی صفحات Login و Register و ایجاد ارتباط میان input‌ها با Fortify:برای هندل کردن لاگین با Fortify باید داخل FortifyServiceProvider با استفاده از متد loginView بیاییم و view مربوط به لاگین رو صدا بزنیم:برای رجیستر، reset password و ...:محتوای فایل‌های blade مربوط به سیستم fortify auth رو اینجا آپلود کردم که البته در دسترس عموم نیست! ولی با ثبت نام توی webprog می‌تونید دانلودش کنید.تغییر سیستم لاگین از email به username:برای این کار باید یک فیلد username به migration جدول users اضافه کنیم و اون رو از نوع unique ست کنیم:و باید:// drop all tables and then run migration
php artisan migrate:freshحالا باید یک input برای user به blade فایل اضافه کنیم؛ بنابراین توی register.blade.php باید این فیلد رو به شکل زیر اضافه کنیم:خب view ما به شکل زیر در میاد:حالا باید کدهای مربوط به validation و ثبت user رو به CreateNewUser توی app/Actions/Fortify اضافه کنیم. در زیر کدهای validation مربوط به username رو می‌بینیم که بهش گفتیم unique:users یعنی داخل جدول users یکتا و واحد باشه.ثبت شدن رو باید به متد create هم اضافه کنیم:و حتما باید username رو به متغیر fillable داخل مدل User هم اضافه کنیم:باید view مربوط به login.balde.php رو هم برای پذیرش username اوکی کنیم:و view زیر رو تحویل می‌گیریم:حالا باید به Fortify هم بگیم که به جای email با username لاگین کنه، پس باید config/fortify.php رو اینطوری تنظیم کنیم:ایمن‌سازی رمز عبور:باید وارد app/Actions/Fortify/PasswordValidationRules بشیم و اونجا ruleهای جدید رو وارد کنیم. ما با اضافه کردن قطعه کد زیر، به لاراول گفتیم که برای password حتما باید ترکیبی از رشته و اعداد باشه:(new Password)-&gt;requireNumeric()برای اینکه حروف بزرگ، کوچک و کاراکترهای خاص رو هم شامل بشه:</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Mon, 22 Aug 2022 01:18:46 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت یازدهم</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%DB%8C%D8%A7%D8%B2%D8%AF%D9%87%D9%85-gly674yog8sw</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.قسمت ۱۴۱ تا ۱۴۳آشنایی با HTTP Client:این ویژگی در لاراول ۷ اضافه شده و به ما کمک می‌کنه تا به راحتی یک درخواست به سمت یک API ارسال کنیم و جوابش رو دریافت کنیم.برای کار با HTTP Client ما نیاز به کتابخانهٔ Guzzle داریم؛ Guzzle یک کتابخانهٔ PHP هست برای کار با درخواست‌ها که لاراول هم از این کتابخونه استفاده می‌کنه. توی ورژن ۷ لاراول این کتابخانه به صورت پیش فرض وجود داره و نیازی به نصب نیست؛ اما اگر ورژن قدیمی‌تر باشه باید حتما با composer نصب بشه.توی این بخش از jsonplaceholder استفاده می‌کنیم که Fake API به ما میده.آشنایی با متد get از HTTP:برای این کار باید Http facade رو به پروژه اضافه کنیم:خروجی که به ما میده:به راحتی می‌تونیم روی response متدهای مورد نیاز رو هم ران کنیمگ مثلا:dd($response-&gt;body());که خروجی زیر رو می‌گیریم:یا به جای body که string برمی‌گردونه، می‌تونیم از json استفاده کنیم که array برمی‌گردونه. حتی می‌تونیم روی خود $response بدون اینکه روش متد json بزنیم؛ دیتا رو دریافت کنیم:(البته این API چیزی به اسم data نداره و فقط برای مثال بود).یا مثلا از متد status استفاده کنیم تا ببینیم وضعیت response چطور بوده؛ یه متد جالبی هم داریم به اسم ok که میاد و به ازاری هر response که status کدش توی رنج ۲۰۰ تا ۳۰۰ بوده true برمی‌گردونه.برای رنج ۴۰۰ تا ۵۰۰ هم clientError رو داریم؛ مثلا اگر درخواست دسترسی بدیم به بخشی که اصلا وجود نداره و ۴۰۴ رو برای ما برمی‌گردونه.متد serverError هم برای هندل کردن خطاهای سمت سرور ۵۰۰ و... هست.متد throw برای هندل کردن exception هست.متد get ورودی هم می‌گیره که اینطوری هست:متدی هم داریم برای تعیین timeout، یعنی تعیین کنیم که اگر یک API بیش از اندازه طول کشید تا ران بشه، timeout بده و مثلا یک خطا رو throw کنیم:متد post و سایر متدهای کاربردی HTTP Client:یک پست ستون‌های UserID id title body رو داره:حالا اگر بخوایم یک پست جدید ایجاد کنیم؛ باید این پارامترها رو بهش پاس بدیم:به راحتی می‌تونیم برای دیدن نتیجه خروجی json رو روی response ببینیم:dd($response-&gt;json());خروجی متد json روی response اینه:اگر بخوایم که داده‌های پست رو به شکل form ارسال کنیم؛ باید از متد asForm استفاده کنیم:همچنین متد attach رو داریم که از اون برای پیوست فایل استفاده می‌کنیم که پارامترهای ورودی اون رو در پایین داریم می‌بینیم:پارامتر اول: اسم فیلدپارامتر دوم: مسیر فایلی که قراره آپلود بشهپارامتر سوم: اسم فایلی که قراره ذخیره بشهنحوه Authentication در API:متد اول withBasicAuth هست که یک auth معمولی و با user و pass هست:اگر auth بر اساس توکن باشه:ارسال دیتا در header:متد retry که برای ارسال مجدد درخواست هست:پارامتر اول (۳) تعداد دفعاتی هست که تلاش می کنیم؛ پارامتر دوم، بر اساس میلی‌ثانیه به فاصلهٔ فلان.</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Sat, 20 Aug 2022 12:39:29 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت دهم</title>
                <link>https://virgool.io/Rocket/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%AF%D9%87%D9%85-mso2lcca2otp</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.قسمت ۱۳۰ تا ۱۴۰آشنایی با دستورات Artisan Console:برای ایجاد command اختصاصی برای Artisan دو راه داریم:۱- ایجاد یک پوشه به اسم Comands در app/Console و قرار دادن commandها در آن۲- ایجاد یک روت در فایل console.php در routesروتی که به صورت دیفالت در console.php قرار دارد:در واقع ما از یکی از دو روش بالا برای ایجاد کامند استفاده می‌کنیم و نیازی نیست که وقتی مثلا یک روت تعریف کردیم؛ همون کامند رو به Commands هم اضافه کنیم.توی console.php ما از دستور Artisan::command برای تعریف کامند کمک می‌گیریم که آرگومان اول نام کامند و آرگومان دوم یک clouser function هست که در واقع با وارد کردن نام کامند (اینجا inspire) این clouser ران می‌شود.وقتی می زنیم php artisan که لیست کامندها رو برای ما میاره، توی بخش Available Commands اسم دستور inspire هم اومده که description اینه که برای ما جملات انگیزشی نشون بده!بریم برای ایجاد یک دستور سفارشی داخل console.php:خیلی ساده اینجا خروجی کار رو داریم:الان وقتی بزنیم php artisan برای ما توی کامندهای در دسترس، اسم test رو هم اضافه کرده:اما description نداره که به راحتی قابل اضافه کردنه:نکته: امکان استفاده از return به جای echo در Clouser function نیست.ارسال پارامتر به Command:برای این کار، اول داخل curly braces توی آرگومان اول clouser function اسم پارامتر دریافتی رو می‌نویسیم و به بعد به coluser function پاس می‌دیم:حالا اگر به حالت عادی و بدون آرگومان دستور greeting رو صدا بزنیم؛ Artisan خطای زیر رو نشون میده:اما وقتی بهش ورودی بدیم؛ خروجی زیر رو خواهیم داشت:وقتی help می‌گیریم؛ به ما آرگومان‌های ورودی و نحوهٔ استفاده رو نشون میده:اگر آرگومان ورودی ما توضیحات خاصی داشته باشه که باید توی help نشون بده، اینطوری واردش می‌کنیم:با استفاده از علامت سوال ؟ می‌تونیم یک آرگومان رو اختیاری کنیم؛ اما باید دقت داشته باشیم که حتما مقدار دیفالت براش در نظر بگیریم تا دچار مشکل نشیم:Artisan::command(&#039;greeting {name : Person Name} {family? : Person Family}&#039;, function($name, $family = &#039;&#039;){
    echo &amp;quotHi &amp;quot.$name.&amp;quot &amp;quot.$family.&amp;quot , Welcome!&quot;
})-&gt;describe(&amp;quotPrints a Greeting Message&amp;quot);به جای اینکه مقدار دیفالت family رو توی clouser ست کنیم؛ می‌تونیم توی آگومان اول به شکل زیر ست کنیم:Artisan::command(&#039;greeting {name : Person Name} {family=Alikhani : Person Family}&#039;, function($name, $family){     
    echo &amp;quotHi &amp;quot.$name.&amp;quot &amp;quot.$family.&amp;quot , Welcome!&quot; 
})-&gt;describe(&amp;quotPrints a Greeting Message&amp;quot);اضافه کردن Option به دستورات Artisan:برای این کار هم از {--name} استفاده می‌کنیم:حالا برای دسترسی به آپشن‌ها باید از یک تابع کمک بگیریم:حالا اگر این option رو توی command line وارد کنیم مقدار true برگشت داده میشه و اگر وارد نکنیم مقدار false.اما چطور آپشن‌ها رو مقدار پذیر کنیم؟ باید بعد از --age یک مساوی قرار بدیم:حالا توی کامندلاین وارد می‌کنیم و خروجی می‌گیریم:نکته: توی تصاویر بالا دیدیم که ما می‌تونیم یک آپشن کوتاه برای آپشن‌های بزرگمون انتخاب کنیم؛ مثلا --A به جای --Age، اما نکتهٔ مهم اینه که اینجا باید آپشن با -- باشه، اما موقع استفاده یا توی help با تک -.برای دسته‌بندی دستورات:توی تصویر بالا داریم می‌بینیم که اسم دسته رو در اون آپشن اول آورده:webprog:greetingمتدهای $this در فایل console.php که در واقع اشاره به Artisan دارد:ارسال داده به خروجی، بدون استفاده از echo که رنگ اون هم در خروجی زرد رنگه:با متد info می‌تونیم مثل comment داده رو به خروجی بدیم ولی با رنگ سبز. error داده رو با بک‌گراند قرمز و رنگ سفید به خروجی میده، question داده رو با بک‌گراند آبی و متن سفید به خروجی میده.متد line متن بدون بک‌گراند و سفید رنگ رو عین echo پرینت می‌کنه.حتی میشه از سیستم تگ بندی هم استفاده کنیم:متد table برای ایجاد جدول:خروجی ما میشه این:دریافت ورودی از کاربر:برای دریافت ورودی، کافیه اون رو توی یک متغیر ذخیره کنیم و هر بلایی خواستیم سرش در بیاریم:نمونه‌ای از خروجی کار:آشنایی با متد confirm:نحوه تغییر دادن مقدار دیفالت در confirm که در حالت عادی برابر با no هست:مقدار true برابر با yes هست و مقدار false برابر با no.متد choice برای انتخاب از بین چند گزینه:تعیین گزینهٔ دیفالت در choice:تعیین تعداد تلاش برای انتخاب از بین گزینه‌های choice:در حالت عادی تا زمانی که کاربر مقدار صحیح رو وارد کنه، ادامه پیدا می‌کنه، اما ما می‌تونیم تعداد دفعات attemp رو محدود کنیم:آخرین آرگومانی هم که متد choice قبول می‌کنه، تعیین حالت multiple هست؛ یعنی امکان انتخاب چندین گزینه وجود داره: متد کاربردی بعدی، متد secret هست که برای دریافت ورودی حساس مورد استفاده قرار می‌گیره، مثلا دریافت پسورد از کاربر که نمی‌خوایم وقتی کاربر ورودی رو تایپ می کنه روی صفحهٔ کنسول چیزی به نمایش در بیاد:$pass = $this-&gt;secret(&#039;Enter your password&#039;);مبحث Generating Commands در لاراول:برای ایجاد کامند در پوشهٔ Commands در داخل app باید از دستور زیر استفاده کنیم:php artisan make:command Nameمثلا:php artisan make:command Greetingمحتویات این فایل به شکل زیره:خب توی تصویر بالا یک متغیر از نوع protected داریم به اسم signature که مقدار command:name رو داره، command در واقع اسم گروه دستور هست و name اسم دستور ما:protected $signature = &#039;group:greeting&#039;;این کلاس یک پروپرتی description هم داره که می‌تونیم توضیحات کامند رو توش قرار بدیم:دستور make:command یک آپشن داره به اسم --command که می‌تونیم مستقیم اسم گروه و دستور رو توش مشخص کنیم و مجبور نشیم دستی signature رو تغییر بدیم:php artisan make:command Name --command=group:command_nameدریافت ورودی در Generating Commands:برای دادن ورودی به کامندهای موجود در app/Commands باید ورودی را در signature دریافت کنیم:protected $signature = &#039;group:command_name {name : persion_name} {--A|--age : age}&#039;;حالا کار clouser function رو توی کلاس‌های کامند، متد handle انجام میده:public function handle()
{
    echo &amp;quotHi &amp;quot.$this-&gt;argument(&#039;name&#039;).&amp;quot, your age is: &amp;quot.$this-&gt;option(&#039;age&#039;);
}روی $this دقیقا تمامی متدهایی که قبلا در مورشون خوندیم قابل ران شدن هست؛ مثل comment و input و... .آشنایی با متد کاربردی Call در Artisan:این متد زمانی کاربرد داره که ما قراره روی هاست اشتراکی سایت خودمون رو deploy کنیم و دسترسی به کامند لاین نداریم؛ پس وقتی کنسولی نباشه، نمی‌تونیم دستورات artisan رو ران کنیم؛ بنابراین باید از این روش استفاده کنیم؛ این روش اینطوری کار می‌کنه که ما یه route تعریف می‌کنیم و وقتی وارد این روت شدیم؛ اون کامند مربوط به artisan ران میشه:وقتی وارد این route بشیم؛ این میشه خروجی کار:خب، پس خیلی راحت می‌تونیم Jobهای Queue شده رو Listen و اجرا کنیم:نکتهٔ مهم در این پروسه اینه که چون http نمی‌تونه پیش از حد منتظر بایسته، و بعد از مدتی timeout error میده، باید از متد once استفاده کنیم و چون once مقداری نمی‌گیره، بهش مقدار true می‌دیم:توی تصویر بالا true رو نمیشد نشون داد، چون از صفحه ویدئو میزد بیرون، در واقع اینطوریه:&#039;--once&#039; =&gt; trueاما اگر بهش false پاس بدیم؛ دیگه اون آپشن در نظر گرفته نمیشه.مبحث Tinker:در واقع Tinker یک REPL خیلی قدرتمند برای لاراوله و مخفف Read Evaluate Print Loop هست؛ REPL یک محیط برنامه‌نویسی تعاملی هست که از کاربر ورودی می‌گیره اون رو ارزیابی می‌کنه و خروجی رو به کاربر میده. مثل console هایی که توی لینوکس و ویندوز داریم. یعنی منتظر می‌مونه بهش یک ورودی بدیم؛ اون رو ارزیابی می‌کنه و نتیجه‌اش رو پرینت می‌کنه و دوباره منتظر دریافت ورودی وایمیسه.برای دسترسی به Tinker از دستور زیر استفاده می‌کنیم:php artisan tinkerتصویر زیر محیط تعاملی tinker رو نشون میده:مثالی از کارکرد این محیط تعاملی در تصویر زیر اومده؛ اومدیم و از مدل User که در App قرار داره متد find رو فراخوانی کردیم و نتیحه رو در $user ذخیره کردیم:این محیط تعاملی tinker به ما کمک می‌کنه تا یه سری تست‌ها یا عملیات خاص رو بدون نیاز به تعریف روت و کنترلر و... انجام بدیم؛ مثلا خیلی راحت می‌تونیم از doc برای خودندن مستندات یک تابع/... استفاده کنیم:doc requestیا حتی می‌تونیم کدهای اون رو هم بخونیم:</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Wed, 17 Aug 2022 23:24:43 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت نهم</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%D9%86%D9%87%D9%85-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-bwmu1nqykply</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.قسمت ۱۲۴ تا ۱۲۹آشنایی با Job و Queue:در واقع Job معادل با کار و پروسه‌ای هست که قراره انجام بشه و Queue معادل لیست کارهایی هست که قراره انجام بشه. چرا از Job استفاده می‌کنیم؟یکی از دلایلش می‌تونه موکول کردن کارهای CPU Bound و... به زمانی هست که فشار کمتری روی سیستمه. مثلا Generate کردن یک گزارش‌گیری خیلی بزرگ از دیتابیس، در واقع می‌خوایم Background Processing انجام بدیم.یکی از دلایل دیگه اینه که قراره یک کاری، در یک زمان خاص انجام بشه، اینطوری می تونیم یک پروسه رو در یک زمان مشخص ران کنیم.تنظیمات درایور Queue ما در پوشهٔ config و فایل queue.php قرار داره، پارامتر default این فایل میاد و از env متغیر QUEUE_CONNECTION رو می‌خونه که به صورت پیش‌فرض روی sync قرار داره، این مقدار بیانگر اینه که Job در همان لحظه ران میشه و به زمان بعدی موکول نمیشه.کانکشن بعدی از نوع database هست که روی این مورد کار می‌کنیم؛ سرویس‌های خارجی هم برای این کار وجود داره که فعلا وارد بحث با اونا نمی‌شیم.توی queue.php بخش faild رو هم داریم که Jobهایی که ران نشدن یا با مشکل مواجه شدن رو برای بررسی بعدی ذخیره می‌کنه.نحوه ایجاد Job در لاراول:php artisan make:job TestJobبا دستور بالا داخل پوشهٔ app یک پوشهٔ جدید به نام Jobs ایجاد میشه که Jobهای ما داخل اون قرار می‌گیره، الان هم با دستور بالا فایل TestJob.php داخل این پوشه ایجاد شده:کلاس Job ما از اینترفیس ShouldQueue استفاده کرده و ۴ مورد رو use کرده:۱- Dispatchable: قابلیت fire یا dispatch شدن رو به جاب ما میده۲- InteractWithQueue: تعامل بین Job و Queue۳- Queueable: قابلیت قرار گرفتن Job در درون صف‌های ما۴- SerializeModels: نوعی فرمت Data مثل Json، علت استفاده از Serialize اینه که اطلاعات Jobها رو ذخیره کنیم؛ و اگر بعد از ۲ ساعت که جاب ران شد ولی fail شد؛ بتونیم اون دیتا رو داشته باشیم تا دوباره ازش استفاده کنیم.کار اصلی داخل متد handle جاب ما رخ میده، یعنی وقتی نوبت اجرای یک جاب داخل صف رسید؛ متد handle اون جاب فراخوانی میشه و کارش رو انجام میده.نحوهٔ Dispatch کردن یک Job:برای فراخوانی و fire کردن یک job می‌تونیم توی جای مد نظر، مثلا توی Controller اینطوری رانش کنیم:TestJob::dispatch();حالا اگر داخل متد handle مقداری رو dd کرده باشیم؛ خروجی رو برای ما نشون میده. علت این که سریعا dd اتفاق افتاده اینه که تنظیمات درایور روی sync قرار داره و به معنی اجرای فوری هست؛ برای زمان دار کردن Job میاییم و از database یا سایر سرویس‌های خارجی استفاده می‌کنیم که اینجا ما از database استفاده می‌کنیم.برای این که درایور Queue رو روی database قرار بدیم؛ باید داخل فایل env مقدار QUEUE_CONNECTION رو به database تغییر بدیم. حالا جاب‌های ما توی table ای به اسم jobs ذخیره میشن، ما در حالت پیش فرض این table وجود نداره و ما باید از artisan برای ایجادش کمک بگیریم:php artisan queue:table با اجرای دستور بالا، جدول مایگریشن جدول jobs ما ایجاد میشه:۱- فیلد queue: دسته‌بندی جاب‌های ما۲- payload: قرار گرفتن اطلاعات Serialize شده۳- attemps: دفعات تلاش برای ران شدن۴- reserved_at: تاریخ اجرا۵- available_at: زمان در دسترس بودن جاب۶- created_at: زمان ایجاد شدن جابحالا اگر یک بار دیگه dispatch کنیم؛ می‌بینیم که داخل جدول jobs جاب ما ایجاد شده، اما ران نشده:مقدار فیلد queue به صورت پیش فرض روی default هست؛ ما می‌تونیم جاب‌های خودمون رو دسته‌بندی کنیم و برای تعیین دسته‌بندی از این فیلد استفاده می‌کنیم. مثلا ممکنه جاب ما از نوع email باشه و...فیلد بعدی ما payload هست که اینطوریه:کمکی که دیتای Serialize شده به ما می‌کنه، اینه که می تونیم کلاس، تابع و موارد این چنین رو به راحتی توی دیتابیس ذخیره و بازیابی کنیم. در واقع به کار بازیابی اون کلاس یا تابع و... Unserialize کردن گفته میشه.برای صدا زدن این جاب ذخیره شده داخل دیتابیس باید از artisan کمک بگیریم:php artisan queue:work --queue=defaultتوی دستور بالا، مقدار --queue رو برابر با default قرار دادیم، این دیفالت در واقع همون مقداری هست که فیلد queue که نوع جاب رو مشخص می‌کنه (توی jobs table)، حالا اگر جاب ما از دسته‌بندی دیگه ای باشه، کافیه به جای default اون دسته‌بندی مثلا email رو قرار بدیم.بعد از اجرا کردن دستور بالا، جاب ما میره برای پردازش و dispatch شدن:نکتهٔ جالب اینه که بعد از پردازش شدن یک جاب، اون جاب از جدول jobs حذف میشه.نحوهٔ دسته‌بندی Jobs:php artisan make:job TestJobEmailحالا میریم برای dispatch کردن این جاب طبق دسته‌بندی خاص خودمون، برای این کار:TestJobEmail::dispatch()-&gt;onQueue(&#039;email&#039;);و حالا توی جدول این میشه:حالا به راحتی می‌تونیم رانش کنیم:php artisan queue:work -queue=emailاما اگر دستور queue:work رو بدون فلگ --queue ران کنیم؛ در واقع queue های داخل دسته‌بندی دیفالت ران خواهند شد.حالا اگر قرار باشه job رو با تاخیر ران کنیم؛ باید از delay استفاده کنیم:در واقع job زمانی اجرا میشه که توی available_at اومده.برای اجرای job در لحظه، می‌تونیم از دستور زیر استفاده کنیم:مبحث Failed Jobs:اگر به هر دلیلی خطایی رخ دهد و یک Job اجر نشود؛ این جاب در جدول failed_jobs داخل دیتابیس لیست می‌شود.خب برای تست، می‌تونیم به راحتی یک Exception رو داخل متد handle ران کنیم؛ اینطوری جاب ما failed میشه و جدول failed_jobs ما مقدار می‌گیره:وقتی queue:work رو ران می کنیم؛ این سرویس مدام در حال پایش هست که اگر job جدیدی اضافه شد؛ به موقع اون رو ران کنه، با ران شدن یک جاب، اون جاب از جدول jos کنار گذاشته میشه و اگر دچار اختلال بشه، میره و توی failed_jobs قرار می‌گیره.علاوه بر اینکه می تونیم جاب‌های failed شده رو توی یک View به نمایش بذاریم (طبیعتا روتر، کنترلر و ویو خودش رو می‌طلبه)، می‌تونیم از دستور زیر هم کمک بگیریم:php artisan queue:failedکه این میشه خروجیش:تعیین تعداد retry برای اجرای چندین باره یک جاب تا اضافه شدن به لیست failed:خود queue:work چندین فلگ داره، که یکی از این flagها tries هست:php artisan queue:work --tries=3 اگر بخوایم برای یک جاب tries اختصاصی تعریف کنیم باید داخل کلاس اون جاب این کار رو انجام بدیم، چون فلگ tries روی queue:work روی تمامی جاب‌ها عمل می کنه.نحوهٔ اجرای مجدد Jobهای failed شده:php artisan queue:retry id/allدستور retry جاب رو از جدول failed خارج و دوباره وارد صف jobs می‌کنه... حالا اگر queue:work بزنیم و مشکل خاصی در اجرا نباشه، اجرا میشه و اگر هم مشکلی داشته باشه، دوباره از jobs وارد failed_jobs میشه.</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Fri, 12 Aug 2022 22:11:49 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت هشتم</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%D9%87%D8%B4%D8%AA%D9%85-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-a5gwcxl1hb40</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.قسمت ۱۰۷ تا ۱۲۳مبحث Mail:لاراول از کتابخونهٔ Swift Mailer برای ارسال ایمیل استفاده می‌کنه.تنظیمات mail در لاراول:باید وارد env و بخشی بشیم که با MAIL شروع میشه:همون‌طور که توی تصویر بالا داریم می‌بینیم MAIL_MAILER ما از نوع پروتکل smtp یا simple mail transfer protocol ست شده، حالا اگر وارد پوشهٔ config و فایل mail.php بشیم؛ می‌بینیم که default همین smtp ست شده، اما میشه از سرویس‌های بیرونی دیگه هم استفاده کرد، مثل ses و... توی این فایل چیزهایی مثل from و... هم از env خونده میشه.کلاس ارسال ایمیل:php artisan make:mail TestMailاین کلاس در پوشه‌ای به نام Mail در app ایجاد می‌شود. کلاس میل ما از کلاس مادری Mailable ارث‌بری داره. این کلاس یک متد داره به اسم build که عملیات ارسال ایمیل رو انجام میده. داخل این متد یک view برگشت داده میشه.خب حالا ما چطوری این کلاس رو صدا بزنیم تا کار ارسال ایمیل رو برای ما انجام بده؟خیلی ساده می‌تونیم داخل یکی از کنترلرهای خودمون مثلا HomeController به شکل زیر صداش کنیم:Mail::send(new TestMail());و در متد build مقدار زیر رو return می‌کنیم:return $this-&gt;view(&#039;your.view&#039;)-&gt;subject(&#039;Your subject&#039;)-&gt;to(&#039;aref@gmail.com&#039;);
// instead of view method, we can use text, the text method does not render
// the HTML codes, just return whatever in the blade fileمتنی که ایمیل میشه، در واقع چیزی هست که داخل viewی ما اومده.ارسال پارامتر به کلاس میل سفارشی خودمون:توی بحث View Data ما می‌خوایم اطلاعات رو از کنترلر که اون رو مثلا از یه مدلی دریافت کردیم؛ به کلاس ایمیل خودمون ارسال کنیم و از اونجا مثلا پاس بدیم به view مربوط به ایمیل تا برای ما ایمیل رو ارسال کنه، اینجا توی کنترلر یک user رو از مدل User خوندیم و به عنوان پارامتر به کلاس TestMail که خودمون ایجاد کردیم؛ ارسال کردیم:حالا این پارامتر رو در constructor دریافت می‌کنیم و اون رو به یک متغیر کلاسی assign می‌کنیم:حالا خیلی ساده، می‌تونیم از این property در هر قسمتی از این کلاس که می‌خوایم استفاده کنیم؛ یا حتی پاسش بدیم به view خودمون، اما جالب اینه که پاس دادن این پارامتر به view به صورت دیفالت انجام میشه و کافیه که ما در view اینطوری بهش دسترسی داشته باشیم:اینم تصویری از build:نحوهٔ Attach کردن فایل به ایمیل:برای این کار ما از متد attach روی شی TestMail استفاده می‌کنیم. خب ما اینجا فایل خودمون رو در storage/app/public قرار می‌دیم و اینطوری از دید کاربران مخفی می‌مونه. حالا یک فایل در public ایجاد می‌کنیم با اسم myfile.txt و اینطوری داخل build عملیات attaching رو انجام می دیم:** نکته: توی سرورهای ویندوز، بزرگی و کوچیکی حروف در path مسالهٔ مهمی نیست؛ اما در لینوکس حروف بزرگ و کوچیک با هم متفاوت هستن و اسلش و بک اسلش هم که مشخصه، اما لاراول slash و back-slash رو خودش هندل می‌کنه و بهتره که از slash استفاده کنیم.توی تصویر بالا، همون‌طور که مشخصه اومدیم روی کلاس TestMail که با this بهش اشاره کردیم متدهایی رو ران کردیم که یکیش attach هست؛ به این متد هم تابع storage_path رو به همراه مسیر فایلی که درون مسیر storage هست پاس دادیم و از این طریق به راحتی فایل رو بهش ضمیمه کردیم.اگر بخوایم چند تا فایل رو ضمیمه کنیم؛ باید دوباره یک متد attach روش بزنیم. این متد به عنوان آرگومان دوم یک آرایه می گیره که می‌تونیم توش یه سری تنظیمات داشته باشیم، مثلا  با کلید as می‌تونیم اسم فایلی که ارسال میشه رو rename کنیم:مبحث Preview E-Mail:یعنی قبل از ارسال، یک پیش‌نمایشی از ایمیل داشته باشیم. برای این کار یک Route تعریف کردیم برای preview گرفتن از ایمیل‌های خودمون:استایل‌دهی به ایمیل با Mark Down:اپ آنلاین برای استایل‌دهی به مارک داونوقتی می‌خوایم یک کلاس میل بسازیم با استفاده از آپشن -m می‌تونیم قالب رو از نوع Mark Down تعریف کنیم.php artisan make:mail MarkDownMail --markdown emails.test-markdownاینم از متد build کلاس MarkDownMail:تصویری از فایل test-markdown که توش view ما قرار گرفته:اینجا ما یه سری component داریم؛ قبلا در مورد نحوه call یا فراخوانی componentهای خونده بودیم؛ اینطوری می‌ اومدیم و از پوشهٔ resources/views/components اطلاعات رو می‌خوندیم؛ اما توی فرمت بالا از :: استفاده شده که معنی و مفهوم دیگه‌ای داره:@component(&#039;components.alert&#039;)خب معنی این :: اینه که میاد و دیتا رو از vendor برای ما واکشی می‌کنه، اما برای تغییر این کامپوننت‌ها ما مجاز به تغییر vendor نیستیم و باید توی resources/views اون رو publish کنیم:php artisan vendor:publish --tag=laravel-mailخب، حالا از دایرکتوری اصلی vendor فایل‌های mail کپی شدن به پروژهٔ ما:ما قبلا برای pagination این کار رو کرده بودیم؛ حالا توی vendor پروژهٔ خودمون دو تا پوشهٔ mail و pagination رو داریم که توی mail هم html و text داریم.وقتی که می‌گیم mail::message میره و دنبال فایل message.blade.php داخل mail می‌گرده:سیستم Notification:نمایش اعلان‌های مختلف، روی کانال‌های مختلف. مثلا از طریق ایمیل، sms یا ذخیره‌سازی در دیتابیس و نمایش در یک view و حتی ارسال با تلگرام (با کتابخونه و API).برای ایجاد notification باید از artisan کمک بگیریم:php artisan make:notification PostPublishedبعد از اجرای این دستور یک پوشه به اسم Notifications داخل پوشهٔ app ایجاد میشه که داخل اون یک فایلی هست به نام PostPublished.php که در واقع همون اسمی هست که برای کلاس notification خودمون انتخاب کرده بودیم.این کلاس، مثل همهٔ کلاس‌ها یک سازنده داره که می‌تونیم بهش متغیر پاس بدیم  و همچنین یک متد داره به اسم via که یک آرایه برمی‌گردونه:وقتی که داخل آرایه mail داریم؛ notification ما از طریق ایمیل ارسال میشه.حالا هر کانالی که دوست داشتیم رو کافیه به این آرایه اضافه و کانفیگ کنیم تا برای ما ارسال رو انجام بده:return [&#039;mail&#039;, &#039;database&#039;, &#039;sms&#039;];متدی داریم به اسم toMail، وقتی که ما داخل via میایم و mail رو به عنوان یکی از کانال‌های ارتباطی انتخاب می‌کنیم؛ باید متد toMail رو هم به این کلاس اضافه کنیم (به صورت دیفالت هست). یا مثلا متد toArray هم داریم که برای ذخیره‌سازی در دیتابیس هست.نحوهٔ ارسال Notification:اگر بخوایم یک پیامی رو از طریق ایمیل ارسال کنیم؛ باید از متد toMail استفاده کنیم؛ این متد یک کلاس از نوع Mail به اسم MailMessage رو return می‌کنه که البته این کلاس در Core لاراول قرار داره و نیازی نیست که ما ایجادش کنیم.توی تصویر زیر، یک instance از کلاس MailMessage ایجاد کردیم و روش متدهای line و action رو فراخوانی کردیم؛ متد line یک خط به ایمیل ما اضافه می‌کنه و متد action یک button.این line و action قابل customize شدن هستند.روش‌های مختلفی برای ارسال notification وجود داره، مثلا یکی از روش‌ها استفاده از Notifiable هست؛ ما معمولا می‌خوایم برای userهای خودمون پیام ارسال کنیم؛ بنابراین، باید توی مدل User بیاییم و Notifiable رو use کنیم:معمولا به صورت دیفالت این Notifiable داخل مدل User مورد استفاده قرار می‌گیره.حالا می‌خوایم داخل کنترلر، به User شماره فلان یا User که لاگین کرده یه ایمیل ارسال کنیم. برای این کار از کدهای زیر در کنترلر استفاده می‌کنیم:$user = auth()-&gt;user();
// don&#039;t forget to use PostPublished
$user-&gt;notify(new PostPublished());متد notify میاد و متد via کلاس PostPublished رو چک می‌کنه، و مثلا وقتی می‌بینه که داخل آرایه نوشته شده mail، حالا متد toMail رو ران می‌کنه و یک ایمیل به کاربری فعلی ارسال می‌کنه.روش دومی که می‌تونیم برای ارسال notification استفاده کنیم؛ استفاده از Notification facade هست:$user = auth()-&gt;user();
Notification::send($user, new PostPublished());

// simply send to all users
$users = User::all();
Notification::send($users, new PostPublished());

// send parameters to PostPublished Notification
$users = User::all();
Notification::send($users, new PostPublished(&#039;something&#039;));ارسال Notification از طریق کانال Mail:۱- ایجاد کلاس Notification با artisan۲- تعریف mail در via۳- دریافت پارامتر در constructor۴- استفاده از Notifiable در مدل مد نظر۵- ارسال پیام با متد notify مربوط به Notifiable یا فساد Notificationبریم برای درک بهتر متد toMail:متد toMail یک آرگومان ورودی داره به اسم notifiable که اگر ازش dd بگیریم، متوجه می‌شیم که این متغیر در واقع همون userای هست که قراره براش ایمیل رو ارسال کنیم.برای اینکه بتونیم notification خودمون رو customize کنیم یعنی view اون رو تغییر بدیم؛ باید فایل vendor رو دستکاری کنیم؛ اما چون نباید خود پوشه vendor رو دست بزنیم؛ بنابراین، باید مثل Mail و Pagination عملیات publish رو انجام بدیم؛ به این صورت:php artisan vendor:publish --tag=laravel-notificationsخب حالا اگر وارد resources/views/vendor/notifications بشیم؛ می‌بینیم که فقط یک فایل email.blade.php هست و می‌تونیم با تغییر اون به فرم دلخواه خودمون برسیم. این فایل در واقع داره از componentهای mail استفاده می‌کنه و با تغییر در اون‌ها می‌تونیم view رو دستکاری کنیم.ما در متد toMailبه راحتی می‌تونیم از view هم به جای template دیفالت استفاده کنیم:ارسال ایمیل notification به صورت markdown:php artisan make:notification MarkdownNotification --markdown emails.markdown-notificationبا این دستور یک کلاس جدید به پوشهٔ Notifications داخل app اضافه شده به اسم MarkdownNotification.php که مثل همون PostPublished هست؛ اما تنها تفاوتش در متد toMail هست که به جای line و action یا view از متد markdown استفاده کرده:اینم از template مربوط به markdown-notification:داخل HomeController خودمون می‌تونیم با دستور زیر یک Notification ارسال کنیم:$user = auth()-&gt;user();
Notification::send($user, new MarkdownNotification());فارسی‌سازی ایمیل Verification و Reset Password:خب، اول از همه باید ببینیم که فایل‌های مربوط به این‌ها کجا هستن؟ اگر مسیر زیر رو دنبال کنیم:vendor/laravel/framework/src/Illuminate/Auth/Notifications/می‌تونیم توش دو تا فایل زیر رو ببینیم:ResetPassword.php
VerifyEmail.phpتوی متد toMail ما این رو داریم:حالا کافیه برای هر یک از این جملات داخل فایل fa.json خودمون یک معادل قرار بدیم.برای تغییر استایل هم می‌تونیم فایل زیر رو تغییر بدیم:resources/views/vendor/mail/html/themes/default.cssآشنایی با کانال Database در بحث Notification:برای این کار باید یک جدول در دیتابیس ایجاد کنیم؛ در واقع دستور زیر میاد و مایگریشن این جدول رو برای ما ایجاد می‌کنه:php artisan notifications:tableتصویری از خود Schema جدول:نکته: uuid الگوریتمی برای ایجاد یک id یکتا هست.نکته: برای اینکه timestamp ما دقیق باشه، باید توی config/app.php مقدار timezone رو به Asia/Tehran تغییر بدیم.خب حالا به راحتی migrate می‌زنیم که توی دیتابیس بشینه:php artisan migrateتوی این جدول type رو داریم که نوع notification رو مشخص می‌کنه و همین‌طور داخلش یک رابطهٔ پلی‌مورفیک داریم که برای ما ستون‌های notifiable_id و notifiable_type رو ایجاد می‌کنه، ستون data داریم برای ذخیره‌سازی دیتا و پیام هایی که برای کاربر ارسال می‌کنیم و ستون read_at که از نوع timestamp هست و زمان خوانده شدن پیام توسط کاربر توش ذخیره میشه و در نهایت timestamp که updated_at و created_at رو اضافه می‌کنه.تصویر زیر نمایه‌ای جدول ایجاد شده در دیتابیس رو نشون میده:برای ذخیره notification توی دیتابیس، باید تغییراتی در متد via کلاس notification خودمون بدیم، برای ایک کار علاوه بر email باید database رو هم به لیستی که return میشه اضافه کنیم و متد toArray رو اینطوری بنویسیم:توی متد toArray اون چیزی که return میشه در واقع پیامی هست که میره و توی ستون data میشینه. ما داریم یک آرایه رو return می‌کنیم که توی دیتابیس به شکل json ذخیره میشه.خب حالا توی HomeController درخواست ارسال notif رو می‌دیم:Notification::send($user, new PostNotification($post));حالا اگر وارد دیتابیس بشیم؛ می‌بینیم که پیام ما اینطوری ذخیره شده:نحوه دسترسی به پیام‌های ذخیره شده در کانال Database:پروپرتی که می‌تونیم روی مدل User ران کنیم:$user = User::find(2);
$user-&gt;notifications;که برای ما اینو برمی‌گردونه:یه متد داریم برای اینکه فیلد read_at رو ست کنیم؛ به این معنی که کاربر پیام رو خونده:$notification = $user-&gt;notifications-&gt;first();
$notification-&gt;markAsRead();یک پروپرتی داریم که notifهای خوانده نشده (اون هایی که فیلد reat_at فاقد مقداره) رو برمی‌گردونه:$user-&gt;unreadNotifications;یک متد داریم که باهاش می‌تونیم یک notif رو پاک کنیم:$notification-&gt;delete();یا مثلا آپدیت یک notif:$notification-&gt;update([]);دریافت data که به صورت json توی db ذخیره شده در قالب Array:$notification-&gt;data;آشنایی با کانال SMS:لاراول در اصل از یک کتابخونه به اسم Nexmo برای ارسال sms استفاده می‌کنه، اما توی ایران ظاهرا تحریم/فیلتر هستن و به درد ما نمی‌خوره، بنابراین باید از API سرویس های داخلی استفاده کنیم.برای این کار ما میاییم و یک پوشه به اسم Channels داخل پوشهٔ app ایجاد می‌کنیم و داخل این پوشهٔ Channels یک فایل به اسم SmsChannel.php با محتویات زیر:متد send داره ۲ تا ورودی می‌گیره، یکی $notifiable که در واقع همون شی ساخته شده از مدل ما میشه (مثلا User) و $notification که یک شی از کلاس Notification هست.برای دریافت متن sms به وسیله متد send، یک متد داخل کلاس Notification ایجاد می‌کنیم که یک متنی رو return می‌کنه، حالا اینجا ما یه چیز ثابت گذاشتیم؛ اما می‌تونیم داینامیکش کنیم و از داخل HomeController یک مقداری رو پاس بدیم و داخل سازنده این کلاس دریافت و به جای Test Sms استفاده کنیم.و به آرایه برگشتی via مقدار SmsChannel::class رو اضافه می‌کنیم.حالا اگر یک پیامی رو notify کنیم؛ کانال‌های داخل آرایه via فراخوانی میشن، که اینجا متد send کانال SmsChannel فراخوانی میشه و بهش مدل و notification پاس داده میشه و ما می‌تونیم روی شی notification مثلا تابع toSms رو ران کنیم و متن دلخواه رو دریافت کنیم.(تا جایی که فهمیدم تقریبا اینطوری بود؛ اما دقیقا از مکانیسمش مطمئن نیستم باید با dd و... از ترتیب فراخوانی‌ها سر در بیارم):خب حالا از طریق مدل user می‌تونیم به شماره تلفن دسترسی داشته باشیم و آرگومان دوم هم متن پیام.مبحث Event:رویداد یا event، به حالتی گفته میشه که ما می‌خوایم منتظر باشیم؛ وقتی یک اتفاقی افتاد، یک کار خاصی رخ بده.مثلا اگر فلان نویسنده یک پست منتشر کرد؛ برای مدیر سایت پیام ارسال بشه.اصطلاحات مهم در این مبحث:Event
Raise/Dispatch/Fire
Listenerدر واقع listener گوش میده که اگر فلان اتفاق (event) افتاد؛  فلان عملکرد Dispatch بشه. مثلا اگر کاربران سفارش ثبت کردند؛ براشون sms یا ایمیل ارسال بشه.هسته لاراول از Eventها زیاد استفاده می‌کنه، مثلا وقتی ما داریم با Eloquent یک دیتایی رو ایجاد می‌کنیم؛ لاراول داره گوش میده و با توجه به رخدادهایی که داریم؛ یک کار خاصی انجام میده.ممکنه یک Event چندین listener داشته باشه.نحوهٔ ایجاد Event و Listener:سناریو: با ایجاد شدن یک پست یک ایمیل ارسال کن.php artisan make:event CreatePost
php artisan make:listener CreatePostEmail
// you can add more listeners for a signle eventتوی پوشهٔ app دو تا پوشهٔ جدید Events و Listeners اضافه شده، هر کدوم از این پوشه‌ها شامل فایل‌های php خودشون هستن که به ترتیب شامل CreatePost.php و CreatePostEmail.php هست.نکته: Broadcast قابلیتی هست در لاراول که وقتی یک اتفاقی در سمت سرور رخ میده، اون رو به سمت client هدایت می‌کنه و برای این کار از یک کتابخونه به اسم pusher استفاده می‌کنه که یک کتابخونه برای ایجاد real time هست. مثلا توی سیستم چت، پیام میاد سمت سرور و سرور سریع باید اون پیام رو به سمت کلاینت بفرسته این بحث broadcast و سوکت میشه.نکته: SerializesModel نوعی استاندارد فرمت‌بندی داده مثل json هست.مهم‌ترین متد در کلاس CreatePostEmail؛ متد handle هست؛ listener میاد و گوش میده که هر وقت event مربوط به CreatePost اجرا شد، متد handle رو برای ما صدا می‌زنه.وصل کردن event و listener:برای این کار باید وارد پوشهٔ Providers بشیم و EventServiceProvider.php رو باز کنیم؛ و event و listener مربوط رو داخل آرایهٔ $listen اضافه کنیم:نکته: use کردن کلاس‌ها فراموش نشه!حالا اگر چند تا listener داشتیم؛ خیلی ساده می‌تونیم اون ها رو در کنار CreatePostEmail::class اضافه کنیم.بریم برای dispatch یا fire کردن event:برای این کار ما یک helper function داریم به اسم event() که به راحتی عمل dispatch رو اجرا می‌کنه:event(new CreatePost());وقتی این event اتفاق افتاد؛ Listener که فال گوش وایستاده بود؛ متوجه میشه و متد handle خودش رو صدا می‌زنه.فرستادن پارامتر به event:خیلی ساده می‌تونیم از HomeController دیتا رو پاس بدیم به constructor کلاس CreatePost یا همون Event خودمون و برای اینکه بتونیم به پارامترهای ارسالی داخل متد handle مربوط به کلاس CreatePostEmail دسترسی داشته باشیم کافیه از $event که به handle پاس داده میشه استفاده کنیم.نکتهٔ دیگه اینکه اگر مقادیر دریافتی در سازنده CreatePost رو در متغیرهای private ذخیره کنیم؛ برای دستیابی به اون‌ها داخل متد handle کلاس CreatePostEnail باید از getter استفاده کنیم.</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Wed, 10 Aug 2022 15:51:57 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت هفتم</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%D9%87%D9%81%D8%AA%D9%85-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-m3lknzxqcph2</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.قسمت ۹۴ تا ۱۰۶مبحث Localization: برای مطالعهٔ مستندات این بخش باید بریم سراغ Frontend و بخش Localization.با استفاده از این قابلیت می تونیم سایت خودمون رو چند زبانه کنیم.خب ما داخل view های خودمون ممکنه که یک سری متن static داریم؛ حالا اگر سایت ما چند زبانه باشه، با تغییر زبان باید این متن‌ها تغییر کنن، پس چاره چیه؟ اگر خاطرمون باشه توی پوشهٔ resources/lang/en یه سری فایل داشتیم که توش آرایه‌های associative برگشت داده میشد و ما قبلا توی بحث validation اون رو به فارسی ترجمه کرده بودیم؛ حالا برای هندل کردن این قضیه به راحتی می تونیم یک فایل حالا به هر اسمی که دوست داشتیم؛ مثلا static_texts.php ایجاد کنیم و مثل سایر فایل‌های داخل en بیاییم و یک آرایهٔ associative رو return کنیم:// static_texts.php file
return [
    &#039;welcome&#039; =&gt; &#039;This is a welcome text!&#039;
]حالا برای دسترسی به این متن در داخل view کافیه از یک helper function به اسم __ استفاده کنیم:// in blade
{{ __(&#039;static_texts.welcome&#039;) }}حالا برای متن‌های فارسی کافیه یک پشه به اسم مثلا fa ایجاد کنیم و دقیقا همون فایل static_texts.php رو کپی می‌کنیم ولی value ها رو فارسی‌سازی می‌کنیم. اما نیاز داریم که یک config داشته باشیم تا از پوشهٔ fa این فایل رو بخونه، برای این کار وارد پوشهٔ config و فایل app.php میشیم و مقدار key مربوط به locale رو به fa تغییر می دیم. حالا اگر توی fa یک کلیدی نباشه، میره از پشتیبان که fallback_locale باشه می‌خونه و اگر اونجا هم نباشه، دقیقا آرگومان خود __ رو به ورودی می‌فرسته.روش دیگه ای برای localization:توی این روش دیگه به این شکل filename.key متن رو نمی‌خونیم، و فقط از یک رشته استفاده می‌کنیم، یعنی فقط key، حالا این key از کجا خونده میشه؟ داخل پوشهٔ resources/lang باید به نام زبان‌هایی که داریم فایل json ایجاد کنیم؛ مثلا en.json یا fa.json و توی این روش، برنامه میاد و از config مقدار locale رو چک می‌کنه و طبق اون متن رو بر می‌گردونه:// how to load text using __ in view
{{ __(&#039;your key in json&#039;) }}محتویات فایل json:// en.json file
{
    &amp;quotkey&amp;quot: &amp;quotvalue&amp;quot
}

// fa.json file
{
    &amp;quotkey&amp;quot: &amp;quotمقدار&amp;quot
}معرفی یک متد کاربردی برای تغییر locale:App::setLocale(&#039;en&#039;);یک متدی داریم برای چک کردن نوع locale:App::isLocale(&#039;en&#039;);ارسال پارامتر به __ و استفاده در فایل‌های lang:// send argument in view to lang files
{{ __(&#039;static_texts.welcome&#039;, [&#039;name&#039;, =&gt; &#039;Aref&#039;]) }}نحوه دریافت آرگومان در فایل‌های زبان:// if you write :NAME, the result will be uppercase
return [
    &#039;welcome&#039; =&gt; &#039;your argument will placed here :name&#039;
];گاهی پیش میاد که ما قراره یه سری ریکورد از دیتابیس بخونیم و مثلا یه پیام نشون بدیم که ۱۰ تا کاربر پیدا شد، یا مثلا کاربری پیدا نشد یا ۱ کاربر پیدا شد. مشکل بیشتر مربوط میشه به زبان انگلیسی:1 user has been found.
no user has been found.
10 users have been found.همون طور که می بینیم ساختار جملات با توجه به تعداد عوض شده، در اینجا اگر قرار بود از core php استفاده کنیم؛ مجبور بودیم یه ساختار شرطی برای هندل کردن این قضیه ایجاد کنیم. اما لاراول تابعی داره به اسم trans_choice که به ما کمک می‌کنه با توجه به تعداد، متن خاصی رو از فایل‌های lang برگشت بدیم:// how to use trans_choice in view
{{ trans_choice(&#039;static_texts.users&#039;, 2) }}

// lang file configuration
return [
    &#039;users&#039; =&gt; &#039;{1}...|[2,9]...|[10,*]...&#039;
]مبحث Pagination:صفحه بندی :)خب، برای درک بهتر این قضیه، توی جدول posts در دیتابیس حدود ۵۰ تا ریکورد اضافه کردیم و می‌خوایم اون ها رو توی view به نمایش بذاریم؛ توی کنترلر میاییم کل ریکوردها رو می‌خوانیم و با compact و view helper function اون ها رو توی خرورجی نمایش می‌دیم:محتویات فایل view:توی فایل view یک foreach directive قرار دادیم که میاد تک تک پست‌های ما رو داخل یک card قرار میده.توی تصویر زیر هم نمایی از جدول posts رو داریم:توی تصویر زیر هم نمونه‌ای از خروجی کار رو می‌بینیم:اما مشکل اینه که وقتی ریکوردها زیاده، کلی باید اسکرول کنیم و لود شدن اون‌ها هم خودش معضلی هست؛ برای این کار میاییم و از pagination استفاده می‌کنیم.در قدم اول توی کنترلر به جای فراخوانی متد all روی فساد Post میاییم و از متد paginate استفاده می‌کنیم.توی تصویر زیر، حالا اومده به روش‌های مختلف دیتا رو از توی دیتابیس خونده و در نهایت روی اون‌ها متد paginate رو صدا زده:نمونه‌ای از خروجی کار که البته dd شده اما در ادامه ui رو هم ردیف می‌کنیم:نمایش صفحه‌بندی در view:برای ایجاد دکمه های pagination کافیه که توی فایل blade بنویسیم:// how to show pagination buttons
{{ $posts-&gt;links() }}
// how many page number around the current page
{{ $posts-&gt;onEachSide(3)-&gt;links(&#039;vendor&#039;) }}خروجی کار:علاوه بر paginate یک متد دیگه‌ای داریم به اسم simplePaginate که دیگه اعداد رو نشون نمیده و فقط توی view یک next و previous خواهیم داشت.کاستومایز (سفارشی) کردن دکمه‌های pagination:خب ما توی پروژه داریم از بوت استرپ استفاده می‌کنیم و فایل‌های مربوط به paginate بوت استرپ هم داخل مسیر زیر قرار داره:vendor/laravel/framework/src/Illuminate/Pagination/resources/viewsاما ما حق دست بردن در فایل‌های داخل پوشهٔ vendor رو نداریم؛ پس چاره چیه؟باید از artisan کمک بگیریم تا بیاد و برای ما یک نسخه از فایل paginate بوت استرپ داخل vendor رو داخل پوشهٔ ریسورس‌ها puslish کنه:php artisan vendor:publish --tag=laravel-paginationخروجی دستور بالا:حالا اگر وارد resources/views/vendor/pagination بشیم؛ می‌تونیم فایل‌های blade مربوط به pagination رو ببینیم و تغییر بدیم:خب حالا چطور فایل pagination خودمون رو تغییر بدیم؟ مثلا فایل default.blade.php رو ران کنیم؟ کافیه که وقت فراخونی لینک‌ها از دستور زیر استفاده کنیم:// load another pagination file
{{ $posts-&gt;onEachSide(3)-&gt;links(&#039;vendor.pagination.default&#039;) }}خروجی کد بالا، view زیر هست که هیچ استایل و کلاسی نداره:البته ما می‌تونیم به راحتی یک فایل دلخواه برای pagination ایجاد کنیم و مثلا بذاریمش توی پوشهٔ views و بهش دسترسی داشته باشیم؛ اما برای اینکه کار خودمون رو راحت کنیم و نیازی نباشه که هر سری بگیم ما می‌خوایم از فلان pagination استفاده کنیم؛ کافیه از AppServiceProvider کمک بگیریم و به شکل زیر عمل کنیم:خود Paginator هم باید داخل AppServiceProvider استفاده - use - بشه:use Illuminate\Pagination\Paginator البته Paginator  متدهایی دیگه‌ای هم داره مثل:Paginator::defaultSimpleView(&#039;simplePagination&#039;);وقتی که ما روی مدل Post متد paginate رو ران می‌کنیم؛ در واقع یک شی از نوع paginator ایجاد کردیم که یه سری متد داره مثل hasPages یا onFirstPage که اگر داخل فایل blade رو نگاه کنیم؛ می‌بینیم که بر  مبنای همین شی و directiveهای blade اومده pagination رو هندل کرده.قسمت مهم بعدی، بحث appending هست؛ یعنی چی؟ فرض کنیم که url ما علاوه بر page=num یه سری پارامتر get دیگه هم داره، حالا اگر ما بین صفحات جابه‌جا بشیم، اون پارامترهای دیگه نادیده گرفته میشن، تکلیف چیه؟ اینجاست که بحث appending میاد وسط:localhost:8000/home?search=keyword&amp;page=10
// after navigate to another page
localhost:8000/home?page=11برای حل این مشکل:// solve problem
{{ $posts-&gt;appends([&#039;search&#039; =&gt; request(&#039;search&#039;)])-&gt;links() }}اما اگر query strings ما زیاد بشه چه باید کرد؟ اینجا از متدی به اسم withQueryString استفاده میشه:// with query string
{{ $posts-&gt;withQueryString()-&gt;links() }}دیگه نیازی نیست که ما دستی بهش query strings رو بدیم و خودش اتومات میاد و هندلش می‌کنه.مبحث fragment:فرض کنیم که یک مقالهٔ بلند بالایی رو توی یک سایتی خوندیم و رسیدیم به بخش نظرات، حالا می‌خوایم بریم صفحه دوم نظرت، در حالت عادی با رفتن به صفحه بعدی، دوباره صفحه از اول میاد و باید اسکرول کنیم تا بخش نظرات و ادامه ماجرا.توی html یاد گرفتیم که میشه برای لینک دادن به یک بخش خاصی از صفحه از #id استفاده کرد؛ توی لاراول هم fragment همین کار رو می‌کنه، کافیه که id اون بخشی که قرار بریم توش رو بزنیم:{{ $posts-&gt;fragment(&#039;comments&#039;)-&gt;links() }}مبحث مدیریت خطاها یا Error Handling:ذخیره‌سازی لاگ در لاراول، خود لاراول از کتابخونه‌ای به اسم monolog برای لاگ کردن استفاده می‌کنه.مثلا در HomeController می‌خوایم یک لاگ ایجاد کنیم:Log::info(&#039;message&#039;);خب فساد Log متدهای زیادی داره مثلا info که یک پیام رو ذخیره می‌کنه و به نوعی یک سیستم طبقه‌بندی هست؛ اگر یک Error داشته باشیم؛ یعنی چیزی که مشکل‌زا باشه از error استفاده می‌کنیم.تنظیمات مربوط به لاگ در پوشهٔ config و فایل logging.php ذخیره شده که دیفالت ما اینه:&#039;default&#039; =&gt; env(&#039;LOG_CHANNEL&#039;, &#039;stack&#039;),حالا اگر وارد فایل env بشیم؛ می‌بینیم که مقدار LOG_CHANNEL برابر هست با stack، اما مفهوم این stack چیه؟خب در واقع stack یک نوع log channel هست که این موارد توی کلید channels ذخیره شدن:اگر نگاهی به تابع storage_path بندازیم؛ می‌بینیم که مسیر ذخیره‌سازی فایل‌های لاگ ما اینه:logs/laravel.log
// but the final path will be
storage/logs/laravel.logما می‌تونیم channels مربوط به stack رو به جای single به daily تغییر بدیم؛ با این کار ذخیره‌سازی در همان مسیر انجام میشه با این تفاوت که لاگ هر روز متفاوته، اما اگر از نوع سینگل باشه، کل لاگ‌ها درون یک فایل ذخیره خواهد شد:نکته قابل توجه در مورد stack اینه که وقتی ما داریم channels  رو ست می‌کنیم؛ چون کانال‌ها رو به صورت آرایه دریافت می‌کنه، می‌تونیم بهش هم daily بدیم و هم single و یا حتی سرویس‌هایی مثل slack.توی داک خود لاراول اومده و انواع درایورها رو توضیح داده:ارسال پارامتر به لاگ:Log::error(&#039;An error occurred during creating new user&#039;, [&#039;id&#039; =&gt; 3]);مبحث Ignition Page (صفحهٔ ارور لاراول):فرض کنید داخل یکی از کنترلرهای خودمون اومدیم و یک متغیر رو dd کردیم بدون اینکه از قبل تعریف و مقداردهی شده باشه، خب طبیعتا با مشکل مواجه می‌شیم و وارد صفحه‌ای میشیم که به ما ارور نشون میده، این صفحه در واقع Ignition Page لاراول هست؛ یا صفحهٔ «آتش‌سوزی!»، این Ignition page در واقع یک ابزار خارجی هست برای مدیریت Exception ها در لاراول که می‌تونیم از سایتش دریافت و در پروژه‌های PHP ازش استفاده کنیم:flareapp.io/ignitionاین Ignition page از ورژن ۶ به بعد اضافه شده و در ورژن‌های قبلی متفاوت بوده. البته نسخه Pro هم داره که یه سری ویژگی‌هاش پولی هست و باید خریداری بشه.خود لاراول هم اگر مشکلی باشه، مثل Error و Warning و اینا، میاد داخل log فایل‌ها قرار میده و می‌تونیم چک کنیم. البته باید توجه داشته باشیم که ترتیب قرارگیری لاگ‌ها برعکسه چیزی هست که مثلا توی پایتون می‌بینیم؛ یعنی اولین خط از سری ارورهای جدید مربوط میشه به آخرین کد اجرا شده.اما وقتی پروژهٔ ما قراره روی سرور پروداکشن deploy بشه، باید جلوی نمایش صفحهٔ Ignition رو بگیریم. چرا؟ چون کلی اطلاعات از پروژه به بازدیدکنندگان و طبعا هکرها میده که می‌تونن از اون سو استفاه کنند؛ چار چیه؟ باید غیر فعالش کنیم؟برای این کار باید وارد فایل env بشیم و مقدار APP_DEBUG رو برابر با false قرار بدیم. حالا دیگه لاراول یه سری ارور نشون میده که خیلی کلی هست؛ مثلا ارور ۵۰۰ که برابر با server error هست و ما باید داخل log ها دنبال ارور اتفاق افتاده باشیم.مبحث Exception:لاراول داخل مسیر app/Exceptions/Handler.php میاد و exception ها رو هندل می‌کنه؛ این فایل دو تا متد داره به اسم report و render که report میاد و خطا رو گزارش می‌کنه و در حالت دیفالت لاگ می‌ندازه و رندر میاد و کارهایی که باید بعد از exception اتفاق افتاده رو هندل می‌کنه، مثلا صفحهٔ ۵۰۰ رو نشون میده یا ۴۰۴ و... .توی فایل Handler.php یه سری آرایه از نوع protected داریم مثل dontFlash و dontReport که توی dontReport ما exceptionهایی رو قرار می‌دیم که نمی‌خوایم متد report برای ما گزارش کنه، مثلا لاگ بندازه و توی dontFlash مربوط به old میشه که اگر خطایی اتفاق افتاد، دوباره فرم رو با همون مقادیر قدیمی پر کنه، حالا به صورت دیفالت password و password_confirmation از این ماجرا استثنا شدن، چرا که داخل این آرایهٔ dontFlash قرار گرفتن، حالا ما می‌تونیم چیزهایی دیگه ای که دلمون میخواد رو اینجا بذاریم مثلا یک input با name برابر با family، اینطوری دیگه بعد از اتفاق افتادن خطا و ریفرش صفحه دیگه اون فیلد پر نمیشه.ایجاد Exception سفارشی:php artisan make:exception MyExceptionاین فایل در پوشهٔ Exception قرار می‌گیره:روش اجرای exception سفارشی ما:که البته باید کلاس MyException هم use بشه:use App\Exceptions\MyException;و خروجی Exception که اتفاق افتاده در Ignition:خب ما می‌تونیم با تغییر توابع report و render نحوه نمایش ارورها رو تغییر بدیم. چطوری؟ خب وقتی یک exception اتفاق می افته، اول وارد تابع report میشه پس ما می‌تونیم مثلا این حرکت رو بریم:خب اینجا ما اومدیم و گفتیم که اگر خطای رخ داده از نوع MyException بود؛ جملهٔ Error ... رو توی لاگ بنویس و دیگه ادامه پیدا نکن، وقتی ما خودمون دستی چیزی رو توی لاگ قرار می‌دیم و دیگه خطا رو به report واگذار نمی کنیم؛ دیگه trace رو به ما نشون نمیده.ما حتی می‌تونیم جلوی نمایش ارورهای Exception خودمون رو بگیریم:البته برای چک شدن dontReport حتما باید parent::report اجرا بشه.دقیقا همین کارهایی رو که در بالا انجام دادیم می‌تونیم توی متد render هم انجام بدیم:اما اگر تعداد exceptionهایی که قراره چک کنیم زیاد بشه، کار مشکل میشه، به خاطر همین میایم و از کلاس Exception خودمون استفاده می‌کنیم و از اونجا متدهای report و render رو صدا می‌زنیم.در واقع با این کار ما اومدیم و از داخل HomeController این Exception رو فراخوانی کردیم؛ اگر داخل کلاسی که تعریف کردیم؛ متد report یا render رو تعریف نکنیم؛ این Exception میاد و کلاس‌ها رو از parent خودش می‌خونه، اما در بالا ما اومدیم و متد report رو override کردیم. با این کار دیگه کنترل Exception اومده به دست چیزی که ما سفارشی کردیم.مبحث Try...Catch:try {
    //code...;
} catch (Throwable $th) {
    //throw $th;
}توی کد بالا، Throwable ما در واقع MyException هست و توی قسمت catch  ما اومدیم و اون error رو هندل کردیم؛ شی ex رو اگر dd بگیریم یه سری property داره، با استفاده از متد getMessage می‌تونیم به پیام error دسترسی داشته باشیم.از اونجایی که ما داریم از بلاک try...catch استفاده می‌کنیم؛ دیگه متد report مربوط به Exception را نمیشه و اگر نیاز باشه که report هم اجرا بشه، ما باید از report helper function استفاده کنیم.مبحث HTTP Exception:این ها در واقع همون HTTP Status Codes هستن:موزیلاویکیپدیا&amp;lt;br/&amp;gt;تصویری از مهم‌ترین status codeهای مربوط به HTTP:توی لاراول یک helper function داریم به اسم abort (یعنی صرف نظر کردن، متوقف کردن) که میاد و طبق عدد int ای که بهش می دیم برای ما یک view مربوط به status codes نشون میده:نتیجه:ما برای اینکه view خودمون برای status codeها داشته باشیم؛ می‌تونیم یک پوشه به اسم errors داخل views بسازیم و یک فایل blade هم نام با status code هایی که می‌خوایم تغییر بدیم قرار می‌دیم:404.blade.php</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Mon, 08 Aug 2022 02:27:19 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت ششم</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%B4%D8%B4%D9%85-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-dtr6gvoye8he</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.قسمت ۸۱ تا ۹۳کنترلر Login:این کنترلر شبیه به register هست و دو تا روت get و post داره و مثل register متدهای showLoginForm و login اون در فایل AuthenticatesUsers در vendor قرار داره.متد guard چیه؟...وقتی که لاگین با موفقیت انجام میشه ما ریدایرکت می‌شیم به صفحهٔ home، اما این پروسه چطور رخ میده؟ توی کنترلر LoginController یک property داریم به اسم redirectTo:protected #redirectTo = RouteServiceProvider::Home;پس RouteServiceProvider یک Provider هست و محل قرارگیریش در app/Providers که اونجا مقادیر متفاوتی ست شده از جمله، HOME، که با تغییر این public const می‌تونیم مسیر redirect رو عوض کنیم.توی تصویر زیر یک نمایی از جدول users در دیتابیس رو داریم که یک فیلدی داره به اسم remember_token و این فیلد در واقع اشاره داره به Remember Me checkbox که اگر تیک بخوره یک توکنی ست میشه و لاراول در دفعات بعدی session رو چک می‌کنه و اگر اوکی باشه، اتوماتیک لاگین میشه:این session تا یه زمان مشخصی فعاله و بعدش منقضی میشه، با منقضی شدن session دیگه اون remember_token کارایی نداره و نمیشه لاگین کرد مگر اینکه دوباره دستی به سیستم لاگین کنیم و ادامهٔ ماجرا.برای تنظیم مدت زمان انقضای session باید وارد پوشهٔ config و فایل session.php بشیم؛ و مقدار lifetime رو تغییر بدیم و یا اینکه اگر متغیر SESSION_LIFETIME رو توی فایل env اگر وجود داره تغییر بدیم و اگر نیست تعریف کنیم و بهش مقدار بدیم.آشنایی با Forget Password:مثل رجیستر و لاگین، password reset هم post و get داره. از اونجایی که وقتی ما توی لوکال‌هاست هستیم و دسترسی به سرویس ارسال ایمیل نداریم؛ برخلاف سرور و هاست که توش میشه این سرویس رو کانفیگ کرد و بهش دسترسی داشت؛ باید از سرویس هایی که توی development مورد استفاده قرار می‌گیرن مثل mailtrap.io استفاده کرد.بعد از رجیستر و لاگین توی این سیستم؛ توی بخش Demo index اطلاعات مربوط به سرویس SMTP ما اومده که می‌تونیم اون رو توی app لاراولی خودمون ست کنیم و ازش استفاده کنیم.توی فایل env می‌تونیم اطلاعات ایمیل رو در این بخش وارد کنیم:نمونه‌ی پر شده:خب حالا کافیهٔ که توی روت get مربوط به ریست پسورد؛ ایمیل خودمون رو وارد کنیم تا لینک ریست پسورد برای ما ارسال بشه، وقتی که ما درخواست ریست پسورد می‌دیم؛ در واقع توی جدول reset_passwords در دیتابیس یک ریکورد جدید ایجاد میشه که حاوی ایمیل، توکن پسورد ریست و... هست که عکسش رو در زیر گذاشتم؛ حالا کافیه که پسورد جدید رو بدیم و تمام؛ آها یک چیز دیگه اینکه توکن ریست و ایمیل هم در URL مربوط به فرم وارد کردن پسورد جدید دیده میشه:پیاده‌سازی Email Verification:این قابلیت اصلا چی هست؟فرض کنید یکی بیاد با کلی ایمیل جعلی یه سری اکانت توی سایت ما بسازه و ما هم مثلا به ازای هر اکانت تازه ساخته شده یه سری تسهیلات بدیم؛ این کار راه رو برای سو استفاده خیلی باز می‌کنه، پس برای اینکه از شدت این ماجرا تا حد زیادی کم کنیم؛ باید کاری کنیم تا زمانی که کاربر ایمیل ثبت نامی که براش اومده رو تایید نکرده اجازهٔ فعالیت یا یه سری فعالیت خاص رو نداشته باشه.برای فعال سازی این سیستم، کافیه که به راحتی توی web.php یعنی فایل web routes بیاییم و به Auth::routes() آرگومان بدیم:Auth::routes([&#039;verify&#039; =&gt; true]);علاوه بر این، باید کلاس MustVerifyEmail رو هم روی مدل یوز Implement کنیم:class User extends Authenticatable implements MustVerifyEmail
{
.....
}و نکتهٔ بعدی اینکه وقتی implement شد، حتما باید use هم شود.خب خب، اگر بخوایم قبل از verify کردن ایمیل، جلوی کاربر برای دسترسی به صفحات سایت رو بگیریم باید یک middleware اضافه کنیم:توی تصویر بالا که یک نمایی از جدول users رو نشون میده، بعد از تایید ایمیل، فیلد email_verified_at یک مقدار گرفته (تاریخ) و طبق همون سیستم ما متوجه میشه که این کاربر ایمیلش تایید شده یا خیر.مبحث Password Confirmation:این سیستم چیه و چطوری کار می‌کنه؟تصور کنید که کاربری در سیستم لاگین کرده و session فعال داره اما قراره یک عملیات خاص رو توی سایت انجام بده که نیازه تا مطمئن بشیم فقط و فقط خودش پشت سیستمه، در این حالت از Password Confirmation استفاده میشه. یعنی برای انجام یک کار خاص در عین حالی که session فعال داریم؛ اما باز از کاربر درخواست پسورد می‌کنیم.روت‌های این سرویس با Auth::routes ایجاد میشن که شامل get و post هست.توی تصویر پایین، به web.php یک روت جدید اضافه کردیم؛ اما براش دو تا middleware تعریف کردیم؛ یکی auth که نشون میده کاربر حتما باید لاگین کرده باشه و دیگری password.confirm که دوباره از کاربر پسورد درخواست می‌کنه.این password confirmation تا سه ساعت فعاله، اما به راحتی می‌تونیم توی config و بخش auth میزان password_timeout رو کم و زیاد کنیم.مبحث Viewهای سیستم Authentication:ویوهای مربوط به این سیستم در مسیر resources/views/auth قرار دارن، برای مثال login.blade.php از app.blade.php که توی پوشهٔ layouts قرار داره extend میشه. توی app.blade.php یک directive داریم که navbar توش قرار داره و این طوری عمل می‌کنه که میاد چک می‌کنه که آیا کاربر مهمان هست یا خیر، و طبق اون یک view خاص رو نمایش میده: دو تا directive مهم در viewهای مربوط به auth، می‌تونیم به موارد زیر اشاره کنیم:@auth
...
@endauth
//
@guest
...
@endguestیه directive دیگه @error که میاد و چک میکنه و اگر ارور داشتیم فلان کلاس رو قرار بده یا فلان پیام رو نشون بده و... که همگی برای هندل کردن کارهای فرانت ما هست که با نگاه کردن به viewها به راحتی میشه از اون‌ها سر در آورد.مرور دوبارهٔ middlewareها:مورد ۱- auth: چک کردن اینکه کاربر لاگین شده یا خیر. ما می‌تونیم این میدلور رو روی یک روت یا حتی در constructor مربوط به کنترلر مد نظر قرار بدیم.مورد ۲- password.confirm: نشون دادن صفحه درخواست مجدد پسورد.مورد ۳- verified: بررسی اینکه کاربر ایمیلش رو تایید کرده یا خیر.مورد ۴- auth.basic: نمونهٔ ساده تر auth هست و به این شکل عمل می‌کنه که یک پاپ آپ نشون میده و از اونجا میخواد که authentication انجام بده، اما خود auth، ما رو به صفحهٔ لاگین پیج ریدایرکت می‌کنه.این middlewareها که در موردشون صحبت کردیم داخل app/Http/Middleware/Kernel.php قرار دارن و براشون alias هم تعریف شده (توی یک آرایهٔ associative):متدهای کاربردی سیستم auth:یکی از متدهای Auth facade (فساد در واقع یک دیزاین پترنه که میاد و از پیچیدگی چندین کلاس کم می‌کنه و به راحتی می‌تونیم از متدهای اون کلاس‌ها استفاده کنیم...) user هست که برای ما اسم userای که لاگین کرده رو بر می‌گردونه:// returns the name of the logged-in user
Auth::user();
// using helper instead of facade
auth()-&gt;user();
auth()-&gt;id();

// check method, returns true or false if a user is logged-in or not
auth()-&gt;check();

//logout method, logs out the user
auth()-&gt;logout();


// login method, this method logs in a specific user
$user = User::find(2);
auth()-&gt;login($user);
// or simply:
auth()-&gt;loginUsingId($num);مبحث کنترل سطح دسترسی با Authorization:کنترل سطح دسترسی به بخش‌های مختلف برنامه با توجه به نقشی که آن یوزر خاص دارد. مثلا admin می‌تونه کاربر رو اضافه/حذف کنه اما یک یوزر عادی نباید یک چنین دسترسی داشته باشه.در مبحث authorization کاربر لاگین کرده و حالا باید سطوح دسترسی اون رو اوکی کنیم.برای درک بهتر این مبحث یک جدول جدید به database اضافه کردیم:توی تصویر زیر داریم می‌بینیم که مدل Post تعریف شده و یک متدی داره به اسم user که رابطه رو برقرار کرده و نوع رابطه یک به چند هست؛ یعنی هر user چندین پست داره، اما هر پست تنها متعلق هست به یک user خاص، جلمه $this داره میگه که این پست متعلق هست به فلان یوزر:توی تصویر زیر مدل User رو می‌بینیم که متدی داره به اسم posts که رابطه رو برقرار می‌کنه، به این صورت که میگه این user، فلان پست‌ها رو داره:تصویری از migration مربوط به post:کل پروسه ساخت مدل، مایگریشن و کنترلر با artisan ساده‌سازی شده و کافیه که دستور زیر رو بزنیم تا همشون رو برامون بسازه:php artisan make:model Post -mcدر واقع آپشن m برای ما model رو می‌سازه و آپشن c برای ما controller رو ایجاد می‌کنه.** نکتهٔ مهم، توی نسخه‌های بعد از ۶ می‌تونیم به جای bigIncreaments برای id از id() استفاده کنیم. همین‌طور foreignId جایگزین unsignedBigInteger شده.مبحث Gate:مجموعه ای gateها میشه policy:وقتی کاربری می‌خواد کاری رو انجام بده ما اون رو از یک دروازه یا gate عبور می دیم تا چک کنیم که آیا دسترسی دارد یا خیر.خب خب، می‌خوایم با اون جدول posts چیکار کنیم؟ می‌خوایم اجازهٔ ویرایش رو تنها به صاحب همون پست بدیم. برای کنترل این عملیات از gate استفاده می‌کنیم.منطق سطح دسترسی یه چیز تو مایه‌های عکس زیره، توی تصویر زیره، اول میاییم چک می‌کنیم کدوم کاربر لاگین کرده، بعدش میایم می‌بینیم قراره به کدوم پست دسترسی داشته باشه و داخل شرط میزان user_id اون پست رو با id یوزر لاگین کرده مقایسه می‌کنیم و اگر برابر بود، باقی عملیات...:اما توی لاراول با استفاده از gate و policy این عملیات خیلی ساده و راحت شده:برای تعریف gate، باید وارد AuthServiceProvider بشیم و توی متد boot به این شکل یک گیت اضافه کنیم:Gate::define(&#039;update-post&#039;, function($user, $post){
    if ($post-&gt;user_id === $post-&gt;id){
        return true;
    }
    else{
        return false;
    }
});که البته اون update-post یک اسم دلخواه هست و می تونیم هر چیز دیگه ای براش بذاریم؛ اما باید توجه کنیم که این نام گذاری قواعد و conventionهایی داره که اگر رعایت بشه بهتره.خب، حالا که قواعد مربوط به این gate رو تعریف کردیم؛ می‌تونیم ازش توی کنترلر خودمون استفاده کنیم؛ به شکل زیر:$user = auth()-&gt;user();
$post = Post::find(1);

$allow = Gate::allows(&#039;update-post&#039;, $post);
if ($allow){
    //...
}در ادامه می‌خوایم سطح دسترسی userای رو چک کنیم که اصلا لاگین (به هر صورت یک یوزر خاص رو می‌خوایم بررسی کنیم) برای این کار از متد forUser مربوط به Gate facade استفاده می‌کنیم:علاوه بر allows متدی داریم به اسم can که روی شی برگشته از user مربوط به auth ران میشه:و همین‌طور authorize که مثل can عمل می‌کنه، اما این متد روی خود کنترلر که می‌تونیم با $this بهش دسترسی داشته باشیم ران میشه، چرا که کنترلرهای ما از یک کلاس اصلی یعنی Controller گسترش پیدا می‌کنن و این کنترلر هم AuthorizesRequests رو use می‌کنه، پس:$this-&gt;authorize(&#039;update-post&#039;, $post);اما authorize اینطور عمل می‌کنه که دیگه true و false بر نمی‌گردونه و اگر اوکی باشه که ماجرا ادامه پیدا می‌کنه اما اگر اوکی نبود؛ صفحه زیر رو برای ما نمایش میده:معمولا همهٔ پروژه ها یک user با سطح دسترسی بالا دارن که هر کاری می‌تونه انجام بده، در واقع همون Admin user، حالا ما باید کاری کنیم که این user از شروط gate کنار گذاشته بشه، برای این کار می‌تونیم مثلا طبق type یوزر در table بیاییم این کار رو بکنیم یا اینکه از نوع خاصی از gate استفاده کنیم که در ادامه بهش می‌پردازیم:یه چیز دیگه، علاوه بر متد allows یک متدی داریم به اسم denies که برعکس allows عمل می‌کنه.مبحث Gate::before و Gate::after:توی مثال قبلی، ما برای رد کردن بررسی دسترسی admin، اومدیم و یه شرط نوشتیم؛ که خب طبیعتا کار هم می‌کنه، اما مشکلی که داره اینه که ما برای gateهای مختلف مجبوریم این شرط رو بذاریم و این کد ما رو کثیف می‌کنه، برای حل این مشکل از متدهای before و after مربوط به Gate facade میشه:خب توی تصویر بالا اما اومدیم یک Gate::before تعریف کردیم، این متد یک Colsure می‌گیره به که سه تا پارامتر ورودی داره، اولی user دومی نوع کاری که قراره انجام بدیم، یعنی ability یا action و در نهایت params یا پارامترهایی که می گیره، حالا قبل از update-post این Gate::before اجرا میشه و دیگه نیازی نیست ما برای هر gate این شرط رو تعریف کنیم.** نکته:حالا ما برای دونستن اینکه این closure مربوط به این before چه ورودی‌هایی داره یا باید doc بخونیم یا اینکه با تابع func_get_args داخل بدنه closure چک کنیم چه ورودی‌هایی می گیره.اما میریم سراغ Gate::after، مهم نیست که حتما این Gate::after بعد از Gate::define قرار بگیره، فقط برای خوانایی می‌تونیم این کار رو بکنیم. ترتیب اجرا شدن gateها اینطوری هست که اول before اجرا میشه، اگر true بود؛ مستقیم میره توی after اما اگر false بود؛ اول میره توی Gate::define و بعدش حتما after ران میشه. با func_get_args اومدیم after رو چک کردیم و دیدیم که چهار تار ورودی می‌گیره:مبحث Policy:به مجموعه‌ای از gateها میگیم policy، یکی از علت‌های مهم استفاده از gateها جلوگیری از بهم ریختگی و آشفتگی کدها توی متد boot در AuthServiceProvider هست. حالا کافیه gateهای مرتبط رو داخل policy ها قرار بدیم؛ مثلا policy پست‌ها، کتگوری‌ها و...برای ایجاد یک policy از artisan کمک می‌گیریم:php artisan make:policy PostPolicy --model=Postحالا توی app یک پوشهٔ جدید به نام Policies ایجاد میشه که policyهای ما اینجا قرار می گیره. با قرار دادن آپشن --model ما میاییم و این policy رو با مدل Post مپ می‌کنیم. حالا اگر وارد PostPolicy.php بشیم می‌بینیم که  این policy یه سری متد داره مثل update یا delete و... که یه جورایی مرتبط با همون عملیات CRUD میشه و به راحتی می‌تونیم از اون‌ها استفاده کنیم؛ برای مثال، توی تصویر زیر ما برای PostPolicy و توی متد update یک شرطی رو بررسی کردیم که true یا false برمی‌گردونه:یا مثال دیگه اینه که به راحتی یه شرط می‌ذاریم که اگر کاربر ما admin بود حق داره فلان کار رو بکنه:برای اینکه بتونیم از این policy استفاده کنیم باید اون رو توی AuthServiceProvider رجیستر کنیم:برای رجیستر کردن Policy اومدیم و توی آرایهٔ associative با نام policies داخل AuthServiceProvider عبارت زیر رو وارد کردیم؛ اما نکته اش اینه که چرا مثل مثال از رشته استفاده نکردیم؟ چون ::class خود به خود اسم اون namespace رو به صورت string برمی‌گردونه و نیازی به نوشتن رشته نداریم.حالا چطور باید از این Policy استفاده کنیم؟ خیلی راحت توی Controller می تونیم ازش استفاده کنیم:$user = auth()-&gt;user();
$post = Post::find(2);
$allow = Gate::allows(&#039;update&#039;, $post);
dd($allow);دیگه نیازی نیست که توی allows دقیقا اسم policy رو مشخص کنیم و خود لاراول این رو هندل می‌کنه که این $post مربوط به فلان policy میشه.خب، اما اگر نگاهی به متدهای policy بندازیم می‌بینیم که همگی متدها به غیر از create آرگومان $post رو دارن، خب حالا اگر بخوایم که یک post ایجاد کنیم؛ چطور به gate حالی کنیم که دنبال فلان Policy هستیم؟ به این شکل:در واقع اومدیم و کلاس Post::class رو براش فرستادیم که میشه همون مدل posts.اما بریم سراغ سایر روش‌های استفاده از Policy:$can = $user-&gt;can(&#039;update&#039;, $post);
$this-&gt;authorize(&#039;update&#039;, $post);
$this-&gt;authorize(&#039;create&#039;, Post::class);مبحث middleware در policy:Route::put(&#039;/posts/{post}&#039;, function(Post $post){
    //....
})-&gt;middleware(&#039;can:update,post &#039;);برای create کردن:Route::post(&#039;/posts&#039;, function()
{     //.... 
})-&gt;middleware(&#039;can:update,App\Post &#039;);ما به راحتی می‌تونیم توی blade از policyهای خودمون استفاده کنیم؛ یک directive داریم به اسم @can که می‌تونیم authorize رو چک کنیم:@can(&#039;update&#039;, $post) // for create: App\Post::class
    //
@else //we have @elsecan() too
    //
@canend</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Sat, 06 Aug 2022 11:37:26 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت پنجم</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%D9%BE%D9%86%D8%AC%D9%85-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-t4nbgaaou7vx</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.قسمت ۷۰ تا ۸۰مبحث Collections:یک wrapper برای آرایه‌های که امکانات زیادی رو به ما میده. با استفاده از این مفهوم می‌تونیم یک مفهوم شی‌گرایی به آرایه‌ها بدیم.نحوهٔ ایجاد یک Collection://Array
$array = [1,2,3]
// Collection
$collection = collect([1,2,3])
//Example of OOP
$collection-&gt;get(index);
$collection-&gt;all();
$collection-&gt;first();
$collection-&gt;count();
$collection-&gt;toJson();
$collection-&gt;toArray();ما با استفاده از helper function ای به نام collect می‌تونیم یک آرایه رو تبدیل به Collection کنیم و از مزایای اون بهره ببریم. متدهایی که روی object ما ران شده، مشابه متدهایی هستن که ما از دیتابیس می‌گرفتیم! علت؟ چون لاراول اون دیتا رو به شکل Collection برای ما برمی‌گردونه.آشنایی با متدهای کاربردی Collection:چک کردن وجود مقداری خاص در کالکشن:$collection-&gt;has(value);چک کردن پر و خالی بودن آرایه:$collection-&gt;isEmpty();دسترسی به مقادیر آرایه‌های Associative:$collection-&gt;get(&#039;key&#039;);حذف کلیدی خاص از کالکشن:$result = $collection-&gt;forget(&#039;key&#039;);آرایه‌ای از آرایه‌ها و اعمال متدهای جالب روی آن‌ها:توی تصویر بالا با اینکه یه آرایهٔ Associative داریم؛ اما به با استفاده از متد avg که میاد و میانگین می گیره، خودش کل آرایه‌های داخلی رو iterate کرد و مقادیر رو میانگین گرفت.تقسیم‌بندی یا chunk کردن آرایه‌ها:$collection-&gt;chunk(2);یعنی اگر روی کالکشن تصویر بالا متد chunk رو اجرا کنیم؛ یک کالکشن با دو تا آرایه تحویل می گیریم که توی هر کدام از این آرایه‌ها دو تا آرایهٔ دیگه هست.و حتی می‌تونیم همون chunk شده رو به آرایه تبدیل کنیم یا متدهای دیگه ای رو روش ران کنیم:$collection-&gt;chunk(2)-&gt;toArray();چک کردن مقدار value یک کلید خاص:$collection-&gt;contains(&#039;key&#039;, &#039;value&#039;);حتی می‌تونیم متد بالا رو توی آرایه‌ای از آرایه‌ها اجرا کنیم.کالشکن متدهایی مثل filter و map که توی پایتون هم داریم رو در اختیار ما قرار میده.فیلتر اینطوری عمل می‌کنه که هر یک از عناصر آرایه رو دریافت می‌کنه، یه شرط خاصی روی تک تک عناصر اعمال میشه و اگر پاس شد؛ فقط اون عنصری که شرط رو پاس کرده، برگشت داده میشه و بقیه فیلتر میشن، مثال:&lt;?php

namespace App\Http\Controllers;

use App\Models\Hacker;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class HomeController extends Controller
{

    public function index()
    {
        $collection = collect([
            [&#039;name&#039; =&gt; &#039;Aref&#039;, &#039;age&#039; =&gt; 20],
            [&#039;name&#039; =&gt; &#039;Jamshid&#039;, &#039;age&#039; =&gt; 21],
        ]);
        dd($collection-&gt;filter(function($value, $key){
            return $value[&#039;name&#039;] == &#039;Aref&#039;;
        }));
    }
}خروجی:Illuminate\Support\Collection {#304 ▼
  #items: array:1 [▼
    0 =&gt; array:2 [▼
      &amp;quotname&amp;quot =&gt; &amp;quotAref&amp;quot
      &amp;quotage&amp;quot =&gt; 20
    ]
  ]
  #escapeWhenCastingToString: false
}اما این $value در واقع متغیری هست که هر یک از عناصر آرایه توش قرار می‌گیرن و $key مربوط میشه به شماره index اون عنصر.حالا بریم سراغ map کردن، map اینطوری عمل می‌کنه که هر مقداری که بهش داده میشه، روش یه پروسس انجام میده و برگشت میده:&lt;?php
$collection = collect([1, 2, 3, 4, 5]);
 
$multiplied = $collection-&gt;map(function ($item, $key) {
    return $item * 2;
});
 
$multiplied-&gt;all();
 
// [2, 4, 6, 8, 10]
?&gt;متد کاربردی بعدی، متد pluck (کندن، چیدن) هست که مقادیر مربوط به یک کلید خاص رو برای ما بر می‌گردونه:&lt;?php

namespace App\Http\Controllers;

use App\Models\Hacker;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class HomeController extends Controller
{

    public function index()
    {
        $collection = collect([
            [&#039;name&#039; =&gt; &#039;Aref&#039;, &#039;age&#039; =&gt; 20],
            [&#039;name&#039; =&gt; &#039;Jamshid&#039;, &#039;age&#039; =&gt; 21],
        ]);
        dd($collection-&gt;pluck(&#039;name&#039;));
    }
}خروجی:Illuminate\Support\Collection {#297 ▼
  #items: array:2 [▼
    0 =&gt; &amp;quotAref&amp;quot
    1 =&gt; &amp;quotJamshid&amp;quot
  ]
  #escapeWhenCastingToString: false
}تابع implode رو هم داریم:&lt;?php
$collection = collect([
    [&#039;account_id&#039; =&gt; 1, &#039;product&#039; =&gt; &#039;Desk&#039;],
    [&#039;account_id&#039; =&gt; 2, &#039;product&#039; =&gt; &#039;Chair&#039;],
]);
 
$collection-&gt;implode(&#039;product&#039;, &#039;, &#039;);
 
// Desk, Chair
?&gt;یه متد کاربردی و جالب دیگه، keyBy هست؛ این متد اینطوری عمل می‌کنه که ما بهش یک key به عنوان آرگومان می‌دیم؛ این میاد مقدار متناظرش رو می‌گیره و به عنوان کلید همون عنصر قرار میده و دیگه به شکل index عددی کلیدگذاری نمیشن:&lt;?php
$collection = collect([
    [&#039;product_id&#039; =&gt; &#039;prod-100&#039;, &#039;name&#039; =&gt; &#039;Desk&#039;],
    [&#039;product_id&#039; =&gt; &#039;prod-200&#039;, &#039;name&#039; =&gt; &#039;Chair&#039;],
]);
 
$keyed = $collection-&gt;keyBy(&#039;product_id&#039;);
 
$keyed-&gt;all();
 
/*
    [
        &#039;prod-100&#039; =&gt; [&#039;product_id&#039; =&gt; &#039;prod-100&#039;, &#039;name&#039; =&gt; &#039;Desk&#039;],
        &#039;prod-200&#039; =&gt; [&#039;product_id&#039; =&gt; &#039;prod-200&#039;, &#039;name&#039; =&gt; &#039;Chair&#039;],
    ]
*/
?&gt;مبحث Sessions:درخواست‌های http ناپایدار یا stateless هستند یعنی وقتی یک سایتی توسط چندین نفر فراخوانی میشه، وب سرور نمی‌تونه در هر بار درخواست/پاسخ تشخیص بده که کدوم کاربر داره باهاش صحبت می‌کنه، برای حل این مشکل می‌تونیم از مفاهیمی مثل session و کوکی استفاده کنیم که اینجا مورد بحث قرار می‌گیرن:در واقع session سیستمی هست برای نگهداری اطلاعات request/response:تنظیمات مربوط به session داخل فایل session.php داخل پوشهٔ config قرار گرفته، یکی از آپشن‌های مهم در تنظیمات session مربوط به ست کردن driver اون هست که می‌تونیم روی file database redis cookie و... قرار بدیم که توی لاراول ۹ به صورت دیفالت روی فایل هست و این فایل‌ها داخل پوشهٔ storage/framework/sessions قرار می‌گیرن.اگر بخوایم ازجدول دیتابیس برای ذخیره‌سازی دیتای مربوط به session استفاده کنیم؛ می‌تونیم به راحتی با استفاده از artisan مایگریشن اون رو ایجاد کنیم:php artisan session:tableخب، در حالت پیش‌فرض ما از file استفاده می‌کنیم چون نسبتا خوب جواب میده، اما حالا که توی فایل config این مورد رو لحاظ کردیم؛ باید بریم سراغ فایل .env و وانجا هم مقدار SESSION_DRIVER رو به file تغییر بدیم.برای ایجاد و ذخیره‌سازی sessions کافیه که از یک سری helper function استفاده کنیم:session()-&gt;put(&#039;key&#039;, &#039;value&#039;);
session()-&gt;get(&#039;key&#039;);
//another way to put
session([&#039;key&#039;,&#039;value&#039;]);آشنایی با متدهای session:با استفاده از متد all می‌تونیم به کل مقادیر ذخیره شده در session دسترسی داشته باشیم.نمونه ای از مقدار برگشتی متد all:بحث flash sessions رو هم داریم که جلوتر در موردشون بحث می‌کنیم...متد has که میاد و یک کلید خاصی رو جستجو می‌کنه و اگر value اون غیر null بود مقدار true رو برمی‌گردونه، اما یک متد مشابه داریم به اسم exists که میاد و دقیقا فقط و فقط کلید رو سرچ می‌کنه و اگر بود (value اون مهم نیست)، مقدار true رو برمی‌گردونه.متد forget میاد و یک کلید خاصی رو حذف می‌کنه.متد flush میاد و کل session رو پاک می‌کنه و تبدیلش می‌کنه به یک آرایه خالی.مبحث flash:اگر بخوایم داده‌ها رو فقط برای درخواست بعدی توی session ذخیره کنیم؛ از flash sessions استفاده می‌کنیم. داده‌های ذخیره شده به این روش فقط و فقط در درخواست http بعدی در دسترس هستند  و بعد از اون بلافاصله حذف می‌شن و برای status messages ایده آل هستن.مثلا یک کاربری یک دیتایی رو توی دیتابیس ذخیره می‌کنه و اگر موفق/ناموفق بود، می‌فرستیم براش و فقط همون یک بار نمایش داده میشه:session()-&gt;flash(&#039;key&#039;, &#039;value&#039;);حالا چطور به این flash data دسترسی داشته باشیم؟ خیلی ساده:$session_data = session()-&gt;all();
dd($session);نمونه خروجی آموزش:اون مقدار name که در old ذخیره شده، در واقع همون کلید ما هست که value داره.اما یک نکتهٔ مهم اینه که ما مثلا وقتی توی یک پیجی میایم و flash data ست می‌کنیم و قراره توی یک پیج دیگه (Route) ازش استفاده کنیم؛ حتما باید return بشه تا اون حالت flash رو داشته باشه و محتواش پاک بشه:اینجا old و همین‌طور کلید name پاک شدن.اگر بخوایم که مقدار flash data رو نگه داریم و در درخواست بعدی هم استفاده کنیم باید از متد reflash استفاده کنیم:session()-&gt;reflash();نمونه خروجی reflash شده که کلید ما داخل new قرار می‌گیره:ما می‌تونیم از متد keep هم استفاده کنیم؛ مثلا اگر چند تا دیتای فلش داشته باشیم؛ با keep می‌تونیم فقط یک سری دیتای خاص رو حفظ کنیم:session()-&gt;flash(&#039;key1&#039;, &#039;value1&#039;);
session()-&gt;flash(&#039;key2&#039;, &#039;value2&#039;);

------------

session()-&gt;keep(&#039;key1&#039;);در این حالت فقط key1 در new قرار خواهد داشت.مبحث Cache:کش کردن باعث میشه تا سرعت اجرای سایت و یا سرویس‌های ما بره بالاتر، چطوری؟ فرض کنیم که یک کوئری داریم که اجرای شدنش n ثانیه زمان میبره، حالا اگر قرار باشه که کاربر هر سری این کوئری رو بزنه و n ثانیه منتظر باشه، کلی وقت‌گیر و اعصاب خردکن میشه. چاره چیه؟ استفاده از همین سیستم کش، که میاد و دیتای واکشی شده از کش رو برای ما یه گوشه نگه می داره و ما دوباره می تونیم ازش استفاده کنیم.تنظیمات کش در لاراول:پوشهٔ config، فایل cache.php که از file redis و... ساپورت می‌کنه، توی کلید default باید درایور رو ست کنیم که به صورت دیفالت روی file هست و میشه از طریق فایل .env هم تغییرش داد. به جای file می‌تونیم از table هم استفاده کنیم و مایگریشن اون رو با artisan ایجاد کنیم:php artisan cache:tableتوی env باید CACHE_DRIVER رو ست کنیم.اگر از فایل درایور استفاده کنیم؛ مقادیر کش شده توی framework/cache/data قرار می‌گیرن.حالا چطور یک فایل رو کش کنیم؟توی کنترلر، از Cache facade و متد put استفاده می‌کنیم:Cache::put(&#039;key&#039;, &#039;value&#039;, &#039;expire_time_secs&#039;);یک مثال:Cache::put(&#039;name&#039;, &#039;Aref&#039;, 15);حالا می‌تونیم توی یک route دیگه به این شکل بهش دسترسی داشته باشیم:$value = Cache::get(&#039;name&#039;, &#039;return_sth_else_if_name_does_not_exist&#039;)&#039;;
dd($value);این مقدار کش شده در مدت زمان مشخص شده در هر جایی از پروژه در دسترس خواهد بود.آشنایی با متدهای مهم و کاربردی سیستم کش:متد remember میاد و چک می‌کنه که آیا یک کلیدی توی کش وجود داره یا خیر، اگر وجود داشت که value اون رو برمی‌گردونه و اگر وجود نداشت با استفاده از دو تا پارامتر بعدی میاد و اون رو ایجاد می‌کنه:دو تا دیگه از متدهای کاربردی Cache متدهای مربوط به حذف میشن که یکی forget هست و دیگری pull، اما یک تفاوتی دارن و اونم اینه که وقتی از forget استفاده میشه، بعد از حذف موفقیت آمیز دیتا، مقدار true رو برمی‌گردونه و pull مقداری value اون key که حذف کرده.اما artisan هم دستوراتی برای کار با کش داره: cache
  cache:clear            Flush the application cache
  cache:forget           Remove an item from the cache
  cache:table            Create a migration for the cache database table
  config:cache           Create a cache file for faster configuration loading
  config:clear           Remove the configuration cache file
  event:cache            Discover and cache the application&#039;s events and listeners
  event:clear            Clear all cached events and listeners
  optimize:clear         Remove the cached bootstrap files
  package:discover       Rebuild the cached package manifest
  route:cache            Create a route cache file for faster route registration
  route:clear            Remove the route cache file
  schedule:clear-cache   Delete the cached mutex files created by scheduler
  view:cache             Compile all of the application&#039;s Blade templatesمبحث مهم Middleware:پلی بین درخواست و پاسخ و کارش فیلتر کردنه، مثلا کاربری یک درخواستی رو برای app ما ارسال می‌کنه، و نیازه که ما این درخواست کاربر رو بررسی کنیم و برعکس، وقتی قراره که سرور پاسخی رو به سمت کاربر بفرسته نیازه که چک کنیم که آیا این پاسخ اوکی هست یا خیر، در اینجا نقش middleware اهمیت پیدا می‌کنه.نمونهٔ خیلی سادهٔ این middleware در واقع سیستم authentication هست؛ مثلا فقط یک یا چند کاربر خاص حق ارسال پست رو توی سایت دارن، ما با middleware میام و چک می‌کنیم که آیا این کاربر لاگین کرده یا خیر...خود لاراول یک سری middleware آماده توی app/Http/Middleware داره و middleware های ایجاد شده توسط ما هم اینجا قرار می‌گیره، توی این پوشه مثلا فایل Authenticate.php رو می‌بینیم که در واقع میدلور authentication ما هست که بالا در موردش نوشتم.یا middleware مربوط به VerifyCsrfToken که فرم‌های پست رو چک می‌کنه که آیا CSRF token ست شده یا خیر.چطور Middleware ایجاد کنیم؟php artisan make:middleware CheckNameParameterمحتویات فایل middleware ما به شکل زیره، متد handle در واقع جایی هست که عملیات فیلتر کردن صورت می‌گیره و به نوعی هسته میان افزار ما محسوب میشه:handle method in the middlewareبرای اینکه middleware ما توی سیستم شناسایی بشه، باید اون رو توی فایل Kernel.php در مسیر app/Http/Kernel.php تعریف کنیم:توی این فایل یه سری آرایه داریم به نام‌های $middleware و middlewareGroup و... که وابسته به کاربردی که دارن، می‌تونیم از اون‌ها استفاده کنیم.اگر یک middleware توی آرایهٔ middleware تعریف بشه، روی تمامی routeهای ما اعمال میشه، اما اگر قرار باشه فقط روی یک route خاص مثل web یا api ران بشه، باید اون رو توی آرایهٔ middlewareGroups تعریف بشه.یک routeMiddleware داریم که در واقع یک آرایهٔ associative هست و می‌تونیم برای یک middleware یک اسم انتخاب کنیم (در واقع کلیدها هستند) و هر جایی که نیاز بود روی route های خودمون تنها با یک نام اعمالشون کنیم.توی آرایهٔ middlewarePriority هم اولویت middlewareها رو تعریف می‌کنیم.برای مثال ما middleware خودمون رو که بالاتر تعریف کرده بودیم؛ توی آرایهٔ routeMiddleware توی سطر آخر اضافه کردیم:به این کار می‌گیم، ثبت یا رجیستر کردن middleware. برای استفاده از middleware در route: خب، وقتی که یک middleware تازه تعریف شده رو روی یک route اعمال می‌کنیم؛ متد handle مقدار زیر رو return می‌کنه، بدون اینکه فیلتر خاصی اعمال کرده باشه و این ما هستیم که باید با توجه به نیاز خودمون تابع یا متد handle رو کامل کنیم:return $next($request)توی تصویر زیر، اومدیم چک کردیم که آیا request کاربر شامل name هست یا خیر و اگر نبود با استفاده از abort helper function اومدیم و صفحهٔ 404 یا not found رو نشون دادیم:یک نکته دیگه اینه که ما می‌تونیم یک مقدار هم به middleware پاس بدی:Route::get(&#039;/&#039;, &#039;HomeController@index&#039;)-&gt;middleware(&#039;name:par1,par2&#039;);برای دسترسی به این مقدار در middleware باید یک متغیر به متد handle اضافه کنیم:public function handle($request, Closure $next, $par1, $par2){
....
return $next($request);
...
}مبحث انواع middleware:۱- نوع before:همون نوعی که توی مطالب پیشین ازش استفاده کردیم؛ یعنی چک کردیم که اگر یک درخواست یا request اوکی بود؛ بریم برای ادامهٔ کار.۲- نوع after:برای زمانی هست که درخواست با request کاربر پردازش شده و قراره یک پاسخی برای کاربر خودمون ارسال کنیم؛ در این حالت باید از after استفاده کنیم. این middleware روی response اعمال میشه. مثلا قبل از فرستادن پاسخ، بیاییم و یه چیزهایی رو به header اضافه کنیم.۳- نوع terminate:مدل پایانی بلافاصله بعد از ارسال پاسخ به مرورگر فراخوانی و اجرا میشه.Terminate MiddlewareBefore MiddlewareAfter Middlewareمبحث مهم Authentication در لاراول:توی داک‌های خود لاراول باید وارد بخش security و authentication بشیم تا به محتوایی که تیم لاراول برای کار با این امکان فراهم کرده دسترسی پیدا کنیم.با دو تا دستور ساده می‌تونیم Authentication رو به سیستم خودمون اضافه کنیم؛ با این کار scaffolding سیستم authentication به app ما اضافه میشه:composer require laravel/ui
// for compiling bootstrap files (like css, js...)
npm run dev
php artisan ui bootstrap --authبعد از اجرای دستور دوم، scaffolding بوت استرپ به همراه سیستم authentication به package.json اضافه میشه و ما تنها باید با دستور زیر، اون نیازمندی‌ها رو به پروژه اضافه کنیم:npm install &amp;&amp; npm run devبا اجرای دستورات بالا، تغییراتی در پروژه اعمال میشه که شامل اضافه شدن ۲ تا route به web.php و همین‌طور اضافه شدن کنترلرهای سیستم auth در مسیر app/Http/Controllers/Auth هست.همین طور پوشهٔ auth به پوشهٔ views در resources اضافه شده.حالا اگر وارد root route بشیم می‌بینیم که به view ما login و register هم اضافه شده.حالا می‌خوایم ببینیم که کد Auth::routes() چه چیزهایی رو به سیستم روتینگ ما اضافه کرده، برای این کار کافیه که از artisan کمک بگیریم:php artisan route:listحالا چیزی شبیه به این خواهیم داشت:اگر وارد مسیر vendor/laravel/ui/src بشیم؛ فایلی داریم به نام AuthRouteMethodes.php که routeهای مربوط به Auth::routes() درش جنریت شده:توی تصویر بالا داریم می‌بینیم که اول مثلا چک می‌کنه که register ست شده هست یا خیر که به صورت دیفالت ست شده و اگر ست شده بود روت‌های اون رو می‌سازه، ما با دادن یک associative array به تابع routes می‌تونیم جنریت شدن اون رو غیر فعال کنیم:Auth::routes([&#039;register&#039; =&gt; false]):اگرم قصد نداریم که از routeهای ساخته شده به وسیلهٔ Auth::routes() استفاده کنیم؛ کافیه که به صورت دستی داخل web.php روت‌های خودمون رو تعریف کنیم.ما حق دستکاری مستقیم روت‌ها داخل vendor رو نداریم؛ چرا که ما با composer.json میام و این فولدر رو ایجاد می‌کنیم و اصلا این پوشه نباید به کس دیگه‌ای داده بشه و خود به خود از composer.json ایجاد میشه.اما نکته ای هم که در مورد روت‌های ایجاد شده به وسیلهٔ Auth وجود داره اینه که وقتی وارد اون کنترلر می‌شیم؛ ممکنه که متدی که وابسته به اون روت هست رو به شکل مستقیم نبینیم؛ اما اون متد با use شدن از یک ترد خاص در کنترلر مورد استفاده قرار می‌گیره، مثلا:use AuthenticatesUsers;حالا بریم سراغ، کنترلرهای سیستم authentication، اولین کنترلری که برسی می‌کنیم؛ کنترلر register خواهد بود. سیستم auth لاراول دو تا روت register داره که یکی از اون‌ها به صورت get هست و دیگر post، در واقع get برای نمایش صفحهٔ register هست و post برای ثبت یک کاربر جدید در دیتابیس.در واقع متدهای سیستم auth لاراول که توی route ها می‌بینم از طریق فایل‌هایی که در مسیر vendor/laravel/ui/auth-backend قرار گرفتن، توی کنترلر use میشن و از این طریق از اون‌ها استفاده می‌کنیم:خب، از اونجایی که ما حق نداریم به شکل مستقیم توی vendor دست ببریم؛ باید توی همون کنترلر مد نظر که فایل auth-backend مورد استفاده (use) قرار گرفته، بیاییم و اون متد خاص رو override کنیم.با اینکه امکان تغییر تمامی این موارد وجود داره، اما بهتره که از سیستم دیفالت لاراول استفاده کنیم. توی کنترلرهای auth که توی app/Http/Controllers/Auth قرار دارن اگر متد validator ای وجود داشته باشه، کاملا قابل config شدن هست و می‌تونیم validation های خاص خودمون رو اعمال کنیم. طبیعتا توی RegisterController.pph این متد هست (حال ندارم متدهای دیگه رو دستی چک کنم!).آها، توی جلسات قبلی، توی مبحث migration و... با جدول users که خود لاراول به شکل دیفالت مایگریشن و... اون رو می سازه آشنا شدیم؛ حالا کافیه که migration رو با artisan انجام بدیم تا این جدول توی دیتابیس ساخته بشه و حتی می تونیم با seeder یا factory اون رو از دیتای مهمل پر کنیم!حالا اگر وارد روت register بشیم؛ خیلی راحت می‌تونیم ثبت نام و لاگین کنیم.توی سازنده HomeController از میدل‌ور auth استفاده کردیم تا چک کنه که لاگین صورت گرفته یا خیر و اگر صورت نگرفت دوباره ریدایرکت می‌کنه به صفحهٔ لاگین.</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Mon, 25 Jul 2022 03:00:56 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت چهارم</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%DA%86%D9%87%D8%A7%D8%B1%D9%85-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-oadtrbxs5fsv</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.قسمت ۶۰ تا ۷۰مبحث Factoryاین مفهوم به ما کمک می کنه تا کار با seeder راحت‌تر بشه، منظور از factory یک کارخونه هست که برای ما اطلاعات fake ایجاد می‌کنه. مسیرش هم که مشخصه، توی database و پوشهٔ factories قرار گرفته و یک فایل default داخلش داریم به اسم UserFactory.php، حالا ما به راحتی می‌تونیم برای جداول خودمون factory ایجاد کنیم.نحوه ایجاد یک factory:php artisan make:factory ArticleFactory --model=Articleنکتهٔ مهم در مورد factory اینه که این‌ها با مدل ها کار می‌کنن و قبل از ایجاد یک فکتوری باید مدل متناظرش رو ایجاد کرده باشیم. artisan این فایل رو توی پوشهٔ factories برای ما ایجاد می‌کنه و مدل به همراه Faker رو use می‌کنه و ما کافیه که توی بدنهٔ return مقادیری که قراره توی table قرار بگیره رو وارد کنیم:How to use a factoryنحوهٔ استفاده از factory هم به این شکله که لاراول برای اون یک helper function قرار داده به اسم factory که هر جایی که نیاز داشتیم می‌تونیم ازش استفاده کنیم و این قضیه مزیت factory نسبت به seeder هست چون seeder حتما باید داخل CLI ران بشه، اما factory خیر، مثلا خیلی راحت می‌تونیم این تابع رو توی controllerهای خودمون کال کنیم:$times_to_create = 15;
factory(Article::class, $times_to_create)-&gt;create();حتی می‌تونیم فکتوری بالا رو (یعنی تابع) رو توی متد run یک seeder فراخوانی کنیم.روابط بین جداول Relationships:۱- روابط یک به یک (one to one):به عنوان مثال، توی یک سایت هر کاربری یک پروفایل دارد و هر پروفایلی متعلق به یک کاربر هست پس روابط میان جدول users و profiles از نوع one to one خواهد بود. ارتباط میان جداول از طریق foreign key برقرار می‌شود. الان توی تصویر زیر، در جدول profiles یک فیلد داریم به اسم user_id که foreign key ما محسوب می‌شود.One to Oneبرای برقراری ارتباط یک به یک بین users و profiles باید مدل و migration اون‌ها رو بسازیم. خود لاراول به صورت دیفالت user رو داره، پس باید مدل و migration مربوط به Profile &#40;دقت به اسم: سینگل، upper&#41; رو بسازیم:php artisan make:model Profile -mو migration اون رو ردیف می‌کنیم و فیلدهای table  رو می‌سازیم. نکتهٔ مهم نحوهٔ ایجاد foreign key در migration هست؛ نوع یا data type کلید خارجی جدول ما باید با id جدول اولیه یکی باشه، یعنی اگر id از نوع bigIncrement هست؛ foreign key هم باید از نوع BigInteger باشه:توی تصویر بالا از onDelete و onUpdate هم می‌تونیم استفاده کنیم و بهش آرگومان cascade رو بدیم.حالا می‌تونیم migrate بزنیم.خب در ادامهٔ کار (مطابق تصویر زیر) یک شی به نام $user از مدل User ایجاد کردیم که روش متد find رو فراخوانی کردیم تا user با id یک رو برای ما برگردونه، بعدش متد profile از شی user رو صدا زدیم؛ طبیعتا وقتی یک چنین کاری می‌کنیم؛ مدل ما باید یک متد با نام profile داشته باشه که در قسمت پایین به تصویر کشیده میشه:توی تصویر زیر متد profile  رو توی متد مدل User تعریف کردیم:خب توی تصویر بالا، یک متد به نام profile داخل مدل User.php تعریف کردیم و با $this که اشاره داره به کلاس مدل ما، یک متد به نام hasOne رو فراخوانی کردیم و به عنوان پارامتر ورودی بهش Profile::class رو دادیم که در واقع مدل Profile ما هست. اینجا داریم می‌گیم که هر user یک پروفایل داره و اینطوری ارتباطه برقرار میشه. و یک سینتکس جایگزین هم برای Profile::class داریم اونم مقداری هست که همین عبارت برگشت میده، یعنی:&#039;App\Models\Profile&#039; دقیقا می‌تونیم برعکس این عملیات رو هم برای مدل Profile اجرا کنیم. با این تفاوت که توی مدل Profile باید از متد belongsTo استفاده کنیم؛ یعنی اینکه این profile مربوطه به فلان user:اساس کار این رابطه id و user_id یعنی پرایمری و کلید خارجی هست. اما نکتهٔ مهم اینه که لاراول چطوری می‌فهمه که user_id چی هست؟ لاراول براساس اسم جدول این عملیات رو انجام میده یعنی اینکه چون ما یه جدول داریم به اسم users، میاد مفردش می‌کنه و تهش یه _id میذاره و به اون مقدار فیلد دسترسی پیدا می‌کنه، اما اگر ما یک فیلد دیگه رو به عنوان foreign key تعریف کنیم باید اسمش رو به عنوان پارامتر به متدهایی مثل hasOne پاس بدیم:توی مدل Profile هم دقیقا می‌تونیم همین عملیات رو انجام بدیم:۱- روابط یک به چند (one to many):در این نوع از روابط یک مدل واحد با چندین مدل دیگر در ارتباط است.مثال: رابطه میان پست‌های یک وبلاگ و کامنت‌های آن. هر پست چندین کامنت داره، اما هر کامنت، فقط و فقط متعلق به یک پست خاصه.نحوهٔ پیاده‌سازی در لاراول:مایگریشن مربوط به جدول postsمایگریشن مربوط به جدول commentsتوی جدول comments اومدیم و post_id رو به عنوان کلید خارجی تعریف کردیم.خب برای دسترسی به کامنت‌های یک پست، اول نیاز داریم که اون پست خاص رو پیدا کنیم؛ این کار رو می‌تونیم توی کنترلر انجام بدیم و با استفاده از مدل Post و متدی مثل find پست با id یک رو پیدا کنیم:$post = Post::find(1);حالا برای پیدا کردن کامنت‌های این پست از جدول comments توی دیتابیس، باید متد comments رو از مدل Post فراخوانی کنیم:$comments = $post-&gt;comments;نحوهٔ پیاده‌سازی متد comments هم به شکل زیر در کنترلر Post هست:خیلی ساده می‌تونیم روی متد comments هم کوئری بزنیم:$comments = $post-&gt;comments-&gt;where(&#039;is_status&#039;, 1);بریم برای رابطهٔ معکوس، یعنی چک کنیم؛ ببینیم که فلان کامنت برای کدوم پسته:$comment = Comment::find(1);
$post = $comment-&gt;post;حالا باید متد post رو توی مدل Comment تعریف کنیم:نحوهٔ پاس دادن foreign key (اگر کلید خارجی نام متفاوتی داشت):توی تصویر بالا یک نکتهٔ ظریف وجود داره و اونم local_key هست که به صورت دیفالت id  هست. از اونجایی که توی این رابطه، کامنت‌ها با استفاده از post_id به id مدل Post وصل شدن، پس id که اینجا تعریف شده (داخل مدل Post)، لوکال نامیده میشه (متعلق به خودشه).۳- رابطهٔ Many to Many:مثال:فرض کنیم که یک table داریم به اسم users که توش کاربران مختلفی تعریف شدن و یک جدول داریم به اسم roles که نقش‌های مختلفی برای کاربران تعریف شده، مثلا admin و نویسنده و ناظر و... خب، هر کاربری می تونه چندین role داشته باشه و هر role می‌تونه متعلق به چندین کاربر باشه، این نوع رابطه رو بهش می‌گیم چند به چند:تفاوت این رابطه، با روابطی که تا الان کار کردیم در اینه که ما نمی تونیم توی جدول role کلید خارجی تعریف کنیم؛ چرا که با این کار، هر role رو فقط به یک کاربر اختصاص می دیم؛ ما نیاز داریم که یک نقش رو به چند کاربر بدیم و برعکس، برای حل این مشکل باید از جدول رابط یا pivot table استفاده کنیم؛ که نقش محور یا رابط رو بین دو تا جدول اصلی ما به عهده می‌گیره:نکتهٔ مهم در ایجاد pivot table نام‌گذاری اون هست که طبق استاندارد لاراول، ترکیبی مفرد از اسامی دو تا جدولی هست که قراره با هم relation داشته باشند و این اسم ترکیبی براساس حروف الفبا هست؛ یعنی چون r زودتر از u میاد اسم جدول میشه role_user:توی pivot table می‌گیم که مثلا یک user با user_id یک، role_id فلان رو داره، حالا اگر همین کاربر یک نقش دیگه‌ای هم داشت؛ یک ریکورد دیگه هم به همین pivot table اضافه خواهیم کرد.تصویری از role_user migration:ما برای انجام این عملیات، نیازی به مدل role_user نداریم و فقط به migration اون نیاز داریم.باید دقت کنیم که هر دو تا attribute از نوع primary و unique هستند.توی تصویر زیر، می‌خوایم ببینیم که چطوری میشه نقش‌های مختلف یک user رو پیدا کرد، در واقع اینجا شروع کاره و تصاویر بعدی، قسمت‌های مختلف این ارتباط رو نشون خواهد داد:متد roles در مدل User:توی کنترلر که متد roles یا به عبارت درست‌تر، رابطهٔ roles رو که فراخوانی کردیم؛ می‌تونیم از طریق اون به جدول pivot هم دسترسی داشته باشیم:$user = User::find(1)
$roles = $user-&gt;roles-&gt;first();
$roles-&gt;pivot;توی کد بالا، حتما باید متد first() رو روی رابطهٔ خودمون اجرا کنیم؛ وگرنه امکان دسترسی به pivot وجود نداره. هم چنین، وقتی table پیوت برمی گرده، فقط ستون‌های اصلی ما یعنی role_id و user_id برگشت داده میشه و به ستون‌های updated_at و created_at دسترسی نداریم؛ برای دسترسی به این ها باید توی متد roles توی مدل User تغییراتی ایجاد کنیم:withTimeStampsبرای برگردوندن سایر فیلدها هم می‌تونیم از دستور زیر استفاده کنیم:حتی یک دستور شرطی مربوط به Pivot table هم داریم:return $this-&gt;belongsToMany(Role::class)-&gt;wherePivot(&#039;is_status&#039;, 1);ارتباط معکوس در رابطهٔ Many to Many:یعنی چی؟ یعنی اینکه ما یک role داریم؛ می‌خوایم چک کنیم که کدام یک از userهای ما این role رو دارن:حالا می تونی داخل مثلا یک کنترلر، چک کنیم که فلان role، مربوط به کدوم userها میشه:آرگومان‌های مربوط به متد belongsToMany:return $this-&gt;belongsToMany(Role::class, &#039;pivot_table&#039;, &#039;local_foreign_key&#039;, &#039;other_foreign_key&#039;, &#039;local_id&#039;, &#039;other_id&#039;);مبحث وارد کردن دیتا به جدول Pivot:این بحث خیلی مهمه، ما مدلی برای Pivot table نداریم و در قسمت بالا هم گفتیم که فقط یک migration برای این جدول ایجاد کردیم؛ حالا چطور بهش دیتا وارد کنیم؟در اینجا دو تا متد بسیار مهم save و attach وارد عمل میشن:توی مثال زیر، ما قصد داریم که role شماره ۱ رو بدیم به user شماره ۲:// find role number one
$role = Role::find(1);
// find user number one:
$user = User::find(2);
// giving the user 2, the role number 1
$result = $user-&gt;roles()-&gt;save($role); 
// the method, attach is exact like the save method:
$result = $user-&gt;roles()-&gt;attach($role);برای برداشتن سطح دسترسی خاصی از یک user از دستور زیر استفاده میشه:$result = $user-&gt;roles()-&gt;detach($role);نهایتا دستور کاربردی بعدی، دستور sync هست؛ این دستور میاد تمامی roleهای یک کاربر رو پاک می‌کنه و roleهای جدیدیی که بهش دادیم رو روی کاربر ست می‌کنه:$result = $user-&gt;roles()-&gt;sync($role);حالا می‌تونیم کاری کنیم که sync رول‌های قبلی رو پاک نکنه و فقط مثل save یا attach عمل کنه (باید دقت کنیم که مقدار برگشتی این متدها در صورت درست اجرا شدن کمی متفاوته، مثلا اتچ اگر درست اعمال بشه، null برمی‌گردونه):$result = $user-&gt;roles()-&gt;sync($role, false);۴- رابطهٔ Has One Throughیک رابطهٔ یک با یک با این تفاوت که برای پیاده‌سازی آن از یک جدول واسط استفاده می‌شود:مثال برای درک بهتر:توی مثال بالا، هر user فقط یک ماشین داره (رابطه یک به یک)، یک جدول رابط داریم به اسم cars_info که اطلاعات منحصر به فرد ماشین‌ها در این جدول قرار گرفته، به عبارت دیگه، هر ماشینی توی cars_info یک سطر رو اشغال کرده (رابطه یک به یک).قصد ما توی این رابطه اینه که با داشتن یک کاربر (مثلا کاربر شماره ۱)، اطلاعات ماشین اون کاربر رو به دست بیاریم. اما از اونجایی توی cars_info فیلد user_id تعریف نشده، پس نمی تونیم مستقیم به اطلاعات ماشین اون user دسترسی پیدا کنیم. بنابراین باید از car_id استفاده کنیم.پیاده‌سازی این رابطه:مایگریشن جدول cars_info مایگریشن جدول users مایگریشن جدول cars برای تمامی این‌ها مدل هم داریم.خب، بریم توی یکی از کنترلرها برای پیاده‌سازی این رابطه:$user = User::find(1);
$car_info = $user-&gt;CarInfo;خب، پس باید یک متد با نام CarInfo بنویسیم و توی مدل User قرار بدیم:public function carInfo(){
return $this-&gt;hasOneThrough(CarInfo::class, Car);
}به این شکل رابطه رو برقرار کردیم.سایر پارامترهای متد hasOneThrough:پارامتر اول، اسم فیلد کلید خارجی جدول واسطپارامتر دوم، اسم فیلد کلید خارجی جدول نهاییپارامتر سوم، کلید اصلی جدول اولیهپارامتر چهارم، کلید اصلی جدول دوم (واسط، در اینجا cars).۵- رابطهٔ Has Many Through:یک رابطهٔ یک به چند، اما با یک جدول واسط:توی رابطهٔ بالا، جدول users جدول واسط ما هست بین posts و countries، در واقع ما می‌خوایم پست های کاربران مربوط به یک کشور خاص رو پیدا کنیم. چون کاربران یک کشور می‌تونند چندین پست داشته باشن، از طرفی هر پست متعلق به کاربر یک کشور هست به این اسم نام گذاری شده.ما به سه مدل و سه migration برای Country Post و User نیاز داریم.توی شکل بالا، ما از سمت راست یعنی جدول countries می‌خوایم برسیم به جدول posts و از جدول users به عنوان واسط استفاده می‌کنیم.تصور کنید، کشور ایران، ۱۰۰ تا user داره و هر کدوم از این userها می‌تونن چندین پست داشته باشند. پس ما می‌تونیم از طریق اسم کشور به پست‌های مربوط به یک کشور خاص دست پیدا کنیم. اما پیاده‌سازی این relation در لاراول:$country = Country::find(1);
$posts = $country-&gt;posts;تعریف متد posts در مدل country:خب، توی تصویر بالا، داخل کنترلر Country، یک متد posts تعریف کردیم و داخلش یک رابطه رو برگشت دادیم، گفتیم که مدل Country از طریق جدول users به یک سری پست‌هایی دسترسی دارد.متد hasManyThrough دو تا آرگومان داره، اولی جدول نهایی و دومی، جدول واسط.۶- روابط پلی مورفیکدر این روابط یک مدل می‌تونه همزمان به چند مدل دیگه تعلق داشته باشه، مثلا یک جدول برای پست‌هامون داریم و یک جدول هم برای کامنت‌ها، یک پست ممکنه چندین کامنت داشته باشه و بنابراین باید یک کلید خارجی به اسم post_id بهش تعلق بگیره، توی این سناریوی که الان گفتیم؛ یک مدل فقط به یک مدل وصله، حالا تصور کنید که یک جدول داشته باشیم برای محصولات (products) خودمون، و کاربرها می‌تونن برای این محصولات هم نظر بذارن، اگر قرار باشه به شکل قدیمی و بدون استفاده از روابط پلی‌مورفیک عمل کنیم؛ باید یک جدول comments هم برای products ایجاد کنیم و یک product_id براش ست کنیم که حرکت جالبی نیست.حالا فرض کنید جدول های دیگه هم داشته باشیم که نیاز به کامنت داشته باشن، بنابراین روابط پلی مورفیک میان و این مشکل رو برای ما حل می‌کنن، یک جدول می‌تونه به بی‌نهایت جدول دیگه وصل بشه.۶-۱- روابط پلی‌مورفیک یک به یک (one to one):در این نوع روابط که مشابه روابط یک به یک معمولی هستن، اما یک تفاوت مهم وجود داره و اونم اینه که مدل مقصد تنها به یک مدل مبدا یا اولیه وابسته نیست.مثال:خب، توی تصویر بالا، جدول images جدول مقصد ما هست که به دو تا جدول مبدا متصل شده، جدول posts و جدول products، ما ممکنه کلی جدول داشته باشیم که نیاز باشه عکس‌هاشون یا نظراتشون رو توی یک جدولی جا بدیم، بنابراین از این نوع رابطه استفاده می‌کنیم؛ اما نکتهٔ مهم در جدول مقصد اینه که دو تا فیلد داره یکی به اسم imageable_id و دیگر imageable_type که توی id، آی‌دی جداول مبدا برای اون ریکورد خاص قرار می‌گیره و توی imageable_type، مدل مربوط به اون جدول مثلا App\Models\Post که مشخص می‌کنه که این مربوط به چه مدل/جدولی هست.نمایی از جدول images در دیتایس:خب، توی تصویر زیر، یه کنترلر داریم و اونجا با استفاده از مدل Post به دنبال یک پستی گشتیم و می‌خواستیم ببینیم که عکس مربوط به اون توی جدول images چیه:مایگریشن‌های مربوط به Post و Product که چیز خاصی نداره، اما برای Image:توی تصویر بالا، خطی که هایلایت شده، فیلدهای imageable_type و imageable_id رو برای ما ایجاد می‌کنه.خب، ما باید توی مدل جدول مقصد یعنی Image یک متد از نوع polymorphic تعریف کنیم:حالا باید رابطهٔ بالا رو توی مدل های Post و Product کامل کنیم؛ نحوه برقراری ارتباط مدل Post با مدل Image:توی تصویر بالا، متد حتما باید از نوع مفرد باشه (مثل image)، و یک رابطه پلی‌مورفیک از نوع یک به یک رو برمی‌گردونه، پارامتر اول، مدلی هست که باهاش مرتبطه و پارامتر دوم، اسم متدی هست که بهش وصله.عینا همین کار رو توی product انجام میدیم:توی این رابطهٔ یک به یک، دقیقا عکس قضیه هم صادقه هم میشه مثلا گفت، فلان تصویر مربوط به چه مدل/جدولی میشه:$image = Image::find(3);
$image-&gt;imageable;داخل مدل Image، جایی که رابطهٔ پلی مورفیک ایجاد شده، آرگومان خاص و اضافه‌تری نیاز نداریم؛ اما می‌تونیم توی Post و Product برای رابطهٔ خودمون یک سری آرگومان اضافه، بسته به نیاز داشته باشیم:آرگومان‌ها:۱- اسم مدلی که قراره باهاش رابطه برقرار بشه۲- اسم رابطهٔ پلی‌مورفیک۳- tableable_type۴- tableable_id۵- id مدلی که رابطه توش نوشته میشه (در اینجا Post).نحوه وارد کردن دیتا به جدول images در روابط پلی‌مورفیک:یک نکته اینکه باید دقت کنیم که fillable رو اوکی کنیم؛ چون داریم mass assign انجام میدیم.نحوهٔ آپدیت کردن:نحوهٔ Delete:۶-۲- رابطهٔ یک به چند در پلی‌مورفیکتوی این تصویر یک نمای کلی از این نوع رابطه رو می‌بینیم که دو تا جدول داریم به اسم‌های photos و videos که هر کدوم ممکنه چندین کامنت داشته باشن، یعنی هر پستی ممکنه چندین کامنت داشته باشه، اما هم کامنتی فقط و فقط به یک پست متعلقه:نمایی از جدول comments توی دیتابیس:نمایی از migration مربوط به comments:خب، طبق معمول بریم سراغ کنترلر و تا به مقادیری که می خوایم از مدل بکشیم بیرون دسترسی داشته باشیم:حالا باید رابطهٔ پلی‌مورفیک رو توی مدل Comment ایجاد کنیم:حالا بریم سراغ مدل های Post و Video برای کامل کردن رابطهٔ پلی‌مورفیک:اینجا می‌تونیم خروجی درخواست خودمون در HomeController رو ببینیم:مباحث دقیقا برای Video هم همونه! پس دیگه چیزی اضافه نمی‌کنیم. ۶-۳- رابطهٔ چند به چند پلی مورفیک:توی مثال زیر، پست‌ها و ویدئوهای ما می‌تونه در چندین category قرار بگیره و هر category هم چندین پست و video داره:توی روابط Many to Many معمولی، از یک جدول واسطه به اسم Pivot table استفاده می‌کردیم که جریان داده به این شکل می‌شد، از پست‌ها به رابطه و از رابطه به دسته‌بندی‌ها، این‌جا هم همین جدول رو داریم به اسم categorizables.اینجا category_id کلید خارجی جدول categories هست.نمایی از جدول categorizables:مایگریشن جدول categorizables:توی این جدول id و timestamps رو نداریم.دوباره مباحث تکراری هست و مشابه موارد قبل، می دونیم که چطور باید روابط رو برقرار کنیم و فقط سریع با اسکرین‌شات و بدون توضیح اضافه ازشون عبور می‌کنیم:دسترسی به کتگوری های یک پست داخل کنترلر:برقراری رابطه در مدل Post:دقیقا همین متد توی همهٔ مدل های مبدا قابل استفاده هست.آرگومان‌های مربوط به متد morphToMany:۱- نام مدلی که قراره باهاش رابطه برقرار کنیم۲- نام رابطهٔ پلی مورفیک ما۳- اسم جدول واسط (categorizables)۴- کلید خارجی مدل مقصد (category_id)۵- کلید خارجی مدل‌های مبدا (categorizable_id)۶- کلید اصلی مدلی که در اون رابطه رو می‌نویسیم (id)۷- کلید اصلی مدل مقصد (id)۸- اگر true باشد، رابطه معکوس می‌شوداین آرگومان‌های توی اسکرین‌شات پایین مشخص هستن:حالا بریم سراغ اینکه با انتخاب یک Category خاص، به پست‌ها و ویدئوهاش دسترسی پیدا کنیم:برای برقراری ارتباط بالا، باید متدهای posts و videos رو توی مدل Category تعریف کنیم:چهار عمل اصلی در روابط Many to Many:Save &amp; AttachAttachSyncDetach</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Tue, 19 Jul 2022 17:41:48 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش API نویسی در Laravel (کامل نشده...)</title>
                <link>https://virgool.io/@maslak/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-api-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-%D8%AF%D8%B1-laravel-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-pefns7bkfoct</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.منبع: TopLearnAPI Flow:نحوه کارکرد Rest APIاین سایت یک Fake API میده تا ما بتونیم باهاش کار کنیم.برای کار با API معمولا دو تا آپشن وجود داره، Rest API و دیگری GraphQL که اولی کاربردی‌تره.آشنایی اولیه با لاراول و API نویسی در لاراول:اول یک پروژه ایجاد می‌کنیم:composer create-project laravel/laravel my_api_project &amp;quot8.x&amp;quotاون قسمت آخر که ورژن وارد شده، اختیاریه و می‌تونیم با توجه به نیاز کلا نادیده بگیریمش.ما باید Routeهای مربوط به API رو داخل فایل api.php داخل پوشهٔ routes بنویسیم. باید دقت داشته باشیم که Route Service Provider میاد و یه سری تغییراتی روی routeهای ما اعمال می‌کنه، مثلا به اول همه اون‌ها یک api اضافه می‌کنه و همین‌طور یک سری middleware اعمال می‌کنه و... که فعلا باهاش کاری نداریم.یک میکروفریمورک هم داریم به اسم lumen که مبتنی بر لاراول هست و برای توسعه API که در اون سرعت مهمه مورد استفاده قرار می‌گیره.استارت پروژه:خب، طبیعتا ما نیاز داریم که یه سری عملیات CRUD روی دیتابیس خودمون انجام بدیم؛ اول از همه نیازه که یک دیتابیس ایجاد کنیم؛ می تونیم از CLI یا phpMyAdmin یا هر چیز دیگه‌ای برای این کار کمک بگیریم. و باید اطلاعات DB رو توی فایل .env وارد کنیم.حالا نیازه که یک migration برای table خودمون ایجاد کنیم:php artisan make:migration create_api_users_table نمونه‌ای از یک migration به همراه متدهای up و down:&lt;?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class Users extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create(&#039;api_users&#039;, function (Blueprint $table) {
            $table-&gt;id();
            $table-&gt;string(&#039;name&#039;);
            $table-&gt;string(&#039;family&#039;);
            $table-&gt;boolean(&#039;status&#039;)-&gt;default(0);
            $table-&gt;timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists(&#039;api_users&#039;);
    }
}متد up وقتی اجرا میشه که ما با دستور php artisan migrate می‌خوایم یک migration رو اجرا کنیم و دستور down زمانی اجرا میشه که دستور rollback رو ران کنیم.حالا با اجرای دستور زیر، migration صورت می‌گیره:php artisan migrateخب، اگر بخوایم روی جدول users خود لاراول کار کنیم؛ کافیه که با seeder از پیش تعریف شده، دیتای fake وارد جدول کنیم؛ برای این کار، اول باید seeder رو فعال کنیم. باید بریم توی پوشهٔ database و پوشهٔ seeders و DatabaseSeeder.php عبارت زیر رو که comment شده، از حالت کامنت خارج می‌کنیم و بعدش دستور زیر رو اجرا می‌کنیم:// uncomment
\App\Models\User::factory(10)-&gt;create();

//run
php artisan db:seedحالا برای ادامهٔ کار باید یک کنترلر ایجاد کنیم:php artisan make:controller UsersControllerحالا توی api.php یک Group Route درست می‌کنیم:Route::group([], function ($router){
    $router-&gt;get(&#039;users&#039;, \App\Controllers\UsersController::class, &#039;index&#039;);
});حالا می‌ریم توی کنترلر خودمون و متد index رو تعریف می‌کنیم:حالا کافیه که برای دسترسی به api با متد get آدرس زیر رو وارد کنیم:localhost:8000/api/users</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Mon, 11 Jul 2022 20:04:25 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت سوم</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%B3%D9%88%D9%85-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-caad7mmd0tq3</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.(هر پارت شامل ۲۰ قسمت)ادامهٔ مبحث Validation:اگر قرار باشه که یک فیلدی از ۲ منظر Validate بشه، باید از Pipe استفاده کنیم:حالا اگر کاربر بیاد و مثلا ایمیل رو نادرست وارد کنه، لاراول یک error برمی‌گردونه و همچنین مقادیری که توی فیلدهای مختلف وارد کرده بود همگی پاک می‌شن که این حرکت اصلا کاربرپسند نیست و ما می‌تونیم با استفاده از یک helper function به اسم old دوباره اون مقدار رو فراخوانی کنیم:فراخوانی مجدد مقادیر ارسال شده در صورت بروز خطا در Validationکل این پروسه با flash session هندل میشه و با یک refresh مجدد، حذف میشه.به جای Pipe می‌تونیم از آرایه هم استفاده کنیم؛ همچنین اگر یک Select Option از نوع multiple داشته باشیم که یک آرایه رو بفرسته سمت سرور، می‌تونیم با array چک کنیم که مقداری که داره میاد از نوع array هست یا خیر و با استفاده از .* می‌تونیم Validation رو برای تک تک عناصر آرایه هم انجام بدیم:فارسی‌سازی یا سفارشی‌سازی Errorهای Validation:توی پوشهٔ resources یک پوشه داریم به اسم lang و یک پوشه به نام en که داخلش فایل‌های زیر هست:auth.php
pagination.php
passwords.php
validation.phpداخل فایل validation، ارورهای ما قرار گرفته، حالا ما کافیه که یک پوشه به اسم زبانی که داریم داخل lang ایجاد کنیم و فایل validation رو توی اون translate کنیم. داخل این پوشه تمامی فایل‌های بالا رو قرار می‌دیم و ترجمه می‌کنیم. حالا چطور به لاراول بفهمونیم که به جای en از fa استفاده کنه؟باید بریم داخل پوشهٔ config و فایل app.php و مقدار locale رو به fa تغییر می‌دیم.نمونه‌ای از فایل validation.php:&lt;?php
    &#039;accepted&#039; =&gt; &#039;The :attribute must be accepted.&#039;,
    &#039;accepted_if&#039; =&gt; &#039;The :attribute must be accepted when :other is :value.&#039;,
    &#039;active_url&#039; =&gt; &#039;The :attribute is not a valid URL.&#039;,
    &#039;after&#039; =&gt; &#039;The :attribute must be a date after :date.&#039;,
    &#039;after_or_equal&#039; =&gt; &#039;The :attribute must be a date after or equal to :date.&#039;,
    &#039;alpha&#039; =&gt; &#039;The :attribute must only contain letters.&#039;,
    &#039;alpha_dash&#039; =&gt; &#039;The :attribute must only contain letters, numbers, dashes and underscores.&#039;,
    &#039;alpha_num&#039; =&gt; &#039;The :attribute must only contain letters and numbers.&#039;,
    &#039;array&#039; =&gt; &#039;The :attribute must be an array.&#039;,
    &#039;before&#039; =&gt; &#039;The :attribute must be a date before :date.&#039;,
    &#039;before_or_equal&#039; =&gt; &#039;The :attribute must be a date before or equal to :date.&#039;,
?&gt;حالا منظور از این :attribute چیه؟ در واقع نام فیلدی هست که از سمت فرانت اومده به back، حالا کافیه که آرایهٔ attributes رو هم با توجه به نام فیلدهای مختلفی که داریم سفارشی سازی کنیم:&lt;?php
    &#039;attributes&#039; =&gt; [
&#039;email&#039; =&gt; &#039;email_in_your_language&#039;
],
?&gt;ایجاد یک Validation اختصاصی:توی پوشهٔ app/Http/Requests و فایل LoginRequest.php  اومدیم و توی متد rules، انواع validationها رو، روی فیلدهای مختلف مثل email و password اعمال کردیم؛ برای تعیین validationهای مختلف می‌تونیم از [] و | استفاده کنیم. اما یکی از مزایای [] اینه که میشه داخلش تابع نوشت.تعریف تابع سفارشی برای validationتوی تصویر بالا ما اومدیم و یک تابع یا closure برای validation سفارشی خودمون ایجاد کردیم. با استفاده از تابع func_get_args چک کردیم که این تابع چه ورودی‌هایی داره و این رو توی خروجی داشتیم:ورودی‌های این تابع به ترتیب شامل $attr و $value و Closure هست. مثال:ما می‌تونیم validation اختصاصی خودمون رو به Service Provider هم اضافه کنیم و براش یک اسم ساده در نظر بگیریم و فقط داخل rules فراخوانیش کنیم:&#039;password&#039; =&gt; [&#039;required&#039;, &#039;your_chosen_name_for_validation&#039;]برای این کار می‌تونیم از Service Providerهای از پیش تعریف شده استفاده کنیم و یا اینکه یک Service Provider ایجاد کنیم. اگر قرار باشه از یک Service Provider از پیش تعریف شده مثل AppServiceProvider استفاده کنیم؛ باید تعریف Validation یا همون تابع Validation خودمون رو توی متد boot تعریف کنیم.داخل متد boot باید بنویسیم Validator و با انتخاب اون و Ctrl Alt I بیاییم و Facade اون رو use کنیم:&lt;?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Validator as BaseValidator;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Validator::extend(&#039;your_chosen_name_for_validation&#039;, function ($attr, $value, $parameters = [], BaseValidator $validator) {
            if ($value &lt; 10) {
                return false;
            }
            return true;
        }, &#039;:attribute is not valid&#039;);
    }
}
وقتی اومدیم Validatorای که use کردیم و اون رو extend کردیم؛ باید بهش یه سری مقادیر رو پاس بدیم؛ مقدار اول، اسم این validator سفارشی هست و دومی، یک تابع که ۴ تا پارامتر داره، اولی همون پارامتری هست که از فرانت بهش پاس داده میشه تا آنالیز بشه، دومی مقدار اون فیلد، سومی پارامترهای مختلف به شکل یک آرایه (اینجا رو باید دوباره توی فیلم نگاه کنم) و در نهایت یک Validator که باید Use بشه و البته چون از قبل یک Validator رو use کردیم باید براش یک alias در نظر بگیریم، در نهایت هم extend یک پارمتر می‌گیره به عنوان خروجی که برمی‌گردونه (یک مقدار درهم و برهم شد:) ).دیتابیس، نحوهٔ اتصال به آن و نوشتن یک Query ساده و Fetch دیتا از اون:فایل .envاین فایل توی root نرم‌افزار ما قرار داره که یک سری متغیر محیطی داره توش. متغیرهای تعریف شده توی این فایل به شکل بزرگ هستن و Value اون‌ها هم داخل سینگل یا دابل کوتیشن نیستن.مقادیری که داخل این فایل تعریف میشن داخل کل پروژه در دسترس هستن. این فایل به ما کمک می‌تونه تا متغیرهایی که مهم هستن و البته تغییرپذیر داخلش تعریف کنیم و در کل پروژه ازش استفاده کنیم بدون اینکه نیاز باشه توی فایل‌های زیادی این مقادیر رو ست کنیم یا تغییر بدیم.این فایل، جز چیزهایی هست که به وسیله git نادیده گرفته میشه و مثلا موقع آپلود توی github آپلود نمیشه، حالا توی composer.json ما میایم و چک می‌کنیم که آیا این فایل وجود داره یا خیر و اگر وجود نداشت، فایل .env.example کپی میشه به اسم .env و بهتره که متغیرهایی که ما تعریف می‌کنیم داخل env توی example هم تعریف کنیم.نحوه دسترسی به این متغیرهای چطوریه؟یک helper function وجود داره به اسم env که یک پارامتر می‌گیره به اسم متغیرهای تعریف شده توی خود فایل .env و اینطوری بهشون دسترسی پیدا می‌کنیم. این helper function دارای ۲ پارامتر است:env(&#039;VARIABLE_NAME&#039;, &#039;Default Value&#039;);برای کامنت کردن متغیرهای از # استفاده می‌کنیم.برای وصل شدن به دیتابیس، باید یک سری مقادیر رو توی فایل env ست کنیم؛ که شامل موارد زیر میشه:DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=blog
DB_USERNAME=root
DB_PASSWORD=لاراول از اونجایی که از PDO استفاده می‌کنه، می‌تونه به چندین مدل دیتابیس وصل بشه، توی متغیر DB_CONNECTION می‌تونیم نوع دیتابیس مورد استفاده توی پروژه رو انتخاب کنیم.این مقادیر میرن توی فایل database.php توی پوشه config  و اونجا ست میشن.نحوهٔ Query زدن به دیتابیس:توی لاراول میشه به ۲ روش به دیتابیس دسترسی داشت:DB Class
Eloquent (The ORM of Laravel)مورد مشترک میان این دو مورد، استفاده از Query Builder هست؛ Eloquent یک سری کلاس به اسم Model ایجاد می‌کنه و از طریق این مدل ها به دیتابیس و جداول مورد نظر ما وصل میشه. اما DB به شکل مستقیم با دیتابیس ارتباط برقرار می‌کنه.کلاس DB:خب ما برای استفاده از این کلاس، میایم و از توی یکی از کنترلرهای خودمون فراخوانیش می‌کنیم؛ برای استفاده از این کلاس می‌تونیم از Facadeش استفاده کنیم و حواسمون باید باشه که حتما Facade این کلاس رو با Ctrl Alt I فراخوانی یا Use کنیم.حالا می‌تونیم به روش زیر یک کوئری به دیتابیس و جدول دلخواه خودمون بزنیم:$res = DB::connection(&#039;mysql&#039;)-&gt;table(&#039;table_name&#039;)-&gt;get();
$res = DB::table(&#039;table_name&#039;)-&gt;get();
$res = DB::table(&#039;table_name&#039;)-&gt;toSql();
dd($res);مقداری $res که برگشت داده میشه، در واقع یک Illuminate\Support\Collection هست و یه نوع تایپ جدید هست که به وسیلهٔ لاراول ایجاد شده و امکانات بیشتری رو به آرایهٔ دیفالت php اضافه می‌کنه. این collection به راحتی قابل iterate کردنه و می‌تونیم به عناصرش با foreach دسترسی پیدا کنیم.خب، می‌تونیم توی کنترلر پاسش بدیم به view:return view(&#039;index&#039;, [&#039;the_name_of_array&#039;, $res]);
// or using compact:
return view(&#039;index&#039;, compact(&#039;the_name_of_array&#039;));حالا توی view هم می‌تونیم به foreach اون رو iterate کنیم:چند متد کاربردی از DB Class:آشنایی با چهار عمل اصلی دیتابیس:CRUDنحوهٔ Insert کردن دیتا به دیتابیس:Insert Data to Databaseنحوهٔ Update کردن دیتا در جدول:Update table دستور delete با DB Class:Delete using DB classاگر متد Delete رو روی table اجرا کنیم؛ تمام محتویات جدول رو پاک خواهد کرد.متدی truncate برای این استفاده میشه که کلا table به همراه sequences رو پاک کنه.مبحث Eloquent ORM و Modelها:خب، Object-relational mapping یک ابزاره که به برنامه‌نویس کمک می‌کنه تا بدون نیاز به نوشتن کوئری‌های دیتابیس مثل SQL به راحتی دیتای مورد نیازش رو از دیتابیس واکشی کنه، در واقع ORM یک سری کد و توابع داره که واسط بین دیتابیس و کدهای ما میشن و دیتای مورد نیاز ما رو بدون دردسر فراهم می‌کنند.در واقع ORM داده های پایگاه داده ما رو به یک سری Object تبدیل می‌کنه که می‌تونیم روشون یک سری متد رو ران کنیم.هر زبانی ممکنه که چندین ORM داشته باشه، خود لاراول یک ORM اختصاصی داره به نام Eloquent که حول محور Modelها می‌چرخه.مدل‌ها توی ساختار لاراول توی پوشهٔ app قرار می‌گیرن، مثلا به طور پیش‌فرض یک User.php داریم که با جدول کاربران ما ارتباط داره.چطوری یک مدل اختصاصی ایجاد کنیم؟php artisan make:model the_name_of_the_modelنحوهٔ نام‌گذاری مدل‌های و جداول در لاراول به این صورته که اسم مدل باید مفرد باشه و با حرف بزرگ شروع بشه و اسم جدول باید جمع باشه و با حرف کوچک شروع بشه://Model:
Article
//Database Table:
articlesالبته ممکنه اسم مدل یک چیزی باشه و اسم جدول یک چیز دیگر...خب حالا چطور از این مدل‌ها استفاده کنیم؟کافیه که برای دسترسی به مدل خودمون توی controller اون مدل رو use کنیم (ctrl alt i):$articles = Article::all();
return view(&#039;index&#039;, compact(&#039;articles&#039;));اما اگر بخوایم که مدل ما به یک table خاص وصل بشه، کافیه که توی کلاس Articleای که داریم؛ مقدار زیر رو override کنیم:protected $table = &#039;article_blahblah&#039;;اما بهتره که از قواعد و convention های لاراول پایبند باشیم. الان کل این پروسه رو بهش می‌گن ORM.متدهای ORM:متد all کل سطرهای table رو برای ما برمی‌گردونه و می‌تونیم روش هر عملیاتی که دلمون می‌خواد رو بریم.یک متد دیگه‌ای داریم به نام find که به صورت دیفالت روی id ما ران میشه و سطر متناظر رو برمی‌گردونه:Article::find(id_num);می‌تونیم به جای یک سینگل int، یک آرایه از intها به find بدیم تا اون سطرها رو برای ما برگردونه.متد کاربردی بعدی، متد where هست که برای فیلتر کردن خروجی‌های table مورد استفاده قرار می‌گیره:$articles = Article::where(&#039;id&#039;, 2)-&gt;get();متد where یک پارامتر میانی هم داره که بین ستون و مقدار قرار می‌گیره و می‌تونه &lt;=&gt; باشه.متد کاربردی بعدی first هست که اولین سطر table رو برای ما برمی‌گردونه:$articles = Article::first();متد findOrFail میاد find می‌کنه و اگر نبود؛ 404 برمی‌گردونه.متد orderBy که یک ستون رو می‌گیره و آرگومان دومش هم ASC و DESC هست:$articles = Article::orderBy(&#039;title&#039;, &#039;DESC&#039;)-&gt;get();و اما متد take که یک int به عنوان تعداد ردیف‌های برگشتی می‌گیره و اون‌ها رو برمی‌گردونه:$articles = Article::taker(num)-&gt;get();نحوهٔ Insert کردن دیتا با استفاده از ORM:برای این کار چندین روش وجود داره که به ترتیب همه اون‌ها رو مرور خواهیم کرد:روش اول اینطوریه که یک شی از مدل ایجاد می‌کنیم و به روش شی‌گرایی مقادیری رو به هر ستون نسبت می‌دیم و در نهایت متد save رو روی شی ایجاد شده از مدل ران می‌کنیم:لاراول به صورت پیش فرض دو تا ستون برای هر جدول در نظر می‌گیره و موقع update و یا insert میاد و مقدار timestamp رو توی اون ها ذخیره می‌کنه:updated_at
created_atبهتره که برای جدول های خودمون این دو تا ستون رو هم بسازیم و تایپ اون‌ها رو هم timestamp ست کنیم و همینطور اون‌ها رو null کنیم.روش دوم، وارد کردن دیتا با استفاده از یک آرایه هست:Mass Assignبرای اینکه بتونیم mass assign رو اجرا کنیم حتما باید داخل controller خودمون مقدار زیر رو ست کنیم تا به لاراول بگیم که چه ستون‌هایی اجازهٔ پر شدن دارن و با ارور mass assign روبرو نشیم:protected $fillable = [&#039;title&#039;, &#039;body&#039;];یه لیست سیاه هم داریم که اجازهٔ پر شدن به یه سری ستون خاص رو نمیده:protected $guarded = [&#039;title&#039;];روش بعدی هم از نوع mass assign هست با این تفاوت که به جای تعریف شی و ران کردن متد save از create استفاده می‌کنیم:$articles = Article::create([
    &#039;title&#039; =&gt; &#039;Article #&#039;,
    &#039;body&#039; =&gt; &#039;Something...&#039;
]);روش سوم به نام firstOrCreate شناخته میشه که توی آرگومان اول میاد و یک آرایه از ستون و مقدار متناظر می‌گیره و چک می‌کنه که آیا وجود داره یا خیر و اگر وجود نداشت؛ آرگومان دوم که شامل مقادیر ورودی ما هست رو وارد دیتابیس می‌کنه:firstOrCreateحالا خیلی راحت می‌تونیم table خودمون رو update کنیم؛ اینجا چند تا مثال می‌نویسم:$articles = new Article();
$articles-&gt;where(&#039;column&#039;, &#039;value&#039;)-&gt;update([&#039;column&#039; =&gt; &#039;value&#039;]);

//example number two:
$articles = Article::find(1)-&gt;update([&#039;title&#039; =&gt; &#039;something&#039;]);علاوه بر firstOrCreate یک updateOrCreate هم داریم؛ که میاد و اول چک می‌کنه که اگر اون مقدار وجود داشت، آپدیت می‌کنه و اگر نبود؛ ایجاد می‌کنه:عملیات delete با استفاده از ORM:یک مثال ساده...$articles = Article::find(1)-&gt;delete();
$articles = Article::where(&#039;id&#039;, 1)-&gt;delete();دستور دیگری که مشابه با delete هست؛ دستور destroy هست که میاد و اون ریکورد رو برای ما hard delete می‌کنه:$article = Article::destroy(id);به این دستور می‌تونیم یک آرایه از idها هم بدیم.Soft Delete:حذف نرم در برابر حذف Physical یا hard delete قرار داره و اجازه نمیده که دیتا از table حذف بشه و فقط اون رو غیرقابل نمایش می‌کنه.پیاده‌سازی:قبلا با فیدهای updated_at و created_at آشنا شده بودیم؛ اما حالا برای درک این مفهوم باید با فیلدی به نام deleted_at آشنا بشیم که میاد و موقع حذف این فیلد رو با timestamp پر می‌کنه و موقع واکشی دیتا از table، دیگه لاراول اون رو در نظر نمی‌گیره.برای soft delete باید داخل کلاس مدل خودمون SofDeletes رو use کنیم و همین‌طور با ctrl alt i اون namespace رو هم use کنیم:use SoftDeletesخب، پس باید این فیلد رو هم به db اضافه کنیم و nullableش کنیم. حالا اگر یک ریکورد رو حذف کنیم؛ دیگه بهش دسترسی نداریم؛ اما این ریکورد به شکل physical هنوز توی دیتابیس و جدول ما وجود داره.حالا اگر نیاز داشتیم که به اون soft delete شده‌ها هم دسترسی داشته باشیم چی؟ پایین کدش رو گذاشتم:$articcles = Article::withTrashed()-&gt;find(7);
// check if the returned value is soft deleted:
dd($articles-&gt;trashed());حالا متدی داریم به نام onlyTrashed که فقط soft delete شده‌ها رو برای ما برمی‌گردونه، همین‌طور متدی داریم به اسم restore که اون ریکورد soft delete شده رو دوباره به حالت معمول برمی‌گردونه و در واقع مقدار deleted_at اون رو null می‌کنه.$articles = Article::onlyTrashed()-&gt;find(7);
$articles-&gt;restore();
dd($articles);حذف Physical یک ریکورد soft delete شده:$articles = Article::where(&#039;id&#039;, 7)-&gt;forceDelete();مبحث مهم Scope در مدل:خب، وقتی ما یک مدل با php artisan make:model model_name ایجاد می‌کنیم؛ یک سری متد به صورت پیش‌فرض برای کلاس مدل ما که از کلاس Model ارث‌بری کرده وجود داره، حالا شاید نیاز باشه که ما خودمون هم یه سری متد به مدل های خودمون اضافه کنیم؛ این متدها دو نوع دسترسی خواهند داشت؛ دسترسی از نوع global و local، که global روی تمامی کوئری‌ها اجرا میشه و روشون اثر میذاره. خب، برای توضیح این قضیه، باید یک سناریوی فرضی رو در نظر بگیریم؛ فرض کنید یک فیلد داریم به نام status که اگر ۰ باشه، اون کوئری باید فیلتر بشه و نمایش داده نشه و اگر ۱ بود؛ نمایش داده بشه، ما می‌تونیم یک متد با دسترسی global تعریف کنیم که این rule رو روی تمامی کوئری‌های ما اجرا کنه.برای این کار باید داخل کلاس مدل خودمون یک تابع به نام boot از نوع protected static تعریف کنیم که اولش داخلش متد boot کلاس والد رو فرامی‌خونیم و یک متد از نوع global اضافه می کنیم:addGlobalScopeهمون‌طور که در تصویر بالا دیده میشه، هر کوئری که بزنیم؛ یک دور هم این فیلتر روش اعمال میشه و نتیجه برمی‌گرده. حالا برای اینکه ببینیم که وقتی این متد global رو ست کردیم چه بلایی سر کوئری ما میاد! کافیه که کوئری‌ای که زدیم رو با toSql تبدیل کنیم به رشته کوئری SQL و ببینیم که چه خبره.اگر خواستیم یه کوئری بزنیم که از گوبال scope استفاده نکنه، چی؟$articles = Article::withOutGlobalScope(&#039;the_name_of_global_scope&#039;)-&gt;where(&#039;id&#039;, 10)-&gt;get();بریم سراغ Local Scope:نحوهٔ استفاده:$articles = Article::status();چطور به کلاس Model اضافه‌اش کنیم؟public function scopeStatus($query){
    $query-&gt;where(&#039;status&#039;, 1);
}خب، اگر دقت کنیم؛ می‌بینیم که این تابع، اولش یه اسمی داره به نام scope که در واقع یک قانون در لاراول هست برای تعریف توابع Local Scope، اما موقع فراخوانی این متد توی Controller اصلا نیازی نیست که اون قسمت scope رو بنویسیم و خیلی ساده لاراول این رو برای ما هندل می‌کنه و باید اون Status رو هم با حرف کوچیک بنویسیم:$articles = Article::status();خیلی راحت هم می‌تونیم به این متد پارامتر هم ارسال کنیم:public function scopeStatus($query, $parameter_1){     
    $query-&gt;where(&#039;status&#039;, 1); 
}Accessors and Mutators:این موارد به ما کمک می‌کنن تا مقادیر، ویژگی یا به عبارتی attributeهای Eloquent خودمون که در واقع همون فیلدها یا ستون‌های table هستن رو وقت ذخیره‌سازی توی دیتابیس به شکل دلخواه فرمت‌بندی کنیم.در واقع Accessor برای بازیابی دیتا از table هست (get) و Mutator برای نوشتن (insert) دیتا توی دیتابیس.یک مثال ساده از کاربرد این موارد اینه، فرض کنیم که ما می‌خوایم موقع get کردن دیتا، مقادیر timestamp رو به جای اینکه میلادی باشه، تبدیلش کنیم به شمسی، یا مثلا تمامی titleها رو با حروف کوچیک برگردونیم.حالا، چطور باید این قضیه رو پیاده‌سازی کنیم؟یک پیاده‌سازی ساده از یک اکسسور برای دستیابی به title جدول پایگاه داده داخل کلاس Article که همون مدل ما باشه، تعریف می‌کنیم:فرمول نام این Accessor به این شکله که:get + capitalized name of the attribute + Attributepublic function getTitleAttribute(){
    
}اگر اسم ستون باشه first_name چه کنیم؟ باید بنویسیم getFirstNameAttribute. اینا در واقع همون getter و setter هستن که لاراول اون ها رو ساده‌سازی کرده.توی تصویر زیر یک accessor رو می‌بینیم که میاد روی attribute یا همون فیلد یا ستون یه سری عملیات (اینجا lowercase کردن) انجام میده و اینطوری می‌تونیم دیتای خودمون رو فرمت کنیم:Example of an accessorپیاده‌سازی و کاربرد Mutator:فرض کنیم که یک ستون title داریم و می‌خوایم هر چیزی که توی این ستون ذخیره میشه Capitalize باشه، کافیه که یک mutator به صورت زیر تعریف کنید (شبیه setter هست):Mutatorمبحث مهم Migration:ابزاری برای ایجاد و مدیریت جداول پایگاه داده، معمولا توی یک پروژهٔ نرم افزار و با پیشرفت پروژه ممکنه که یه سری جداول جدید نیاز باشه، یا نیاز باشه فیلدهایی به یک جدول اضافه یا کم بشه، بنابراین نیازه که یه ابزاری داشته باشیم تا این کار رو برای ما راحت کنه و این امکان رو برای ما فراهم کنه تا به راحتی به نسخه‌های مختلف جداولی که تعریف کردیم سوییچ کنیم (تاریخچه).توی پوشهٔ روت و داخل پوشهٔ database سه تا پوشه داریم به نام‌های factories migrations و seeders که در ادامه در موردشون بیشتر خواهیم خوند.اگر وارد پوشهٔ migrations بشیم، به صورت پیش‌فرض لاراول ۳ تا migraton برای ما گذاشته که اسم همهٔ اون ها با تاریخ و زمان شروع میشه و بعد نام تخصصی اون فایل که بیانگر کاری هست که می‌کنه. علت آوردن تاریخ و زمان در ابتدای نام هر فایل اینه که از ایجاد اسامی تکراری جلوگیری بشه و دوم اینکه اولویت این فایل‌ها بسیار مهمه.نحوه ایجاد یک migration در لاراول:php artisan make:migration create_[the_name_of_the_table]_table --create=the_name_of_the_tableخب کلاس migration ما که ایجاد شد، داخلش دو تا متد داریم؛ یکی up و دیگری down که up برای ایجاد جدوله و down برای پاک کردن جدول.در تصویر زیر می‌تونیم فایل migration و کلاسی و متد up رو ببینیم که از کلاس پدری Migration اکستند شده:Migration file and class with up methodاون متد timestamp برای created_at و updated_at به کار میرن. که جلوتر در مورد این متدها و نحوهٔ ایجاد فیلدهای دیگه توضیح خواهیم داد.اما روش دیگه ای هم برای ایجاد migration وجود داره و اون هم استفاده از command ساخت مدل هست؛ به این صورت که وقتی داریم یک مدل می‌سازیم؛ یک migration هم برای اون table ایجاد می‌کنیم:php artisan make:model Article -mکه میاد و یه migration هم طبق نامی که بهش دادیم برای ما ایجاد می کنه، مثلا اسمش میشه:2022_05_10_202020_create_articles_tableنحوه ایجاد ستون یا فیلدهای (به قول لاراول attributes) جدول دیتابیس با استفاده از Migration:توی تصویر زیر، متد up رو داریم که توی بدنهٔ تابع، اومدیم و ستون هایی رو تعریف کردیم؛ شی table متدهایی داره که مقادیری رو به عنوان ورودی می‌گیره، در واقع این متدها برمی‌گردن به type های MySQL  و اون پارامتر ورودی هم در واقع نام ستون هست:MySQL data types: bigIncremets and timestampsخب این bigIncrements یعنی چی؟ اگر بریم توی داکیومنت لاراول و بخش Migrations و Columns لیست تمامی typeهای جدول میاد.خب حالا بریم برای اضافه کردن چند تا ستون خوشگل:$table-&gt;string(&#039;title&#039;, 100);
$table-&gt;text(&#039;body&#039;);
$table-&gt;boolean(&#039;status&#039;)-&gt;default(0);توی کد بالا، default در واقع یک column modifier هست. خب، یک نکتهٔ مهم دیگه هم داریم، اونم اینه که توی لاراول برای varchar از string استفاده می‌کنیم چون توی پایگاه‌های داده مختلف دیتا تایپ‌ها متفاوته و مثلا ممکنه توی یک پایگاه داده اصلا چیزی به اسم varchar نباشه یا با اسم متفاوتی ارائه بشه، اما استفاده از string به ما کمک می‌کنه که یک حالت compatibility بین دیتابیس‌های مختلف به وجود بیاریم و حتی اگر بعدا مجبور شدیم دیتابیس رو تغییر بدیم، زحمت زیادی برای تغییر کدهای خودمون نکشیم.خب، خب، خب... حالا که migration رو نوشتیم، باید با کمک atrisan اونو روی دیتابیس هم پیاده‌سازی کنیم:php artisan migrateحالا قبل از اینکه مستیم migrate بزنیم؛ میتونیم از دستور status استفاده کنیم که وضعیت migrationهای ما رو نشون خواهد داد:php artisan migrate:statusاگر حین migrate کردن با error زیر مواجه شدیم؛ باید یک مقداری رو توی متد بوت، AppServiceProvider برای Schema ست کنیم:// Error:
unique key is too long...

// way to solve:

use Illuminate\Database\Schema\Builder;
public function boot()
{
    Builder::defaultStringLength(191);
}وقتی دستور migrate رو به صورت خشک و خالی می‌زنیم؛ تمامی migration ها رو برای ما migrate می‌کنه و یک table به نام migrations ایجاد می‌کنه که توی لیست migrationهای ما نیست. در قسمت پایین می‌تونید یک تصویر از این جدول رو ببینید که سه ستون داره، id migration و batch که دستهٔ migration رو نشون میده:Migrations table in dbحالا منظور از batch چیست؟ همهٔ جدول‌ها یک بچ نامبر دارن، و این به معنی هست که تمامی این جدول‌ها با هم و در یک زمان ایجاد شدند. حالا اگر بیاییم و یک یا چند migration جدید ایجاد و migrate کنیم؛ این‌ها در دسته‌ی ۲ قرار می‌گیرند و الی آخر.حالا این دسته‌بندی‌ها به ما کمک می‌کنه تا عملیات rollback رو با کمترین هزینه و دردسر اعمال کنیم.توی تصویر زیر نمونه‌ای از خروجی status رو می‌بینیم:php artisan migrate:statusمجموعه دستورات migrate مربوط به artisan: migrate
  migrate:fresh          Drop all tables and re-run all migrations
  migrate:install        Create the migration repository
  migrate:refresh        Reset and re-run all migrations
  migrate:reset          Rollback all database migrations
  migrate:rollback       Rollback the last database migration
  migrate:status         Show the status of each migrationحالا اگر یک مدل و migration جدید ایجاد کنیم و status بگیریم؛ با این جدول روبرو می‌شیم:حالا اگر migrate بزنیم؛ تنها همین جدولی که migrate نشده؛ migrate خواهد شد و وضعیت Ran? به Yes تغییر پیدا می‌کنه و همین‌طور Batch number هم برابر با ۲ خواهد شد.نحوهٔ عملکرد دستور rollback به این صورت هست که میاد و آخرین batch number موجود در جدول migrations رو پیدا می‌کنه و اون(ها) رو drop می‌کنه. مثلا ما تصمیم گرفتیم که یک فیلدی رو به جدول posts خودمون اضافه کنیم؛ حالا کافیه که یه rollback بریم تا جدول توی دیتابیس drop بشه، بعدش تغییرات رو داخل فایل migration ایجاد کردیم؛ یک migrate می‌زنیم و جدول دوباره ایجاد میشه.بعد از دستوراتی مثل refresh fresh reset کل دسته‌بندی‌های یا batch ها حذف میشن و تغییر می‌کنن.برای ایجاد تغییرات روی جداول چند راه وجود داره، مثل rollback و یا reset و... که مشکلشون اینه که کل table رو drop می‌کنن و به خصوص روی سرور پروداکشن دردسرزا هستن، اما با استفاده از خود migration هم میشه این کار رو کرد که در ادامه بررسی خواهیم کرد:برای جدولی که توی دیتابیس داریم و قراره که با استفاده از migration یک ستون جدید بهش اضافه کنیم بدون اینکه مجبور به استفاده از rollback...migrate و دستورات مشابه  که باعث drop شدن و بهم ریختگی batchها میشن می‌تونیم از --table مربوط به دستور make:migrate استفاده کنیم:php artisan make:migration add_new_field_to_articles_table --table=articlesخب، حالا یک migration جدید ساخته میشه که به جای Schema::create از Schema::table استفاده می‌کنه:How to add new columns to a exist tableاما باید متد down این migration رو هم اوکی کنیم:چند تا نکتهٔ ریز و مهم اینجا وجود داره، اول اینکه توی متد up که میاییم و ستون های جدید رو تعریف می‌کنیم؛ حتما باید اون ستون ها یا nullable باشن و یا اینکه یک مقدار default براشون تعریف کنیم؛ وگرنه دچار مشکل میشیم و دوم اینکه برای دراپ کردن در متد down به جای تعریف چندین دستور dropColumn کافیه که یک آرایه از ستون‌ها رو بهش بدیم.حالا به راحتی می‌تونیم rollback بزنیم و فقط همون یک ستونی که اضافه کردیم رو drop کنیم؛ اما برای modification چطور؟ یعنی مثلا مقدار varchar که همون string باشه رو از ۲۰۰ به ۱۰۰ برسونیم و یا حرکاتی مثل این! برای این کار باید دقیقا مثل روش اضافه کردن ستون یک migration جدید ایجاد کنیم؛ و حالا یک اسمی هم برای migration قرار میدیم که نشون بده قراره توش یک سری column رو تغییر بدیم:php artisan make:migration change_column_from_articles_table --table=articlesحالا محتویات فایل ما اینطوری میشه:توی متد up در واقع اومدیم و مقدار string ستون title رو از ۱۰۰ به ۱۵۰ افزایش دادیم و اسم ستون status رو به is_status تغییر دادیم. و برعکس این عملیات رو توی down پیاده‌سازی کردیم.حالا اگر migrate کنیم؛ ممکنه که با error مواجه بشیم که میگه کتابخونهٔ doctrine/dbal نصب نیست؛ که در واقع یک کتابخونه‌ای هست که برای chnage کردن ستون‌ها مورد استفاده قرار می‌گیره.برای نصب کتابخونه کافیه که از composer کمک بگیریم:composer require doctrine/dbalمبحث Seeders:از سیدر برای تولید دیتای فیک و ایجاد ریکوردهای فیک به صورت اتوماتیک استفاده میشه. برای این کار اول از همه باید ببینیم که این seederها کجا قرار می‌گیرن، توی پوشهٔ database گفتیم که ۳ تا پوشه داریم به اسم های seeds factories و migrations که مشخصه seeders توی seeds قرار می‌گیره.توی پوشهٔ seeds یک فایل seeder دیفالت به اسم DatabaseSeeder.php داریم که یک متد داره به اسم run که با استفاده از متد run میاد و seederهای مختلفی که ما تعریف می‌کنیم رو ران می‌کنه.حالا چطوری یک seeder برای جدول خودمون بسازیم؟ خیلی ساده با استفاده از artisan میشه این کار رو کرد:php artisan make:seeder ArticlesTableSeederکه حالا جای Articles می‌تونیم هر جدولی که مد نظره رو بذاریم (بدیهیات!) این seederی که ایجاد شده یک متد run داره و توی این ران میاییم و دیتا رو به جدول تزریق می‌کنیم؛ وقتی که این seeder کال شد، دیتا توی جدول می‌شینه:توی روش بالا، وقتی دیتا رو با create داریم وارد دیتابیس می‌کنیم؛ در واقع داریم از روش mass assign استفاده می‌کنیم؛ بنابراین باید که متغیر fillable رو توی مدل خودمون که اینجا Article باشه، تعریف کنیم:حالا چطوری seeder خودمون رو فراخوانی کنیم؟ باید این seeder رو توی DarabaseSeeder.php کال کنیم:علت این داستان اینه که db:seed به صورت دیفالت، فایل DatabaseSeeder رو فراخوانی می‌کنه، اگر بخوایم که فقط همون seeder رو ران کنیم؛ باید از آپشن --class استفاده کنیم:php artisan db:seed --class=ArticleTableSeederباید دقت داشته باشیم که اگر چندین seeder داشته باشیم؛ کافیه که یک آرایه از seederها رو به متد call بدیم.حالا اگر بخوایم یه سری دیتای واقعی‌تر و ملموس‌تر وارد table کنیم؛ چیکار باید کرد؟یک کتابخونه وجود داره به اسم Faker که به صورت دیفالت همراه با Laravel نصب میشه، حتی Fakerهای ایرانی هم وجود داره. حالا چطور ازش استفاده کنیم؟یه نکتهٔ جالب که وجود داره اینه که دستور زیر، که برای drop کردن کلی tableها کاربرد داره، یه آپشن --seed هم می‌گیره و بعد از پاک کردن جدول‌ها، اون‌ها رو سید هم می‌کنه و نیازی نیست که دستور seed رو جداگانه بزنیم:php artisan migrate:fresh --seedپایان قسمت ۶۰</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Wed, 06 Jul 2022 01:10:16 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت دوم</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%AF%D9%88%D9%85-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-bjrtt5c6xhvn</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.(هر پارت شامل ۲۰ قسمت)اینجا قراره که دو تا directive جدید به نام‌های section و show رو یاد بگیریم. با مثال توضیح داده میشه:@section(&#039;something&#039;)
Some Default Value...
@showاین directive به جای yield توی master layout میشینه و یک مقدار Default رو برای اون بخش تعیین می‌کنه، بنابراین اگر توی یک صفحه‌ای master layout رو extend کردیم؛ و مقداری برای اون section در نظر نگرفتیم؛ مقدار default که توی master تعریف شده، میاد و جاش می‌شینه.حالا می‌ریم سراغ Component و Slot:در واقع Componentها خیلی شبیه به Partials کار می‌کنند؛ حالا این Partials چی هستن؟همون پوشه ای که اومدیم و قسمت‌های مختلف master layout رو توش جداسازی کردیم؛ یعنی فوتر و هدر و سایدبار و نو و... .ما می‌تونیم موارد مختلف رو تبدیل کنیم به یک component، مثلا یک کامپوننت برای دکمه‌ها و...چطوری میشه این کار رو کرد؟برای دسته‌بندی و نظم دادن به کار، کافیه یک پوشه به اسم components توی Views ایجاد کنیم؛ و یک فایل مثلا button.blade.php ایجاد می‌کنیم. این محتویات این فایل فرضی می‌تونه باشه:&lt;button class=&amp;quot{{ $class }}&amp;quot&gt;
    {{ $title }}
&lt;/button&gt;حالا می‌تونیم از این کامپوننت هرجایی استفاده کنیم، مثلا توی about یا index و... از syntax زیر برای به کار گیری کامپوننت و پاس دادن آرگومان‌ها استفاده میشه:@component(&#x27;components.button&#x27;, [&#x27;title&#x27; =&gt; &#x27;something&#x27;, &#x27;class&#x27; =&gt; &#x27;btn btn-primary&#x27;])@endcomponentحالا اگر از یک کامپوننت استفاده کنیم و یک آرگومان رو ارسال نکنیم؛ با خطا روبرو می شیم؛ اما کافیه که با php directive چک کنیم که اصلا اون متغیر خاص ست شده یا نه، و اگر ست نشده، براش یه مقدار default در نظر بگیریم:به جای ارسال آرگومان‌ها با استفاده از یک آرایهٔ associative کافیه که از slotها استفاده کنیم. اسلات یک پارامتر داره که در واقع همون اسم متغیری هست که قراره مقداردهی و توی کامپوننت بارگذاری بشه، مقداری که بین directive اسلات قرار می‌گیره در واقع مقدار اون متغیر خواهد بود:@slot(&#039;name_of_variable&#039;)
value of variable
@endslotاگر بدون اینکه متغیری رو به کامپوننت بفرستیم (چه با اسلات و چه با آرایه) و فقط یک قطعه کد یا متن رو بین component directive قرار بدیم؛ می‌تونیم به اون از طریق متغیر $slot دسترسی داشته باشیم.ساخت کامپوننت سفارشی در لاراول:...ساخت یک directive سفارشی:فرض کنید می‌خوایم یک directive طراحی کنیم که قراره یک نوشته رو bold کنه، پس باید دایرکتیو ما یک چنین چیزی باشه:@bold(text blah blah)برای این کار باید یک Service Provider طراحی کنیم که در مسیر app\providers\AppServiceProvider.php قرار داره، این یک Service Provider پیش‌فرض هست که از طرف خود لاراول ارائه شده و برای اینکه ما سرویس‌های سفارشی خودمون رو توش بنویسیم:&lt;?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    } حالا توش متد بوت از قطعه کد زیر استفاده می‌کنیم:Blade::directive(&#039;name_of_directive&#039;, function($directive_argument){
    return &#039;something&#039;;
});برای مثال، bold directive ما یک چنین چیزی میشه:Bold Directiveحالا کافیه که توی blade template از کد زیر برای دسترسی به directive سفارشی خودمون استفاده کنیم؛ اصلا نیاز هم نیست که آرگومان رو بین دابل یا سینگل کوت قرار بدیم.@bold(Argument)حالا بریم سراغ نوشتن conditional directives:Blade::if(&#039;isint&#039;, function($value=null){
    return is_int($value);
});نحوهٔ استفاده از Directiveهای شرطی:@isint(num)
    do_something
@else
    do_something_else
@endifفایل‌های استاتیک و استفاده از آن‌ها در لاراول:منظور از فایل‌های Static چیزهایی مثل javascript و CSS و... هست که توی پوشهٔ public قرار خواهند گرفت. در نهایت وقتی پروژه به اتمام رسید؛ فایل‌های این پوشه رو توی پوشهٔ public_html روی سرور/هاست آپلود می‌کنیم تا بهش دسترسی داشته باشیم. بهتره که محتویات پوشهٔ public ما دسته‌بندی شده و مرتب باشه، برای این کار باید اون رو پوشه‌بندی کنیم؛ مثلا پوشه‌هایی به نام‌های css و js و... ایجاد کنیم و فایل‌های css و اسکریپت‌هامون رو توی اون ها قرار بدیم که البته سلیقه‌ای هست.حالا برای استفاده از این فایل‌ها داخل پوشهٔ resources\views و فایل‌های blade کافیه که داخل head بهشون link بدیم و نکتهٔ مهم اینکه نیازی نیست که آدرس دقیق بدیم و آدرس نسبت به پوشهٔ public داده میشه، مثلا اینطوری:&lt;link rel=&amp;quotstylesheet&amp;quot href=&amp;quot/css/style.css&amp;quot&gt;علت این مسردهی نسبی هم مربوط به این میشه که فایل‌های قالب blade بعد از کامپایل/ترجمه شدن، داخل index.php که توی public هست لود میشن، بنابراین از نظر منطقی به پوشه‌های داخل اون به‌طور نسبی دسترسی دارن.یک helper function داریم به اسم asset که می‌تونیم به فایل های موجود در resource اشاره کنیم. یعنی به این شکل:&lt;link rel=&amp;quotstylesheet&amp;quot link=&amp;quot{{ asset(&#039;/css/style.css&#039;) }}&amp;quot&gt;آشنایی با پوشهٔ Resource و فایل‌های static و SASS و همین‌طور Laravel Mix:بحث Compiling Assets یا Mix که مربوط به بخش فرانت پروژه میشه. تکلیف فایل‌های static که مشخصه، اما اگر قرار باشه که از SASS یا LESS یا کتابخونه‌های مختلف جاوا اسکریپتی مثل Vue استفاده کنیم؛ قبل از اینکه کاربر بتونه خروجی رو ببینه، این‌ها باید کامپایل بشن تا بتونیم خروجی رو ببینیم.ابزار Mix لاراول کار compiling رو به راحتی برای ما انجام میده، برای استفاده از این ابزار باید با package.json به خوبی آشنا بشیم. npm یک پکیج منجر برای javascript هست که ما می‌تونیم این پکیج‌ها رو با استفاده از package.json مدیریت کنیم.داخل پوشهٔ روت یک برنامهٔ لاراولی یک فایل داریم به اسم webpack.mix.js که به صورت دیفالت موجوده و داخل این فایل میاد و laravel-mix رو require می‌کنه و با استفاده از دستوراتی که این کتابخونه برای ما فراهم می‌کنه، کارهای مختلفی انجام میده، مثلا میاد و فایل resources/js/app.js رو کامپایل می‌کنه و خروجی رو داخل public/js قرار می‌ده.برای نصب پکیج‌های package.js باید node.js رو نصب کنیم و با دستور زیر پکیج‌ها رو نصب کنیم:npm install حالا برای کامپایل کردن و خروجی گرفتن از کانفیگ های webpack.mix.js باید از کد زیر استفاده کنیم:npm run devاین dev مربوط میشه به راهنمایی که داخل خود webpack اومده و دستورات دیگه‌ای هم داریم مثل production و watch و... .مبحث Javascript and CSS Scaffolding:منظور از Scaffolding اسکلت‌بندی است. منظور اینه که ما برای طراحی فرانت و اسکلت کارمون به ابزارها و کتابخونه‌هایی مثل bootstrap، ویو، ری‌اکت و... نیاز داریم و برای برای استفاده از scaffolding اول باید با دستور زیر، پکیج laravel/ui رو نصب کنیم:composer require laravel/ui --devاستفاده از این بسته هم خیلی راحته.با دستورات زیر، بوت‌استرپ، ویو یا ری‌اکت رو دانلود می‌کنه و با laravel mix ترکیب می‌کنه و می‌تونیم توی viewهای خودمون ازش استفاده کنیم:php artisan ui bootstrap
php artisan ui vue
php artisan ui reactو حتی سیستم authentication هم توی لاراول به شکل دیفالت وجود داره و با استفاده از scaffolding به راحتی می‌تونیم ui کار رو آماده کنیم:php artisan ui bootstrap --auth
php artisan ui vue --auth
php artisan react --authشروع مبحث Routing:فساد Route دارای چند تا متد هست که در قسمت پایین اون‌ها رو توضیح می‌دیم:get: برای خوندن، مثلا نمایش لیست محصول(ات).post: برای ایجاد کردن استفاده میشه، مثلا ایجاد یک محصول جدیدput: برای بروزرسانی هست؛ یعنی یک record رو پاک می‌کنه و اطلاعات جدید به جاش قرار میدهpatch: فقط فیلدهای دارای مقدار رو تغییر میدهdelete: حذف یک ریکورد در پایگاه دادهany: باعث رد شدن هر چیزی میشه و می‌تونیم توی Controller مدیریتش کنیم.با استفاده از artisan می‌تونیم لیست روت‌های موجود رو ببینیم؛ برای این کار کافیه که دستور زیر رو بزنیم:php artisan route:listدریافت اطلاعات از URL:Route::get(&#039;/page/{name_of_parameter}&#039;, function($name_of_parameter) {
    return &#039;the parameter is: &#039;.$name_of_parameter;
});پارامترهای اختیاری:در ورودی اول تابع get که متغیرها را تعریف می‌کنیم باید انتهای نام متغیر یک علامت سوال قرار دهیم و در قسمت دوم که مربوط به تابع بی‌نام ماست، برای آن متغیر باید یک مقدار default تعریف کنیم.آشنایی با Route Name:برای لینک دادن به یک صفحه می‌تونیم خیلی راحت لینکش رو توی اون view خاص وارد کنیم؛ اما از اونجایی که مسیردهی ممکنه کمی سخت باشه، کافیه که برای هر Route یک name ست کنیم و از اون name برای دسترسی به هر روتی که خواستیم استفاده کنیم:Route::get(&#039;/page/{name_of_parameter}&#039;, function($name_of_parameter) {     
    return &#039;the parameter is: &#039;.$name_of_parameter; 
})-&gt;name(&#039;A_Name_For_This_Route&#039;);حالا خیلی راحت می‌تونیم توی view بهش لینک بدیم:&lt;a href=&amp;quot/page&amp;quot&gt;GO TO PAGE&lt;/a&gt;
&lt;a href=&amp;quot{{ url(&#039;/page&#039;) }}&amp;quot&gt;GO TO PAGE&lt;/a&gt;
&lt;a href=&amp;quot{{ route(&#039;route name&#039;) }}&amp;quot&gt;GO TO PAGE&lt;/a&gt;اگر Route ما ورودی دریافت کنه، می‌تونیم توی لینک، این ورودی‌ها رو بهش پاس بدیم:&lt;a href=&amp;quot{{ route(&#039;course&#039;, [&#039;course&#039; =&gt; &#039;Laravel&#039;, &#039;id&#039; =&gt; &#039;65&#039;]) }}&amp;quot&gt;GO TO PAGE&lt;/a&gt;مبحث Route Group:فرض کنیم که یک سری Route مشابه داریم؛ ما با استفاده از Route Group Prefixed Route Groupاستفاده از آرایه به جای متد prefix:Associative array instead of prefix methodمبحث کنترلر در لاراول:برای تعریف یک کنترلر جدید باید به مسیر زیر بریم:app/http/Controllers/Controller.phpداخل این فایل یک کلاس Controller داریم که از یک کلاس دیگه‌ای به نام BaseController مشتق شده.کنترلرهای ما باید داخل این پوشه باشند و از کلاس Controller مشتق بشن. برای ایجاد یک کنترلر جدید می‌تونیم از artisan استفاده کنیم:php artisan make:controller AdminControllerحالا محتویات کنترلر ما اینطوریه:&lt;?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class AdminController extends Controller
{
    //
}** نکته: برای اینکه ببینیم که هر دستور artisan چه آرگومان‌هایی داره و... می‌تونیم از help با دابل دش استفاده کنیم.** نکته: همیشه باید سعی کنیم که آخرین نسخه از nodejs استفاده کنیم، برای این کار بهتره که نود رو از داخل سایت خودش دانلود کنیم و مسیر فایل اجرایی رو به PATH اضافه کنیم.حالا ما می‌تونیم داخل کلاس AdminController متدهای مختلفی رو ایجاد کنیم که در اصلاح لاراولی بهشون Action هم گفته میشه، برای اینکه بتونیم از Route کنترلر و یک اکشن از اون کنترلر رو فراخوانی کنیم؛ باید از روش زیر استفاده کنیم؛ و اینکه کنترلر میاد و با مدل ارتباط برقرار می‌کنه و مدل هم با DB و نهایتا مدل دیتا رو به کنترلر می‌فرسته و کنترلر به View.تعریف یک اکشن:class AdminController extends Controller {

    public function hello_world(){
       return view(&#039;admin&#039;);
    }
}و توی روت:Route::get(&#039;/admin&#039;, &#039;AdminController@hello_world&#039;)-&gt;name(&#039;admin&#039;);ما می‌تونیم Controllerهای خودمون رو به راحتی پوشه‌بندی کنیم؛ برای این کار می‌تونیم مثلا:php artisan make:controller Admin\AdminControllerمبحث resource controller:فرض کنیم که ما یک Route داریم به اسم Article، حالا برای عملیات مختلفی که قراره با این articles انجام بدیم؛ باید روت‌های مختلفی بنویسیم و برای هر روت یک اکشن مناسب توی controller بنویسیم؛ به جای نوشتن دستی هر اکشن، کافیه که از resource controller استفاده کنیم:php artisan make:controller ArticleController -rRoutes for articlesحالا دستور artisan برای ما کنترلر به همراه متدهای اون رو ساخته.باز هم می تونیم عملیات بالا رو ساده‌تر کنیم؛ به جای نوشتن تک تک روت‌ها، کافیه که از روت زیر استفاده کنیم؛ خود تمامی اکشن‌های کنترلر رو برای ما می‌سازه:Route:resource(&#039;articles&#039;, &#039;ArticleController&#039;);حالا اگر یک Route list بگیریم، می‌بینیم که خود لاراول برای ما اتوماتیک اون روت‌ها رو جنریت می‌کنه:php artisan route:listحالا گاهی پیش میاد که ما نخوایم یک سری روت برای ما جنریت بشه، یا فقط یک سری روت خاص رو بخوایم؛ برای این کار:Route::resource(&#039;articles&#039;, &#039;ArticleController&#039;)-&gt;only([&#039;index&#039;, &#039;show&#039;]);
Route::resource(&#039;articles&#039;, &#039;ArticleController&#039;)-&gt;except([&#039;index&#039;, &#039;show&#039;]);مبحث Request یا درخواست در لاراولدر تصویر زیر یک کنترلر به نام HomeController رو می‌بینیم که داخلش Request مورد استفاده (use) قرار گرفته، حالا می‌تونیم این Request رو به عنوان پارامتر متد home داخل کلاس کنترلر دریافت کنیم.حالا این Request کجا کاربرد داره؟ اینجا بحث DI یا Dependency Injection مطرحه، Service Container میاد Request رو می‌گیره و با توجه به نیازهایی که داره یک شی ازش ایجاد می‌کنه (کلاس Request طبیعتا یک سری ورودی و آرگومان داره، اینجا Service Container این وابستگی‌های رو بهش میده) و توی $request قرار میده. حالا می‌تونیم ببینم که محتویات این شی request چیه، کافیه که با dd helper function اون رو ببینیم.استفاده از Request در متدهای کنترلراینجا نمونه‌ای از محتویات $request که dd شده رو می‌تونیم ببینیم:محتویات شی $requestشی $request یک متد داره به اسم all که برای ما محتویات مقادیر ارسالی با get رو به نمایش می‌ذاره، توی تصویر زیر این رو می‌تونیم ببینیم:خروجی کد بالا:حالا اگر قرار باشه که مقادیری رو از طریق Route دریافت کنیم؛ باید به ترتیب این تغییرات رو در Route و Controller ایجاد کنیم:دریافت مقدار id از طریق Routeنحوه دریافت $id از طریق کنترلرنمایش خروجی کدهای بالاتا اینجای کار، مقادیری که از طریق $request هندل کردیم؛ از طریق get دریافت شده بودند؛ حالا توی این بخش می‌خوایم ببینیم که اگر مقادیر رو از POST یا فرم‌های ارسال کنیم؛ چطور باید مقادیر رو از طریق شی Request هندل کنیم.هندل کردن مقادیر ارسالی با POST از طریق شی $request در کنترلر:ما می‌خوایم که ui پروژه خودمون رو با بوت استرپ ایجاد کنیم، برای این کار از دستورات artisan کمک می‌گیریم:php artisan ui bootstrapحالا دستور زی رو می‌زنیم:npm install &amp;&amp; npm run devبعد از اجرای دستور npm install پکیج‌های موجود در package.json توی یک پوشه به اسم node_modules نصب میشن و بعد می‌تونیم از این پکیج‌ها استفاده کنیم. حالا با زدن دستور npm run dev پکیج‌های مورد نیاز ما کامپایل می‌شن و توی public قرار می‌گیرن و ما می‌تونیم به راحتی به اون‌ها دسترسی داشته باشیم؛ مثلا بوت استرپ داخل پوشهٔ css و فایل app.css قرار گرفته.** یادآوری فرآیند MVC:ما یه سری Route داریم که در واقع مسیر دسترسی ما به قسمت‌های مختلف سایت رو مشخص می‌کنه، حالا این Route می‌تونه مستقیما یه مقداری رو توی view نمایش بده (با فراخوانی view helper function) و یا اینکه اگر بخوایم دقیقا مثل مدل MVC عمل کنیم؛ روت ما یک متد از یک Controller رو فراخوانی می‌کنه و حالا می‌تونه یک View رو نمایش بده و یا اینکه کار دیگه‌ای بکنه...** نکتهٔ مهم:این آموزش (حداقل تا اینجای کار) بر مبنای نسخه‌های قبل از ۸ هست؛ بنابراین باید توجه داشته باشیم که نحوهٔ دسترسی به کنترلرها از Route تغییر کرده و به این شکله:Route::get(&#039;/page&#039;, [\App\Http\Controllers\YourController::class,&#039;your_method&#039;]); اگر یک فرم رو با POST ارسال کنیم (Route هم باید post باشد)، و میان‌افزار VerifyCsrfToken فعال باشه، با خطای ... روبرو می‌شیم.توی فایل app/Http/Kernel.php یک سری Middleware قرار داره که این‌ها، روی Routeهای داخل web یا api و... اجرا می‌شن. یکی از مهم‌ترین Middlewareها، VerifyCsrfToken هست که اگر ما بیاییم و غیرفعالش کنیم؛ اطلاعات فرم ما به راحتی با POST از مجرای post Route عبور می‌کنند و با خطا رو برو نمی‌شیم. در واقع این Middlewareها یک سری میان‌افزار هستن که درخواست‌های ارسالی ما به یک Route رو بررسی می‌کنن و اگر OK بود به Controller پاس می‌دن. در واقع باید از فیلتر این میان‌افزارها عبور کنند.حالا این CSRF Token چی هست؟این عبارت کوتاه شدهٔ Cross-site Request Forgery هست؛ حالا معنیش چیه؟ «درخواست جعلی از سایتی دیگر» که توی لینک بالا کامل توضیح داده شده، راه حل این مشکل استفاده از توکن‌های auto-generateای هست که هر کاربر لاگین شده تعلق می‌گیره و جلوی این رو می گیره که هکر از یک سایت میانی از session فعال ما استفاده کنه.متدهای نوع POST DELETE PUT نیاز به این توکن دارن. حالا چطوری توی یک فرم این توکن رو فراخوانی کنیم؟{{ csrf_token }}استفاده از helper function توکن CSRFبه جای استفاده از csrf helper function ما می‌تونیم از دایرکتیو زیر هم استفاده کنیم:@csrfآشنایی بیشتر با آپشن‌های شی request:برای دسترسی به value یک key از شی request باید از متد get استفاده کنیم:$value = $request-&gt;get(&#039;key&#039;);علاوه بر متد get می‌تونیم از setter و getter هم برای دسترسی به مقادیر شی req استفاده کنیم:$value = $request-&gt;key;اما بین دو روش بالا تفاوت‌هایی هم وجود داره، مثلا اگر در هر یک از روش‌های بالا یک key غیر معتبر وارد کنیم؛ برنامه مقدار null رو برای ما برمی‌گردونه، اما متد get این قابلیت رو هم داره که یک مقدار default هم براش تعریف کنیم تا اگر یک invalid key وارد کردیم، به جای مقدار null برای ما اون مقدار default رو برگردونه:$value = $request-&gt;get(&#039;key&#039;, &#039;default_value&#039;);یک متد کاربردی دیگه از شی request، متد has هست که میاد چک می کنه که آیا اصلا اون key وجود داره یا خیر!// returns true or false
$value = $request-&gt;has(&#039;key&#039;)متد کاربردی بعدی، متد only هست که فقط اون مقادیری که ما نیاز داریم رو برمی‌گردونه:$values = $request-&gt;only([&#039;key0&#039;, &#039;key1&#039;]);متد کاربردی بعدی، متد except هست که همهٔ مقادیر به جز اون‌هایی که ازش نام بردیم رو برمی‌گردونه:$values = $request-&gt;except([&#039;key0&#039;, &#039;key1&#039;]);متد query میاد و طبق query string مقادیر مورد نظر رو برمی‌گردونه:localhost:8000/page?id=20
$value = $request-&gt;query(&#039;key&#039;, &#039;default_value&#039;);اما باید دقت داشته باشیم که حتما Route و فرم ما از متد get استفاده کنه.اما چطوری می‌تونیم به پارامترهای URI تعریف شده در Route دسترسی داشته باشیم؟// routes/web.php
Route::get(&#039;/home/{val0}/{val1}&#039;, [app\Http\Controllers\HomeController::class, &#039;method&#039;])-&gt;name(&#039;home&#039;);

// * app/Http/Controllers/HomeController.php
public function method(Request $request, $val0, $val1){
    dd($data-&gt;all());
    dd($val0);

// another way to access URI parameters
$value = $request-&gt;route()-&gt;parameter(&#039;val0&#039;);
dd($value);استفاده از روشی که با * مشخص شده به روش دوم برتری داره.فایل‌ها در لاراولآپلود فایلروش‌های دسترسی به فایل‌ها در کنترلر، با استفاده از شی request و متدهای کاربردی آن:متدهای مربوط به دسترسی به فایل از طریق شی requestایجاد Requestهای سفارشی:برای این کار می‌تونیم از artisan کمک بگیریم:php artisan make:request NameRequestدر حالت Default ما داخل app/Http، موارد زیر رو داریم:Controllers
Middlewares
Kernel.phpاما با اجرای دستور ساخت یک request جدید، یک پوشه به اسم Requests هم ایجاد میشه که requestهای ما داخل اون قرار می‌گیره:Controllers
Middlewares
Requests
Kernel.phpنمونه ای از محتویات یک Request به نام LoginRequest:&lt;?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class LoginRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return false;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}حالا کافیه که به جای Request بذاریم LoginRequest و در ابتدای فایل use کنیم؛ یک میانبر برای vscode وجود داره که خط use رو اضافه می‌کنه:Ctrl + Shift + iحالا Service Container یک شی از LoginRequest ایجاد می‌کنه و توی شی $request قرار میده. این Request شخصی‌سازی شده تمامی امکانات Request اولیه رو داره به اضافهٔ Authorize و Rules که اولی برای تعیین سطح دسترسی هست؛ اگر authorize مقدار false رو برگردونه، به ارور 403 برخورد می‌کنیم و اجازه دسترسی به اون action رو از ما می‌گیره.و rules مربوط به قوانینی هست که روی پارامترها قرار می‌دیم؛ ما باید صحت و درستی درخواست‌های کاربر رو Validation می‌کنیم؛ مثلا آیا فایلی که کاربر داره می‌فرسته، حتما باید متنی باشه، یا فلان فیلد ۱۰ کاراکتر باشه و...مبحث Validation:اعتبارسنجی حتما باید در سمت سرور و کلاینت اجرا بشه. خود لاراول یک سری قوانین از پیش تعریف شده داره که می‌تونیم اون‌ها رو توی بخش The Basics و Validation و نهایتا Validation Rules ببینیم.یک مثال:نمونه ای از Validation در Laravelخب، ما اینجا اومدیم و توی متد loginUser کنترلر خودمون، مقادیری که از طریق فرم submit میشن رو Validate می‌کنیم؛ شی request ما یک متد داره به اسم validate که یک آرایهٔ Associative دریافت می‌کنه که توش Ruleها تعریف شده، مثلا اینجا ما می‌دونیم که یک ورودی داریم به نام email، پس براش یک rule ست کردیم که این فیلد اجباریه، حالا اگر کاربر، این مقدار رو نفرسته، دوباره redirect میشه به همون view و اگر اوکی باشه مقدار Request Validated برگشت داده میشه.وقتی که این validation انجام میشه یه flash session ایجاد میشه که فقط یک بار نشون داده میشه و توی $errors ذخیره میشه و حالا کافیه که مقادیر موجود در اون رو بخونیم:dd($errors-&gt;all());اینطوری می‌تونیم این ارورها رو بخونیم.نمایش errorهای validationچک و نمایش error مربوط به یک فیلد خاصمثال بالا، با این تفاوت که از Directive استفاده شده و کار ما راحت شده، اون message هم مربوط به خود دایرکتیوه*** نکتهٔ مهم: اینجا ما Validation رو توی Controller انجام دادیم؛ حالا کافیه که این Validation رو منتقل کنیم به Request سفارشی خودمون:اعتبارسنجی در rulesپایان قسمت ۴۰</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Wed, 29 Jun 2022 02:39:17 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی لاراول - پارت اول</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%A7%D9%88%D9%84-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-ngxutkoavnom</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.(هر پارت شامل ۲۰ قسمت)آشنایی با چند اصطلاح مهم:شباهت و تفاوت Library و Framework:هر دو یک سری کدهای از پیش نوشته شده هستن که می‌تونیم به راحتی (گاهی هم به سختی :)) از اون‌ها بارها و بارها توی برنامه‌های خودمون استفاده کنیم. در واقع استفاده از فریمورک و کتابخونه چند تا هدف اصلی و مهم داره، اول اینکه از اختراع دوباره چرخ جلوگیری می‌کنه و در نتیجه در زمان خیلی صرفه‌جویی میشه و این خودش از صرف هزینهٔ اضافی جلوگیری می‌کنه. از طرفی اگر قرار باشه ما یک سیستمی (از یک تابع ساده تا یک کلاس پیچیده) رو خودمون طراحی و ساماندهی کنیم؛ طبیعتا ممکنه کلی باگ و یا exception وجود داشته باشه که به ذهنمون نرسیده هندلش کنیم؛ اما توی کتابخونه‌ها و فریمورک‌ها این مشکلات توی آپدیت‌های مداوم حل شدن.کتابخونه یا library چیه؟مجموعه‌ای از کلاس‌ها و متدها که ما بسته به نیاز خودمون اون‌ها رو به برنامه‌ای که کنترلش کاملا دست خودمونه ایمپورت می‌کنیم. نکتهٔ مهم در اینجا اینه که جریان برنامه دست برنامه‌نویسه و وقتی مثلا نیاز پیدا کرد که یک تابع خاص رو استفاده کنه، اون رو به برنامه اضافه می‌کنه، در واقع این کتابخونه نیست که برای ما تصمیم می‌گیره، این ما هستیم که تصمیم می‌گیریم.فریمورک (framework) چیه؟مجموعه‌ای از کتابخونه‌ها و دارای یک ساختار و اسکلت از پیش تعیین شده که برنامه‌نویس باید از اصول و قواعد اون اسکلت و چارچوب پیروی کنه و به نوعی این فریمورک هست که به برنامه‌نویس جریان رو نشون میده و کنترل جریان برنامه دست اونه، مثل یک کتاب که بعضی از کلمات یا صفحاتش خالی هست و ما باید طبق نیاز خودمون اون‌ها رو پر کنیم. بنابراین تمامی کسانی که این کتاب با جای خالی رو کامل می‌کنن، از یک چارچوب و اسکلت مشترک پیروی می‌کنن، اما اهداف خاصی رو دنبال می‌کنن، بنابراین وقتی این کتاب بین افرادی که اون رو می‌فهمن می‌چرخه، درک قسمت های مختلفش خیلی راحته و به راحتی می تونن اون رو اصلاح کنن یا توسعه بدن.نکات تکمیلی:۱- هر موقع که نیاز داشته باشیم می‌تونیم یک library و محتویاتش رو کال کنیم.۲- اما اگر قراره از یک فریمورک برای توسعه برنامه استفاده کنیم از همون ابتدا باید پروژه رو با اون استارت بزنیم.۳- ما ممکنه از هزار تا کتابخونه توی برنامه خودمون استفاده کنیم، اما برای توسعه برنامه تنها مجاز به استفاده از یک فریمورک هستیم.۴- فریمورک معمولا از کتابخونه پیچیده‌ترهframework vs libraryفریمورک به ما کمک می‌کنه که کارهای روتین و تکراری رو skip کنیم و تمرکز رو بذاریم روی کار اصلی که قراره بکنیم. مثلا اکثر پروژه‌های نرم‌افزاری چیزهای مثل اتصال به db رو دارن و اگر قرار باشه که ما برای هر پروژه اون ها رو توسعه بدیم یا از جای دیگه ای کپی کنیم؛ کمی زمان‌بره و همین‌طور در آخر از یک قالب استاندارد تبعیت نمی‌کنه و کدها نهایتا messy میشن، حتی ممکنه از نظر امنیتی هم اشکالاتی در کدهای ما باشه.ولی فریمورک به کدهای ما نظم میده، چون داره یک ساختار رو به ما تحمیل می‌کنه و این تحمیل رو نباید به معنی بدش تفسیر کرد؛ بلکه مثل قوانین راهنمایی و رانندگی هستن که با اجبار راننده‌ها به تبعیت از یک سری اصول خاص از ایجاد بی‌نظمی در شهر و جاده جلوگیری می‌کنن.الگوی طراحی MVC:مخفف Model View Controller هست که یک الگوی طراحی معماری نرم‌افزاره.حالا Design Pattern چیه؟راه حلی استاندارد برای حل کارهای تکراری و پر استفاده.هدف MVC و یا الگوهای مشابه مثل MVT چیه؟هدف اون‌ها اینه که عملکرد، منطق و همهٔ اجزای یک برنامه رو طبق استاندارهای از پیش تعریف شده از هم جدا کنیم تا چند نفر بتونن به شکل همزمان روی یک پروژه بدون تداخل کار بکنن.نکته: فریمورک‌های زیادی از مدل یا الگوی طراحی MVC استفاده می‌کنن، اما نکتهٔ مهم اینه که هر فریمورک ممکنه به یک شکل خاص این الگوی طراحی رو پیاده‌سازی کنه، به خاطر همین گاهی ممکنه که MVC پیچیده به نظر برسه.مدل (Model):این بخش مسئول:- گرفتن و دستکاری داده‌ها- در تعامل با یک پایگاه داده و بانک اطلاعاتی هست.- کوئری زدن به دیتابیس داخل مدل انجام میشه.- مدل می‌تونه مستقیما View رو بروز کنه، اما بهتره که این کار رو نکنیم.کنترلر (Controller):وظیفهٔ این بخش گرفتن ورودی‌های کاربر، فرستادن درخواست به مدل و دریافت داده‌ها از Model و بروز رسانی View هست.اینجا ورودی می‌تونه هر چیزی باشه، مثلا وقتی کاربر روی یک لینک یا button مثل submit (و مشابه اون) کلیک می‌کنه، در واقع ما مقادیری رو از طریق POST به عنوان ورودی به کنترلر می‌فرستیم و بعد Controller میاد و اون‌ها رو پارس می‌کنه و طبق چیزی که ازش خواستیم یک سری عملیاتی رو از طریق ارسال درخواست به مدل انجام میده و جواب رو از مدل می‌گیره و دوباره به View می‌فرسته.نمای ظاهری (View):- رابط کاربری برنامه- چیزی که کاربر وقتی با برنامهٔ ما کار می‌کنه، می‌بینه- View به وسیلهٔ Controller بروز میشه- در واقع View همون کدهای HTML و CSS هستن که ما می‌تونیم با استفاده از Template Engine مقادیر و متغیرهای داینامیکی که از طریق Controller یا گاهی Model (توصیه نمیشه) دریافت شدن رو نشون بدیم.نحوه عملکرد MVCتوسعه دهندهٔ اولیه لاراول Taylor Otwell هست.تاریخچه مختصر لاراول:نسخهٔ اول و دوم:در سال ۲۰۱۱ منتشر شد.Model View Session رو داشت اما خبری از Controller نبود.اما توی همون سال بهش Controller و Blade رو هم اضافه کرد و شدن نسخهٔ ۲.نسخهٔ سوم:- ارائه یک داک خیلی قوی- توسعه برنامهٔ خط فرمان artisan- ساپورت از:  مدیریت سیستم بانک اطلاعاتی و migrations  لایه‌های بانک اطلاعاتی  پشتیبانی از events  همگام شدنش با unit testing  توسعه سیستم پکیجینگ یا Bundleنسخهٔ چهارم:سال ۲۰۱۳ بر پایه کدهای Illuminate توسعه داده و منتشر شد.اینجا بود که composer وارد بازی شد (یک پکیج منجر مشابه pip توی پایتون).پشتیبانی از:  - database seeding  - صف‌های پیام  - ارسال انواع ایمیل  - حذف نرم (soft deletion)نسخهٔ پنجم: سال ۲۰۱۵ منتشر شد.- برنامه‌ریزی برای اجرای تسک‌ها با استفاده از پکیج scheduler- بهینه‌سازی پیکیج‌های assets با استفاده از Elixir- ساده‌سازی عملیات احراز هویت با پکیج Socialite** نسخه ۵.۵:۲۰۱۷ و به صورت LTS ارائه شد (۲ سال رفع مشکل، ۳ سال مشکلات امنیتی)نسخهٔ ششم:بهبود پاسخ به مجوزهای دسترسیقابلیت Job Middlewareقابلیت Lazy Collectionsبهبود Sub Querysیکجا کردن ساختار فرانت در پکیج کامپوزر laravel/uiلینک دانلود XAMPP، نکتهٔ مهم توی نصب زمپ اینه که ببینیم که Laravelای که قراره نصب کنیم به چه نسخه‌ای از PHP نیاز داره و مطابق همون زمپ رو دانلود و نصب کنیم تا مشکلی پیش نیاد.دانلود Composer که یک Dependency Manager برای PHP هست.// check if php is installed:
php -v

// check if composer is installed:
composerبرای اینکه لاراول روی سیستم ما (یا هاست و سرور ما) درست کار کنه باید یک سری php extension هم داشته باشیم؛ اگر این extensionها موجود نباشن، با مشکل روبرو می‌شیم.این doc نحوهٔ نصب و راه‌اندازی لاراول و یا یک پروژهٔ لاراولی رو توضیح داده. ما می‌تونیم مستقیم لاراول رو روی سیستم نصب کنیم و بعد برای اجرا هر پروژه یک کامند بزنیم و پروژه استارت بخوره و یا اینکه نه، مستقیم با composer هر سری لاراول رو نصب و استفاده کنیم.از طریق composer:composer create-project laravel/laravel example-app
cd example-app
php artisan serve
// access to server: http://localhost:8000از طریق نصاب global:composer global require laravel/installer
laravel new example-app
cd example-app
php artisan serveدستور laravel رو باید به PATH سیستم عامل اضافه کنیم:Add Laravel to PATHساختار پوشه‌های لاراول:پوشه app، که پوشهٔ اصلی برنامه محسوب می‌شود. داخل این پوشه، موارد زیر رو داریم:۱- console: که CLI مربوط به artisan داخلشه۲- exceptions: کنترل خطاهای برنامه۳- http: شامل Controller Middleware و Kernel.php۴- providers۵- User.php: مدل‌ها اینجا قرار می‌گیرندپوشهٔ bootstrap که ربطی به بوت استرپ CSS نداره و یک راه‌اندازه و شامل:۱- cache: فایل‌های مورد نیاز رو کش می‌کنه.۲- app.php: اجرا کننده و راه‌انداز frameworkپوشهٔ کانفیگ:تمامی پیکربندی‌های ما اینجا قرار می‌گیره.پوشهٔ Database:برای مدیریت دیتابیس مورد استفاده قرار می‌گیرد و داخل این پوشه، یک پوشه دیگه داریم به اسم factories که دیتاهای ساختگی برای کار با دیتابیس ایجاد می‌کنه، پوشهٔ migrations که می‌تونیم جدول های دیتابیس خودمون رو به راحتی ایجاد کنیم. پوشهٔ seeds برای تست دیتابیس هست.پوشهٔ public:htaccess. که برای پیکربندی آپاچی مورد استفاده قرار می‌گیره.فایل index.php که تمامی درخواست‌ها از این فایل عبور می‌کنن و htaccess نقش مهمی رو در این جریان داره.پوشهٔ resources:این پوشه شامل فایل‌های جاوا اسکریپتی، CSS و SASS هست. lang برای زبان‌ها و نهایتا view که باید فایل‌های view با پسوند blade.php توش قرار بگیرن.پوشهٔ route برای مسیردهی درخواست‌ها به سرور مورد استفاده قرار می‌گیرد:فایل api.php: مدیریت درخواست‌هایی که قصد برقراری ارتباط از طریق api رو دارن.فایل channels.php: برای ثبت eventها مورد استفاده قرار می‌گیره.فایل console.php: که برای مدیریت درخواست‌هایی هست که از طریق CLI می‌رسن.فایل web.php: برای مسیردهی درخواست هایی که از طریق وب می‌رسن.در طول پروژه یک سری فایل توسط لاراول ساخته و کامپایل میشه که توی پوشهٔ storage قرار می‌گیرن.پوشهٔ app: فایل‌های تولید شده در حین پروژهپوشهٔ framework: ذخیره فایل‌ها و کش‌های پروژهپوشهٔ logs: ذخیره‌سازی لاگ‌های پروژهپوشهٔ tests:توی این پوشه فایل‌های مربوط به unit testing قرار می‌گیرند.پوشهٔ vendor که مربوط به composer میشه.فایل editconfig برای ویرایش تنظیمات و پیکربندی‌های editor هست.فایل env که می‌شه کانفیگ‌های پروژه رو اینجا قرار داد. (فایل env.example) نمونه‌ای از این فایل است.فایل package.json مربوط به npm می‌شود.فایل phpunit.xml برای تنظیم ابزار تست هست.فایل webpack.mix.js برای تنظیمات پکیج میکس لاراول هست.پلاگین‌های مناسب برای کار با لاراول در vscode:- Bracket Pair Colorize: رنگی کردن پرانتز، براکت و... برای تمیز دادن این موارد.- Electron Color Theme: رنگ‌بندی مورد علاقه، شایدم Dracula- PHP Extension Pack: شامل Debugger و IntelliSense- Prettier: فرمت کردن کد- vscode-icons- Laravel Extension Pack: شامل چندین پلاگین برای کمک به توسعه لارول (فعلا ۱۲ پلاگین)- PHP Namespace Resolverمعماری لاراول:مفهوم DI و IoC:تزریق وابستگی (Dependency Injection):منظور از وابستگی‌ها: کلاس‌ها، کتابخانه ها و... اون چیزهایی که پروژهٔ ما به اون‌ها وابسته است. اما کلا این مفهوم یک Design Pattern یا الگوی طراحی هست و هدف اون حذف وابستگی‌های میان دو کلاسه، یعنی بین کلاس‌ها باید کمترین وابستگی وجود داشته باشد.وارونگی کنترل (Inversion of Control):اما IoC یک اصله و نه یک الگوی طراحی، این اصل میگه که وابستگی دو ماژول باید چطوری باشه.برای درک مفهوم DI و IoC باید به مثال زیر توجه کنیم، پایین‌تر توضیحش اومده:درک مفهوم DI و IoCتوی کلاس Photo یک constructor داریم که توش به دیتابیس کانکشن می‌زنه و بعدش از اون کانکشن برای کوئری زدن استفاده می‌کنه، در واقع کلاس Photo حالا وابسته به کلاس DB هست. حالا ما می‌تونیم با استفاده از دیزاین پترن DI وابستگی به کلاس رو حذف کنیم برای این کار از setter یا contractor استفاده میشه.استفاده از constructor برای DIدر تصویر سمت راست می‌بینیم که از constructor برای تزریق وابستگی استفاده کردیم، یعنی برخلاف سمت راست که کلاس خودمون رو مستقیما وابسته به یک کلاس دیگه برای کانکشن زدن کردیم؛ اما در سمت راست، ما میام و اون وابستگی یعنی شی‌ای که قراره کانکشن ما باشه رو از طریق constructor به کلاس خودمون تزریق می‌کنیم.اما روش setter:استفاده از setter برای تزریق وابستگییک متد setter تعریف کرده، حالا کانکشن رو به این متده پاس میده و در واقع تزریقش می‌کنه به داخل کلاسی که قراره از این کانکشن استفاده کنه.اما هنوز مثال‌های بالا یک ایرادی دارن، اون ایراد هم اینه که برنامه‌نویس باید بدونه کلاسی که قراره باهاش کار کنه، چه وابستگی‌هایی دارن، برای حل این مشکل میام و از اصل وارونگی کنترل یا IoC استفاده می‌کنیم:IoCکلاس IoC در واقع میاد پیچیدگی‌های تزریق وابستگی و اینکه چه وابستگی‌هایی نیازه رو برای ما حل می‌کنه، فرم کلی ماجرا اینطوریه که یک متد static داریم که توش یک شی از کلاس مورد نظر ساخته میشه و وابستگی بهش تزریق میشه و حالا کافیه که ما یک شی از این IoC بسازیم بدون اینکه نگران DI و خود D باشیم.اما IoC توی لاراول خیلی حرفه‌ای‌تر و پیچیده تره و با Service Container و مفاهیم مثل register و boot هندل میشه.بخش اول - Service Container:The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are &quot;injected&quot; into the class via the constructor or, in some cases, &quot;setter&quot; methods.در واقع IoC Container همون Service Container هست که توی Laravel به این اسم می‌شناسیمش و برای ما DI رو انجام میده.شروع یک پروژهٔ لاراولی از پوشهٔ bootstrap و فایل app.php هست. چه اتفاقی توی app می‌افته؟در قدم اول یک شی از Application از مسیر Illuminate\Foundation ایجاد می‌کنه و توی app ذخیره می‌کنه. نکتهٔ مهم اینه که ما با کد زیر که مقدار آدرس اصلی پروژه رو برمی‌گردونه به constructor کلاس Application ارسالش می‌کنیم:$_ENV[&#039;APP_BASE_PATH&#039;] ?? dirname(__DIR__)توی قسمت بالا اولش برنامه سعی داره تا مسیر برنامه رو از طریق متغیر محیطی و گلوبال ENV بخونه، اما اگر null باشه، با null coalescing operator مقدار __DIR__ رو برمی‌گردونه.خب حالا app ما در واقع Service Container ما هست که ما می‌تونیم وابستگی‌ها رو بهش تزریق کنیم:app()-&gt;bind(&#039;num&#039;, function (){
    return 10;
    });
dd(app()-&gt;make(&#039;num&#039;));تزریق وابستگی به روش‌های مختلفی می‌تونه صورت بگیره، دو تا از مهم‌ترین متدهایی که برای این کار مورد استفاده قرار می‌گیرن، یکیش bind هست و یکی دیگه singleton که خود سینگلتون یک دیزاین پترن هست و فقط و فقط یک شی از یک کلاس رو برمی‌گردونه و می‌تونه در مواردی مثل ساخت connection از یک کلاس اتصال به db کاربردی باشه، چون ما نمی‌خوایم که مثلا ۱۰ تا کانکشن باز به دیتابیس داشته باشیم و منابع سرور رو به چخ بدیم.توی مثال بالا ما به جای تابع app که در واقع یک Helper Function هست می‌تونیم به راحتی از شی ایجاد شده استفاده کنیم؛ اما خوبی این هلپر فانکشن اینه که هر جای پروژه در دسترسه.خب حالا ما با bind کردن یک dependency که می‌تونه تابع، کلاس و... باشه رو به Service Container لاراول که اینجا همون app ما هست inject کردیم، حالا با متد make می‌تونیم بهش دسترسی داشته باشیم، مثلا اگر یک تابع تزریق کردیم، خروجی تابع رو بگیریم؛ اگر یک کلاس تزریق کردیم، برامون یک شی می‌سازه (به همراه وابستگی‌ها) و اون رو برمی‌گردونه.بررسی تفاوت دو متد bind و singleton برای تزریق وابستگی:&lt;?php
app()-&gt;bind(&#039;num&#039;, function (){
    return rand(10, 50);
    });
for ($i=1; $i&lt;=5; $i++){
    print_r(app()-&gt;make(&#039;num&#039;).&#039;&lt;br&gt;&#039;);
}
?&gt;

//output:
20
23
33
46
41همون‌طور که توی مثال بالا می‌بینیم با هر بار فراخوانی num یک مقدار جدید بر می‌گرده، اما حالا با signleton design pattern ببینیم که چه اتفاقی می‌افته:&lt;?php
app()-&gt;singleton(&#039;num&#039;, function (){
    return rand(10, 50);
    });
for ($i=1; $i&lt;=5; $i++){
    print_r(app()-&gt;make(&#039;num&#039;).&#039;&lt;br&gt;&#039;);
}
?&gt;

//output:
50
50
50
50
50خب توی لاراول وقتی app ساخته شد، حالا با singleton بهش dependency هایی رو تزریق می‌کنیم که شامل موارد زیر هست، آرگومان اول یک interface و دومی یک کلاسه:در واقع ما دیگه نگران وابستگی‌های کلاس Kernel::class نیستیم و با make کردن خیلی راحت ازش یک خروجی می‌گیریم.Service Provider:ما اگر بخوایم یک سرویسی داشته باشیم؛ مثلا یک کلاسی تعریف کنیم برای کار با دیتابیس، باید این کلاس رو به Service Container خودمون bind کنیم. برای این کار ما می‌تونیم بریم توی bootstrap/app.php و اونجا عملیات bind رو انجام بدیم یا اینکه از Service Provider لاراول استفاده کنیم که توی پوشهٔ Providers در پوشه app قرار گرفته:AppServiceProvider.php
AuthServiceProvider.php
...با اجرا شدن پروژهٔ لاراول این providerها بوت می‌شن و توی اون‌ها عملیات بایند شدن به Service Container لاراول صورت می‌گیره. هر Service Provider دو بخش register و boot داره:&lt;?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}توی متد register ما عملیات DI رو انجام می‌دیم و توی boot اجراش می‌کنیم.برای ساخت یک Service Provider سفارشی از دستور artisan می‌تونیم استفاده کنیم:php artisan make:provider NameServiceProviderبعد از ایجاد Service Provider باید اون رو به لاراول بشناسونیم، برای این کار باید وارد پوشه config و فایل app.php بشیم و دنبال آرایه &#x27;providers&#x27; باشیم و سرویسی که ایجاد کردیم رو توی این آرایه قرار بدیم تا لاراول اون رو auto load کنه.برای این کار این خط رو به آرایه اضافه می‌کنیم:app\Providers\DBServiceProvider::classحالا داستان این دابل کالن class چیه که همه جا هست؟ اینجا توضیح داده.** نکتهٔ مهم: ما می‌ریم به یک فروشگاه که یک جنسی رو خریداری و استفاده کنیم؛ طبیعتا توی یک فروشگه اجناس مختلفی وجود داره، حالا ما می‌تونیم این چرخه رو به لاراول هم بسط بدیم:کارخانه‌ها: Service Providersفروشگاه: Service Containersاجناس: ServicesRequest Lifecycleنماهای خارجی یا Facades:این الگوی طراحی، جزئیات نرم‌افزار رو به خوبی پنهان می‌کنه، همون‌طور که نمای یک ساختمون، اونچه که پشت پرده هست رو مخفی و زیباسازی می‌کنه. کاری که facades می‌کنه اینه که به ما اجازه میده تا به صورت static به سرویس‌های service container دسترسی داشته باشیم. در واقع یک نوع static interface هست.حالا facades کجا تعریف میشه؟توی فایل app.php در پوشهٔ config بخش aliases دسترسی به facades اومده.قراردادها (Contracts):لاراول وابستگی زیادی به پکیج‌ها دارد؛ یعنی می‌تونیم از پکیج‌های بیرونی استفاده کنیم یا اینکه حتی خودمون یک پکیجی برای لاراول بنویسیم. حالا فرض کنید ما قراره یک پکیج جدید توسعه بدیم یا اینکه یک درایور جدید برای یک پکیج خاص مثل Caching توسعه بدیم، حالا لاراول برای ما این امکان رو به وجود اورده که از یک intreface از پیش آماده شده و استاندارد استفاده کنیم.در واقع به این interface واحد در لاراول قرارداد گفته میشه. محل قرارگیری:vendor\laravel\framework\Illuminate\Contractsشروع کار با View از مدل MVC:برای آشنایی اولیه با View، در ابتدای کار ما از روتر به View خروجی می‌گیریم؛ اما روند صحیح اینه که ابتدا روتر یک کنترلر رو فراخوانی می‌کنه و کنترلر خروجی رو به View می‌فرسته.توی پوشهٔ routes یک فایلی داریم به اسم web.php که داخلش یک facade به اسم Route با متد get فراخوانی شده و بررسی می‌کنه که اگر ما صفحه index رو خواستیم؛ عملیات درخور اون رو انجام بده.** نکته: ما به جای Route facade می‌تونیم از متغیر router هم استفاده کنیم؛ اما اینجا با استفاده از فساد Route به شکل static به متدهاش دسترسی پیدا کردیم.&lt;?php
/ /Path: routes\web.php
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the &amp;quotweb&amp;quot middleware group. Now create something great!
|
*/

Route::get(&#039;/&#039;, function () {
    return view(&#039;welcome&#039;);
});در کد بالا، View یک helper function هست. حالا این View از کجا اومده؟ویوها توی لاراول توی resources و توی پوشهٔ Views قرار گرفتن، همهٔ این Viewها دارای پسوند blade.php هستن که blade در واقع موتور مفسر templateهای لاراول محسوب میشه. اما وقتی که توی routes یک view رو فراخوانی می‌کنیم نیازی نیست که پسوند blade.php رو انتهاش قرار بدیم.پارامتر دوم view helper function یک آرایه هست؛ ما می‌تونیم دیتای خودمون رو از این طریق به view پاس بدیم و با سینتکس خود php یا blade اون‌ها رو نمایش بدیم. مثال:view(&#039;page&#039;, [&#039;var&#039; =&gt;  &#039;some value&#039;]);حالا داخل viewی page می‌تونیم به صورت زیر به $var دسترسی داشته باشیم:&lt;?php echo $var ?&gt;
{{ $var }}خود لاراول می‌تونه کدهای html رو با htmlspecialchars پاکیزه‌سازی کنه، اما اگر ما نخواییم که این کار رو انجام بده، کافیه که این حرکت رو بریم://prevent htmlspecialchars
{!! $name !!}حالا اگر بخوایم که double curly brace رو بفرستیم به خروجی، چیکار باید کنیم؟@{{ $something }}حالا این view کجا کامپایل میشه؟اگر بریم به مسیر storage\framework\views دو تا فایل با اسم هش شده هست. لاراول فایل‌های blade رو تفسیر می‌کنه و به کدهای php تبدیل می‌کنه.آشنایی با دستورات Blade:دستورات شرطی در blade:@if (condition)

@else

@endifحالا کافیه که افزونه Laravel Blade Snippets رو نصب کنیم و با یک @ توی vscode به تمامی دستورات دسترسی داشته باشیم.اما یک دستور دیگه‌ای داریم به اسم unless که جدیده،  در واقع unless در واقع همون if ما هست با این تفاوت که یک نقیض پشت شرط می‌ندازه، یعنی اگر شرط false باشه، اون رو true می‌کنه و اجرا میشه.@unless ()

@endunlessبه این دستورات blade در واقع directive گفته میشه.یک دستور کاربردی دیگه:@forelse ( $list as $item)
{{ $item }}
@empty
&lt;p&gt; No List &lt;/p&gt;
@endforelseدر دستور بالا، اگر لیست ما خالی باشه، No List پرینت می‌شه و اگر خالی نباشه، که محتویاتش رو چاپ می‌کنه.خیلی جالبه که یک directive هم برای php داریم:@php

@endphpبرای افزایش کانتر:&lt;?php
@while ($i &lt; 10)
  The current value is {{ $i }} &lt;br&gt;
  @php
     $i++;
  @endphp
@endwhileارث‌بری در Templates و Master Layout:توی لاراول این امکان وجود داره که صفحات از یکدیگر ارث‌بری داشته باشن. حالا این کجا کاربرد داره؟ فرض کنیم که ما اکثرا توی سایت‌های خودمون یک header و یک footer داریم؛ اگر قرار باشه برای هر صفحه از app خودمون یک فوتر و هدر قرار بدیم؛ کلی کد رو باید به صورت دستی و hard code شده وارد برنامه کنیم که منطقی نیست. بنابراین با این قابلیتی که لاراول و blade برای ما فراهم کردن کافیه که یک Template اصلی داشته باشیم و بقیهٔ صفحات از این صفحه ارث ببرن.برای این کار توی پوشهٔ views از پوشهٔ resources یک پوشه ایجاد می‌کنیم به اسم layouts و توی اون قالب‌های خودمون رو ایجاد می‌کنیم. صفحه‌ای که قراره از اون ارث‌بری بشه رو با مقادیر ثابتش مثل هدر و فوتر طراحی می‌کنیم و بعد هر صفحه‌ای که قرار از اون ارث‌بری داشته باشه رو با دایرکتیو extends توسعه می‌دیم.حالا یک نکتهٔ مهم در مورد تعیین محل قرارگیری نوشته‌ها و... داریم. توی مسترپیج با استفاده از yield directive می‌تونیم قسمت های مختلف سندمون رو tag گذاری کنیم و به این قسمت‌ها با استفاده از section directive دسترسی داشته باشیم. حالا دو مدل syntax برای section وجود داره، یکی section...endsection و دیگری section که ۲ تا پارامتر داره، اولی اسم yield و دومی مقداری که قراره جای اون yield نمایش بده.بخش‌بندی master layout یا Partial کردن:  یه directive داریم به اسم include که می‌تونیم بخش‌های مختلف master layout رو بخش‌بندی کنیم و هر وقت نیاز بود به پروژه include کنیم. بهتره برای دسته‌بندی بهتر توی views یک پوشهٔ partial ایجاد کنیم و فایل‌های مربوط به هدر، فوتر و... رو توی اون قرار بدیم.یک directive داریم به اسم includeif که اگر فایلی که به عنوان آرگومان گرفته وجود داره رو لود می‌کنه و اگر نبود؛ اون رو لود نمی‌کنه.** نکته: ما با استفاده از composer.log و package.json می‌تونیم پوشهٔ vendor رو بسازیم. کافیه بزنیم composer install و تمام.پایان قسمت ۲۰</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Mon, 27 Jun 2022 00:27:38 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی PHP (کامل نشده)</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-php-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-bheglomzy9l4</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.زبون‌هایی مثل Python و PHP و کلا زبون‌های Server-Side برای ایجاد برنامه‌های داینامیک به وجود اومدن. تقریبا ۸۰ درصد از وب سایت‌های حاضر حداقل در بخشی از کدهای خودشون از PHP استفاده کردن.منظور از LDE یا Local Development Environment اینه که یک وب سرور مثل Apache رو روی سیستم خودمون کانفیگ کنیم و به جای استفاده از یک سرور/هاست دیگه، توسعه رو روی سیستم خودمون انجام بدیم و بعد وقتی محصول نهایی شد اون رو روی سرور production دیپلوی کنیم.  طبیعتا خوبی استفاده از LDE اینه که مشکلات نرم‌افزاری که حین توسعه رخ میده برای عموم در دسترس نیست و امنیت پروژهٔ ما و سایر پروژه‌هایی که روی همون هاست یا سرور هستن هم حفظ میشه و اینکه طبیعتا خیلی سریع‌تره و نیاز به دسترسی به اینترنت هم نداریم.حالا این PHP مخفف چیه؟ PHP: Hypertext Preprocessorچه کارهایی با PHP می‌کنیم؟- پردازش فرم‌ها- برقراری ارتباط با دیتابیس- ایجاد نواحی امن (لاگین و...)- خوندن و نوشتن توی فایل‌ها- مدیریت فایل‌های آپلود شده- و چیزهایی که خودت می‌دونی :)نحوه عملکرد PHPوب سرور نرم‌افزاریه که روی سیستم ما نصب میشه و منتظر دریافت یک درخواست از سمت کاربر می‌مونه، مثلا یک کاربر درخواست باز شدن یک صفحهٔ php به آدرس localhost/index.php رو میده، وب سرور می‌بینه که این یک فایل php هست پس به zend میگه که تو این صفحه رو برای من تفسیر کن و خروجیش رو بده به من تا من بدمش به کاربر.قبلا در مورد این خونده بودم که دو تا کد زیر دقیقا یکی هستن و دومی یک shortcut برای اولیه، اما از اونجایی که ممکنه این فیچر روی وب سرور غیر فعال شده باشه، بهتره که ازش استفاده نکنیم:&lt;?php echo &#039;Hello World&#039;; ?&gt;
&lt;?=&#039;Hello World&#039;?&gt;فرق echo و print چیه؟ تقریبا فرق خاصی ندارن، به جز اینکه echo کمی سریع‌تره.برای فراخوانی یک Constant نیازی نیست که از $ ساین استفاده کنیم.توی PHP یک سری متغیر از پیش تعریف شده داریم که توی هر scope‌ای در دسترس هستند و بهشون می‌گیم Super Globals مثل متغیر POST GET FILES و... لیست کامل این متغیرها اینجا اومده.همچنین یک سری ثابت از پیش تعریف شده هم داریم که لیستش اینجا امده.انواع داده توی PHP:اعداد صحیحاعداد اعشاریکاراکترهاآرایه‌هارشته‌هاBooleanتفاوت Double Quote و Single Quote چیه؟دابل کوت: رشته‌هایی که داخل دابل کوت قرار می‌گیرند قبل از اینکه به خروجی فرستاده بشن به وسیلهٔ PHP مورد پردازش قرار می‌گیرن:&lt;?php
$name = &#039;Joe&#039;;
echo &amp;quotHello $name&amp;quot
?&gt;

output: Hello Joe&lt;?php 
$name = &#039;Joe&#039;; 
echo &#039;Hello $name&#039;; 
?&gt;  

output: Hello $nameدابل کوت، تمامی escape characters رو میشناسه، اما سینگل کوت فقط بعضی‌ها رو میشناسه.آرایه‌ها:Indexable
Associativeآرایه‌های چند بعدی: Multidimensional Arraysنحوه اضافه کردن یک مقدار جدید به آرایه indexable:$colors = array(&#039;blue&#039;, &#039;red&#039;);
$colors[] = &#039;green&#039;;توی پایتون برای اینکه چیزی به لیست اضافه کنیم از متد append استفاده می‌کردیم و برای ترکیب دو تا دیکشنری از متد update.نحوه پیاده‌سازی ۲ نوع آرایه عملگر XOR در صورتی مقدار true بر می‌گرداند که طرفین عملگر از یک جنس نباشند؛ یعنی اگر هر دو false یا true باشند؛ مقدار ارزیابی شده false می‌شود اما اگر یکی false و دیگری true باشد مقدار ارزیابی شده true خواهد بود.نحوه عملکرد XORانواع عملگرها در PHP:مطالعهٔ بیشتر در اینجا.توی زبان‌های برنامه‌نویسی انواعی از عملگرها رو داریم؛ با توجه به کار و ماهیتی که دارن، اون‌ها رو توی دسته‌های مختلفی قرار می‌دیم. مثلا عملگرهای ریاضی مثل + - * / ٪ ** باعث انجام یک عمل ریاضی یا محاسباتی بین عملوندهای طرفین خودشون میشن (operators and operands).عملگرهای رابطه‌ای، رابطهٔ بین دو عملوند رو بررسی می‌کنن، مثلا بزرگتری یا کوچکتری و... .عملگرهای منطقی، مطابق منطق جبر بول یک مقدار مطلق، یعنی صفر (false) و یا ۱ (true) رو برمی‌گردونن.یه سری دیگه از عملگرها، عملگرهای بیتی هستن.یه نکته‌ای که اینجا بهش اشاره شد و جالب بود، بحث تفاوت بین &amp;&amp; و and هست که &amp;&amp; رو کاربردی‌تر می‌کنه برای ما، به مثال زیر توجه کنید:$res = true &amp;&amp; false;
var_dump($res);
// output: bool(false)

$res = true and false;
var_dump($res);
// output: bool(false)عملگر = تقدم (precedence) بالاتری نسبت به عملگر and داره، در صورتی که علملگر &amp;&amp; تقدم بالاتری نسبت به = داره، اما از اون‌جایی که پرانتز تقدمش از = بیشتره، پس اگر true and false رو بذاریم داخل پرانتز و بعد به یک متغیر نسبت بدیم، همون جوابی رو می‌گیریم که از &amp;&amp; گرفتیم.یک سینتکس جایگزین برای if...esle مرسوم داریم؛ این سینتکس زمانی کاربرد داره که می‌خوایم از این ساختار داخل کدهای html استفاده کنیم؛ اگر از if...esle سنتی استفاده کرده باشید؛ می دونید که چه عذابیه، اما این ساختار کار رو خیلی راحت می‌کنه:ساختار جایگزین if...else سنتی شرطی‌های Yoda:یک شیوه برای بررسی شروط هست؛ تفاوتش با بررسی‌های مرسوم و سنتی اینه که متغیر در سمت راست قرار می‌گیره، این کمک می‌کنه که مثلا اگر به جای == از = استفاده کردیم، پارسر جلوی عملیات رو بگیره:Yoda Conditionalsساختار کوتاه شرطی:$message = $is_logged_in ? &amp;quotWelcome Back!&amp;quot : &amp;quotHello There&amp;quotاگر مقدار $is_logged_in برابر با true باشه، مقدار بعد از ؟ برمی‌گرده و اگر غلط باشه، مقدار بعد از :یه حرکت دیگه‌ای داریم به اسم operator الویس، که شبیه الویسه، اگر متغیر null باشه، مقدار بک‌آپ رو برمی‌گردونه:$name = $name ?: &amp;quotJoe&amp;quotساختار switch که بر مبنای یک سینگل variable میاد و موارد مختلفی رو بررسی می‌کنه:switch...caseما می‌تونیم توی ساختار switch...case دسته‌بندی یا گروهبندی هم داشته باشیم:گروه‌بندی switch...case در phpتابع rand توی php برای تولید اعداد تصادفی بین دو تا رنج مین و ماکز کاربرد داره.$random_value = rand($min, $max);</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Thu, 23 Jun 2022 03:41:56 +0430</pubDate>
            </item>
                    <item>
                <title>مقدمه‌ای بر الگوریتم‌ها (کامل نشده)</title>
                <link>https://virgool.io/@maslak/%D9%85%D9%82%D8%AF%D9%85%D9%87-%D8%A7%DB%8C-%D8%A8%D8%B1-%D8%A7%D9%84%DA%AF%D9%88%D8%B1%DB%8C%D8%AA%D9%85-%D9%87%D8%A7-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-fortyxax65ws</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.یک الگوریتم، در واقع راه حلی برای برطرف کردن یک مشکل یا برآورده‌سازی یک نیاز خاصه، هر آدمی ممکنه یک مشکل یا یک نیاز خاص رو به روش خودش حل کنه، پس ممکنه به ازاری یک مشکل یا نیاز چندین الگوریتم داشته باشیم. یک برنامهٔ نرم‌افزاری برای حل مشکل/برآورده‌سازی نیاز ممکنه از یک یا مجموعه‌ای از الگوریتم‌ها استفاده کنه، بعضی از این الگوریتم‌ها عمومیت دارن و به‌طور گسترده و به شکل استاندارد پیاده‌سازی و در قالب کدهای کتابخانه‌ای استاندارد همراه با زبان‌های مختلف ارائه میشن، اما قطعا در یک برنامهٔ حرفه‌ای برنامه‌نویس مجبوره که الگوریتم‌های خاصی رو توسعه بده که قطعا در این میان از اون الگوریتم‌های استاندارد (مثل مرتب‌سازی، محاسباتی و...) استفاده می‌کنه. در واقع این الگوریتم یا روش حل مساله هست که به یک برنامه موجودیت و قدرت میده.توسعه نرم‌افزار بدون دانش الگوریتم و دیتا استراکچر، مثل ساخت یک ماشین بدون دونستن نحوهٔ عملکرد موتوره.ویژگی‌های هر الگوریتم:۱. پیچیدگی الگوریتمالف) پیچیدگی فضایی: میزان حافظهٔ مورد نیازب) پیچیدگی زمانی: میزان زمان مورد نیاز برای کامل شدن۲. ورودی و خروجی‌هاالف) الگوریتم چه چیزهایی را دریافت و در عوض چه چیزی را به عنوان خروجی برمی‌گرداند؟ مثلا یک عدد می‌گیره و با اعمال یک پروسه و تست‌های خاص می گه که این عدد اول/زوج و... هست یا خیر.۳. دسته‌بندی الگوریتم‌ها:سریال (Serial)/موازی (Parallel)، دقیق (Exact)/تقریبی (Approximate)، قطعی (Deterministic) و غیر قطعی (Non-Deterministic).الگوریتم‌های معمول:منظور الگوریتم‌های عمومی و با کاربرد فراوون هستن که احتمالا حین توسعهٔ هر نرم‌افزاری باهاشون برخورد داریم و معمولا در قالب کتابخانه‌های استاندارد پیاده‌سازی میشن و به صورت یک پکیج همراه با هر زبان برنامه‌نویسی خاصی نصب میشن یا اصولا به شکل built-in درون خودشون دارنش.۱. الگوریتم‌های جستجو:پیدا کردن یک دیتای خاص توی یک ساختار (برای مثال، یک زیر رشته از یک رشته بزرگتر)، مثل تابع substr توی php ۲. الگوریتم‌های مرتب‌سازی:یک مجموعه دیتا دریافت می‌کنند و با یک ترتیب خاص، به اون داده‌ها نظم می‌دن مثل sort توی php۳. الگوریتم‌های محاسباتی:یک مجموعه دیتا دریافت می‌کنند و یک سری محاسبات روی اون‌ها انجام میدن.مثلا یک لیست از اعداد می‌دیم به یک تابع تا برای ما برآورد کنه که کدوم اعداد اول، زوج و... هستن.۴. الگوریتم‌های مجموعه‌ای (collection algorithms) - نمی‌دونم دقیقا توی فارسی چی باید بهش گفت:کار با مجموعه ای از داده‌ها (شمارش آیتم‌های خاص، جابه‌جا شدن بین عناصر داده، فیلتر کردن داده های ناخواسته و...)پیاده‌سازی الگوریتم اقلدیس برای پیدا کردن بزرگترین مقسوم‌علیه مشترک:Euclid&#x27;s Algorithm for find the greatest common denominator (GCD) of two ints:البته ظاهرا بهش Greatest common divisor هم گفته میشه.تعریف بزرگترین مقسوم علیه مشترک چیه؟ بزرگترین عدد مشترک بین دو تا عدد صحیح که باقی‌ماندهٔ تقسیم این اعداد برش برابر با صفر بشه، مثلا GCD برای اعداد ۲۰ و ۸ برابر ۴ هست.الگوریتم ما چی میشه؟ برای دو عدد a و b، اگر a از b بزرگتر باشه، a رو بر b تقسیم کنید.اگر باقی‌مانده (r) برابر با صفره، GCD برابر با b خواهد بود.۳. در غیر این صورت، a رو به b و b رو به r نسبت می‌دیم و دوباره این پروسه رو تکرار می‌کنیم تا جایی که r برابر با صفر بشه.اینم یک عکس برای درک بهتر الگوریتم GCDپیاده‌سازی این الگوریتم با پایتون:</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Wed, 22 Jun 2022 23:35:39 +0430</pubDate>
            </item>
                    <item>
                <title>آپلود فایل‌ها به شکل امن در PHP</title>
                <link>https://virgool.io/@maslak/%D8%A2%D9%BE%D9%84%D9%88%D8%AF-%D9%81%D8%A7%DB%8C%D9%84-%D9%87%D8%A7-%D8%A8%D9%87-%D8%B4%DA%A9%D9%84-%D8%A7%D9%85%D9%86-%D8%AF%D8%B1-php-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-ksi1dhw2awb7</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.مشخصات دوره:Lynda Uploading Files Securely with PHP - David Powersبرای درک این دوره باید با سینتکس اولیه PHP و توابع کاربردی زیر آشنا باشید:&lt;?php
  isset()
  is_array()
  in_array()
  str_replace()
?&gt;نکتهٔ مهم در طراحی فرم آپلود فایل در HTML، ست کردن attribute زیر هست:enctype=&amp;quotmultipart/form-data&amp;quotکلید PHP_SELF از آرایه سوپر گلوبال SERVER به فایل جاری اشاره می‌کنه:&lt;?php echo $_SERVER[&#039;PHP_SELF&#039;]; ?&gt;متد آپلود فایل‌ها حتما باید از نوع POST باشه.فایل‌هایی که آپلود میشن، اول توی یک مسیر موقت توی سرور قرار می‌گیرن و بعد ما می‌تونیم با نوشتن یک قطعه کد ساده اون فایل‌ها رو به جایی که می‌خوایم منتقل کنیم. برای دیدن مسیر موقتی که توی php ست شده، کافیه که تابع phpinfo رو ران کنیم و دنبال کلید واژهٔ upload_tmp_dir باشیم.اما باید دقت داشته باشیم که فایل آپلود شده، اگر به جای دیگه‌ای منتقل نشه، به سرعت از روی سرور ما حذف میشه.باید دقت داشته باشیم که فیلد name ورودی file ما حتما پر شده باشد؛ چرا که اگر این کار را نکنیم، روی سرور خروجی زیر یک آرایه تهی خواهد بود:print_r($_FILES):var_dump($_FILES);نکته: برای اینکه خروجی print_r به شکل خوانا و جذاب پرینت بشه، حتما باید خروجی تابع رو بین تگ های pre قرار بدیم:Array
(
    [uploaded_file] =&gt; Array
        (
            [name] =&gt; Screenshot_2022-05-23_09-27-39.png
            [full_path] =&gt; Screenshot_2022-05-23_09-27-39.png
            [type] =&gt; image/png
            [tmp_name] =&gt; /opt/lampp/temp/phpak1LmS
            [error] =&gt; 0
            [size] =&gt; 381197
        )
)در حین آپلود فایل، ممکنه که با یه سری error مواجه بشیم و کد اون ارور رو توی آرایه FILES می‌بینیم. هر کد یک معنا داره که لیست اون‌ها رو اینجا می‌بینیم:لیست کدهای error آپلود فایل در php (بخش اول) لیست کدهای error آپلود فایل در php  (بخش دوم) سایز فایل آپلود شده به byte هست و همین‌طور نوع فایل هم به وسیلهٔ مرورگر حدس زده میشه که ممکنه گاهی نادرست باشه.چطور می‌تونیم بزرگی فایلی که آپلود میشه رو محدود کنیم؟&lt;?php
$max = 50 * 1024;
if (isset($_POST[&#039;submit_pressed&#039;])) {
}
?&gt;

&lt;!doctype html&gt;
&lt;html lang=&amp;quoten&amp;quot&gt;

&lt;head&gt;
    &lt;title&gt;Title&lt;/title&gt;
&lt;/head&gt;

&lt;body&gt;
    &lt;form method=&amp;quotpost&amp;quot enctype=&amp;quotmultipart/form-data&amp;quot&gt;
      &lt;input type=&amp;quotfile&amp;quot name=&amp;quotuploaded_file&amp;quot&gt;
        &lt;input type=&amp;quothidden&amp;quot name=&amp;quotMAX_FILE_SIZE&amp;quot value=&amp;quot&lt;?php echo $max ?&gt;&amp;quot&gt;
        &lt;button type=&amp;quotsubmit&amp;quot name=&amp;quotsubmit_pressed&amp;quot&gt;Submit&lt;/button&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;کافیه یک متغیر که بزرگی فایل رو به بایت توش ذخیره می‌کنه داشته باشیم و توی فرم html، یک ورودی از نوع hidden با نام MAZ_FILE_SIZE و value برابر با متغیر یاد شده داشته باشیم. حالا اگر فایلی که کاربر قصد داره آپلود کنه از اون مقدار محدود شده بیشتر باشه، فایل اصلا آپلود نمیشه و برای ما error_code شماره ۲ برگشت داده میشه. ویژگی‌های پوشه آپلود فایل (upload directory):۱- ست کردن دسترسی‌ها یا permissions: وب سرور باید دسترسی نوشتن را داشته باشد.توی لینوکس برای امنیت بیشتر باید به دایرکتوری‌های دسترسی ۷۵۵ داد (شاید هم ۷۷۵). اعداد به ترتیب از چپ به راست مربوط به صاحب (owner)، گروه (group) و تمامی کاربران (all users) است. جدول زیر مفهوم هر عدد را نشان می‌دهد:سطوح دسترسی در linuxآپلود کردن فایل‌ها درون روت، به تمامی افراد امکان دسترسی عمومی به فایل را می‌دهد. بنابراین ایجاد پوشه‌ای که بتوان درون آن فایل‌ها را آپلود کرد؛ ضروری هست.مثلا می‌تونیم به راحتی در پوشه‌ای که می‌خوایم امکان دسترسی مستقیم رو محدود کنیم یک فایل htaccess ایجاد کنیم و دستور زیر رو توش قرار بدیم، که باعث میشه کاربری که به اون صفحه وارد شد؛ یک خطای ۴۰۳ دریافت کنه:Options -Indexesحالا که فایل رو آپلود کردیم و باید اون رو از پوشهٔ موقت یا upload_tmp_dir به پوشهٔ مورد نظر خودمون منتقل کنیم.توی PHP یک ثابت داریم به اسم __DIR__ که مسیر جاری رو نشون میده. باید توجه داشته باشیم که این ثابت در آخر خودش / نداره.برای انتقال فایل از تابع move_uploaded_file استفاده میشه:&lt;?php
$destination = __DIR__ . &#039;/path/to/your/upload/folder&#039;;
$from = $_FILES[&#039;name_of_input_in_html&#039;][&#039;tmp_name&#039;];
$to = $destionation . $_FILES[&#039;name_of_input_in_html&#039;][&#039;name&#039;];
$result = move_uploaded_file&#40;$from, $to&#41;;
?&gt;ما از طریق تابع php_info می‌تونیم به اطلاعات خیلی خوبی در خصوص نحوهٔ پیکربندی آپلود فایل‌ها دست پیدا کنیم؛ این اطلاعات در قسمت Core قرار گرفتن:مورد اول، حداکثر تعداد فایلی رو که میشه در یک زمان آپلود کردن نشون میده، اگر تعداد بیشتر از این باشه، به طور خود به خود نادیده گرفته میشن.مورد دوم، برای هر سینگل فایل محدودیت حجمی ایجاد می‌کنه.مورد سوم، مقدار کل حجم نهایی رو محدود می‌کنه، مثلا ممکنه ۴ تا ۲ مگابایتی آپلود کنیم یا ۸ تا ۱ مگابایتی و... . https://stackoverflow.com/a/23686617/12930583 یک سری از تنظیمات مربوط به آپلود رو می‌تونیم از طریق htaccess هم که یک فایل پیکربندی برای وب سرور آپاچی هست کنترل کنیم:فضای نام یا namespaces در php: https://www.w3schools.com/php/php_namespaces.asp فضاهای نام در php نسخه ۵.۳ معرفی شدند؛ این قابلیت امکان دسته‌بندی کلاس‌ها را برای ما ساده‌تر کرده و همین‌طور احتمال تصادم بین کتابخانه‌ها و فریمورک‌ها رو از بین برده. قبل از فضاهای نام، برنامه‌نویس‌ها از نام‌های بلند برای کلاس‌ها جهت حل این مشکل استفاده می‌کردند.طبق توضیحاتی که توی این دوره آموزشی اومده (که احتمالا الان استاندارها و قراردادها تغییر کرده)، معلم دوره میگه که طبق یک قرارداد ما باید در روت سایت یک پوشه به اسم src داشته باشیم و داخل اون باید برای هر فضای نام یک sub-directory داشته باشیم.یک قرارداد دیگه اینه که اسم فایلی که توش یک کلاس خاص داریم باید هم اسم کلاسمون باشه.نحوه تعریف namespace در یک فایل حاوی کلاس:FileName.php:

&lt;?php
namespace name_of_namespace;
class ClassName
{
... the body of the class ...
}

?&gt;نحوهٔ استفاده از namespace در کد خودمون:use name_of_namespace\ClassName as nickname;نکتهٔ مهم در قطعه کد بالا اینه که backslash استفاده شده ربطی به ویندوز و یا لینوکس و... نداره، این فرمت درست فراخوانی یک فضای نام توی php هست و دیگه اینکه use کردن یک فضای نام هیچ ربطی به لود کردن اون با require و include و مشتقاتش نداره، بلکه ما باید فایل رو جداگانه require کنیم.برخلاف require و include که می‌تونیم اون‌ها رو تقریبا در هر جایی از کد استفاده کنیم؛ اما use رو حتما باید در بالاترین نقطهٔ فایل انجام بدیم.دو تا تابع کاربردی:&lt;?php
// checks if the path passed as argument is a directory
is_dir()
// checks if the path passed as argument is writable
is_writable()
?&gt;چطوری یک error رو throw کنیم؟throw new \Exception(&amp;quotError message...&amp;quot) ;باید دقت کنیم که فرم عمومی بدون \ هست، اما چون از داخل یک فضای نام داریم این کلاس رو فراخوانی می‌کنیم باید بگیم که دنبال کلاس Exception از مسیر روت هستیم.حالا ما می‌خوایم یک کلاس ایجاد کنیم که کل پروسهٔ آپلود فایل رو توش هندل کنیم، بخش اول پیاده‌سازی این کلاس به شکل زیر خواهد بود:&lt;?php
namespace foundationphp;

class UploadFile
{
	protected $destination;
	
	public function __construct($uploadFolder)
	{
		if (!is_dir($uploadFolder) || !is_writable($uploadFolder)) {
			throw new \Exception(&amp;quot$uploadFolder must be a valid, writable folder.&amp;quot);
		}
		if ($uploadFolder[strlen($uploadFolder)-1] != &#039;/&#039;) {
			$uploadFolder .= &#039;/&#039;;
		}
		$this-&gt;destination = $uploadFolder;
	}
}و نحوهٔ استفاده از این کلاس در فایل index.php یا هر فایلی که بخش اصلی سایتمون هست:&lt;?php 
use foundationphp\UploadFile;

$max = 50 * 1024;
$message = &#039;&#039;;
if (isset($_POST[&#039;upload&#039;])) {
	require_once &#039;src/foundationphp/UploadFile.php&#039;;
	$destination = __DIR__ . &#039;/upload/&#039;;
    try {
    	$upload = new UploadFile&#40;$destination&#41;;
    } catch (Exception $e) {
    	$message = $e-&gt;getMessage();
    }
}
?&gt;
&lt;!doctype html&gt;
&lt;html lang=&amp;quoten&amp;quot&gt;
&lt;head&gt;
    &lt;meta charset=&amp;quotUTF-8&amp;quot&gt;
    &lt;title&gt;File Uploads&lt;/title&gt;
    &lt;link href=&amp;quotstyles/form.css&amp;quot rel=&amp;quotstylesheet&amp;quot type=&amp;quottext/css&amp;quot&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Uploading Files&lt;/h1&gt;
&lt;?php 
if ($message) {
    echo &amp;quot&lt;p&gt;$message&lt;/p&gt;&amp;quot
}
?&gt;
&lt;form action=&amp;quot&lt;?php echo $_SERVER[&#039;PHP_SELF&#039;];?&gt;&amp;quot method=&amp;quotpost&amp;quot enctype=&amp;quotmultipart/form-data&amp;quot&gt;
&lt;p&gt;
&lt;input type=&amp;quothidden&amp;quot name=&amp;quotMAX_FILE_SIZE&amp;quot value=&amp;quot&lt;?php echo $max;?&gt;&amp;quot&gt;
&lt;label for=&amp;quotfilename&amp;quot&gt;Select File:&lt;/label&gt;
&lt;input type=&amp;quotfile&amp;quot name=&amp;quotfilename&amp;quot id=&amp;quotfilename&amp;quot&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;input type=&amp;quotsubmit&amp;quot name=&amp;quotupload&amp;quot value=&amp;quotUpload File&amp;quot&gt;
&lt;/p&gt;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;از اونجایی که دسترسی به عناصر آرایه درونی در آرایه associative سوپر گلوبال FILES کمی دشواره، می‌تونیم به راحتی براش یک reference در نظر بگیریم؛ برای این کار از یک تابع built-in به اسم current استفاده می‌کنیم؛ خوبی این کار اینه که دیگه نیازی نیست که مقدار name مربوط به input صفحه html رو بدونیم:$uploaded = current($_FILES);
$uploaded[&#039;name&#039;];
$uploaded[&#039;type&#039;];
...به جای:&lt;?php
$_FILES[&#039;html_name_value&#039;][&#039;name&#039;];
$_FILES[&#039;html_name_value&#039;][&#039;type&#039;];
$_FILES[&#039;html_name_value&#039;][&#039;tmp_name&#039;];
$_FILES[&#039;html_name_value&#039;][&#039;error&#039;];
$_FILES[&#039;html_name_value&#039;][&#039;size&#039;];
?&gt;نحوه تعریف آرایه در php:$my_array = array();
$my_array = [];پارامترهای تابع str_replace:str_replace($search_for, $replace_with, $main_string); https://stackoverflow.com/questions/3828352/what-is-a-mime-type#:~:text=A%20MIME%20type%20has%20two,MIME%20type%20is%20application%2Fmsword. پارامترهای تابع strpos:strpos($main_string, $keyword);تابع pathinfo اصلا کاری به این نداره که مسیر یا فایلی که بهش دادیم؛ واقعا روی فایل سیستم ما باشه یا خیر، فقط اون رو برای ما parse می‌کنه:&lt;?php
print_r(pathinfo(&amp;quot/testweb/test.txt&amp;quot));
?&gt;

The output:

Array
(
[dirname] =&gt; /testweb
[basename] =&gt; test.txt
[extension] =&gt; txt
)منظور از needle و haystack چیه؟ یه چیز تو مایه‌های سوزن در خرمن! needle که همون سوزن هست و haystack هم خرمن، وقتی یک تابع میگه این needle هست، یعنی ما دنبال این سوزن توی haystack که یه حجم عظیمی هست می‌گردیم!تابع scandir میاد و مسیری که بهش می‌دیم رو روی فایل سیستم اسکن می‌کنه و لیست فایل‌ها و دایرکتوری‌های موجود رو در قالب یک آرایه ارائه میده:print_r(scandir(&#039;.&#039;));
output:
Array ( [0] =&gt; . [1] =&gt; .. [2] =&gt; file.txt [3] =&gt; index.php [4] =&gt; src )توی این آموزش یک تابع داشتیم که بخشی از solution مربوط به نام گذاری اسم های تکراریش برام جالب اومد، این قطعه کد رو این پایین می‌ذارم با یه توضیح مختصر در مورد اینکه چطور کار می‌کنه:&lt;?php
	protected function checkName($file)
	{
		$this-&gt;newName = null;
		$nospaces = str_replace(&#039; &#039;, &#039;_&#039;, $file[&#039;name&#039;]);
		if ($nospaces != $file[&#039;name&#039;]) {
			$this-&gt;newName = $nospaces;
		}
		$nameparts = pathinfo($nospaces);
		$extension = isset($nameparts[&#039;extension&#039;]) ? $nameparts[&#039;extension&#039;] : &#039;&#039;;
		if (!$this-&gt;typeCheckingOn &amp;&amp; !empty($this-&gt;suffix)) {
			if (in_array($extension, $this-&gt;notTrusted) || empty($extension)) {
				$this-&gt;newName = $nospaces . $this-&gt;suffix;
			}
		}
		if ($this-&gt;renameDuplicates) {
			$name = isset($this-&gt;newName) ? $this-&gt;newName : $file[&#039;name&#039;];
			$existing = scandir($this-&gt;destination);
			if (in_array($name, $existing)) {
				$i = 1;
				do {
					$this-&gt;newName = $nameparts[&#039;filename&#039;] . &#039;_&#039; . $i++;
					if (!empty($extension)) {
						$this-&gt;newName .= &amp;quot.$extension&amp;quot
					}
					if (in_array($extension, $this-&gt;notTrusted)) {
						$this-&gt;newName .= $this-&gt;suffix;
					}
				} while (in_array($this-&gt;newName, $existing));
			}
		}
	}
?&gt;بخش جالب این تابع برای من مربوط میشه به renameDuplicates که میاد اول با scandir کل دایرکتوری‌ها و فایل‌های موجود توی مسیر آپلود رو توی یک آرایه ذخیره می‌کنه، بعد میاد و چک می‌کنه که اسم فایلی که قراره آپلود بشه، آیا داخل آرایهٔ scandir هست یا خیر، اگر بود که حلقهٔ do...while ادامه پیدا می‌کنه و هر سری به آخر اسم فایل با counter عدد اضافه می‌کنه تا اینکه شرط نقض بشه و اسمی تولید بشه که توی لیست نیست.یک راه حل دیگه‌ای که اینجا بهش اشاره نکرد ولی یا خودم قبلا ازش استفاده کردم و یا اینکه جایی دیدم، اینه که به آخر اسم فایل timestamp رو اضافه کنیم.آپلود به صورت multiple و درک اینکه html و php چطور این موضوع رو هندل می‌کنند.در حالت single file ما یه همچین input ای داخل html داریم:&lt;input type=&amp;quotfile&amp;quot name=&amp;quotuploaded_file&amp;quot id=&amp;quotupload_file&amp;quot&gt;حالا برای اینکه به php بفهمونیم که ما قراره چندین فایل رو از طریق این فیلد آپلود کنیم؛ کافیه که یک جفت square brackets به انتهای پروپرتی name اضافه کنیم و همین‌طور attribute مربوط به آپلود چندگانه یعنی multiple رو هم اضافه کنیم که یک attribute جدید هست که توی html5 اضافه شده؛ بعنی اینجوری:&lt;input type=&amp;quotfile&amp;quot name=&amp;quotuploaded_file[]&amp;quot id=&amp;quotupload_file&amp;quot multiple&gt;اما نکته جالب اینه که سوپر گلوبال FILES خودش راهی برای هندل کردن این آپلود چندگانه داره و در حالت عادی شاید نخوایم که تغییری توی کدهای سمت سرور بدیم؛ php میاد و مشخصات فایل‌ها رو به شکل ترکیبی از associative و indexed مرتب می‌کنه:&lt;?php
Array
(
    [uploaded_file] =&gt; Array
        (
            [name] =&gt; Array
                (
                    [0] =&gt; Screenshot_2022-02-26_23-24-15:.png
                    [1] =&gt; Screenshot_2022-03-08_14-50-50.png
                    [2] =&gt; Screenshot_2022-05-23_09-27-39.png
                    [3] =&gt; Screenshot_2022-05-12_21-11-27.png
                    [4] =&gt; Screenshot_2022-03-30_22-50-28.png
                )

            [full_path] =&gt; Array
                (
                    [0] =&gt; Screenshot_2022-02-26_23-24-15:.png
                    [1] =&gt; Screenshot_2022-03-08_14-50-50.png
                    [2] =&gt; Screenshot_2022-05-23_09-27-39.png
                    [3] =&gt; Screenshot_2022-05-12_21-11-27.png
                    [4] =&gt; Screenshot_2022-03-30_22-50-28.png
                )

            [type] =&gt; Array
                (
                    [0] =&gt; image/png
                    [1] =&gt; image/png
                    [2] =&gt; image/png
                    [3] =&gt; image/png
                    [4] =&gt; image/png
                )

            [tmp_name] =&gt; Array
                (
                    [0] =&gt; /opt/lampp/temp/phptQVsL5
                    [1] =&gt; /opt/lampp/temp/phpne1RT1
                    [2] =&gt; /opt/lampp/temp/phpIQIBF5
                    [3] =&gt; /opt/lampp/temp/phpl4rKJ3
                    [4] =&gt; /opt/lampp/temp/phpM06yj3
                )

            [error] =&gt; Array
                (
                    [0] =&gt; 0
                    [1] =&gt; 0
                    [2] =&gt; 0
                    [3] =&gt; 0
                    [4] =&gt; 0
                )

            [size] =&gt; Array
                (
                    [0] =&gt; 441503
                    [1] =&gt; 431455
                    [2] =&gt; 381197
                    [3] =&gt; 366068
                    [4] =&gt; 335107
                )

        )

)
?&gt;برای iterate کردن key-value داخل php:&lt;?php
$my_array = [&#039;name&#039; =&gt; &#039;Aref&#039;, &#039;age&#039; =&gt; 25];
foreach($my_array as $key =&gt; $value){
    echo &amp;quotKey: &amp;quot.$key;
    echo &#039;&lt;br&gt;&#039;;
    echo &amp;quotValue: &amp;quot.$value;
    echo &#039;&lt;br&gt;&#039;;
    echo &#039;&lt;br&gt;&#039;;
}
?&gt;

output:

Key: name
Value: Aref

Key: age
Value: 25کلاس نهایی که برای آپلود فایل استفاده می‌کنیم؛ به شکل زیر خواهد بود:لینک pastebin به شکل هایلایت شده.&lt;?php
namespace foundationphp;

class UploadFile
{
	protected $destination;
	protected $messages = array();
	protected $maxSize = 51200;
	protected $permittedTypes = array(
			&#039;image/jpeg&#039;,
			&#039;image/pjpeg&#039;,
			&#039;image/gif&#039;,
			&#039;image/png&#039;,
			&#039;image/webp&#039;
	);
	protected $newName;
	protected $typeCheckingOn = true;
	protected $notTrusted = array(&#039;bin&#039;, &#039;cgi&#039;, &#039;exe&#039;, &#039;js&#039;, &#039;pl&#039;, &#039;php&#039;, &#039;py&#039;, &#039;sh&#039;);
	protected $suffix = &#039;.upload&#039;;
	protected $renameDuplicates;
	
	public function __construct($uploadFolder)
	{
		if (!is_dir($uploadFolder) || !is_writable($uploadFolder)) {
			throw new \Exception(&amp;quot$uploadFolder must be a valid, writable folder.&amp;quot);
		}
		if ($uploadFolder[strlen($uploadFolder)-1] != &#039;/&#039;) {
			$uploadFolder .= &#039;/&#039;;
		}
		$this-&gt;destination = $uploadFolder;
	}
	
	public function setMaxSize($bytes)
	{
		$serverMax = self::convertToBytes(ini_get(&#039;upload_max_filesize&#039;));
		if ($bytes &gt; $serverMax) {
			throw new \Exception(&#039;Maximum size cannot exceed server limit for individual files: &#039; .
	self::convertFromBytes($serverMax));
		}
		if (is_numeric($bytes) &amp;&amp; $bytes &gt; 0) {
			$this-&gt;maxSize = $bytes;
		}
	}
	
	public static function convertToBytes($val)
	{
		$val = trim($val);
		$last = strtolower($val[strlen($val)-1]);
		if (in_array($last, array(&#039;g&#039;, &#039;m&#039;, &#039;k&#039;))){
			switch ($last) {
				case &#039;g&#039;:
					$val *= 1024;
				case &#039;m&#039;:
					$val *= 1024;
				case &#039;k&#039;:
					$val *= 1024;
			}
		}
		return $val;
	}
	
	public static function convertFromBytes($bytes)
	{
		$bytes /= 1024;
		if ($bytes &gt; 1024) {
			return number_format($bytes/1024, 1) . &#039; MB&#039;;
		} else {
			return number_format($bytes, 1) . &#039; KB&#039;;
		}
	}
	
	public function allowAllTypes($suffix = null)
	{
		$this-&gt;typeCheckingOn = false;
		if (!is_null($suffix)) {
			if (strpos($suffix, &#039;.&#039;) === 0 || $suffix == &#039;&#039;) {
				$this-&gt;suffix = $suffix;
			} else {
				$this-&gt;suffix = &amp;quot.$suffix&amp;quot
			}
		}
	}
	
	public function upload($renameDuplicates = true)
	{
		$this-&gt;renameDuplicates = $renameDuplicates;
		$uploaded = current($_FILES);
		if (is_array($uploaded[&#039;name&#039;])) {
			foreach ($uploaded[&#039;name&#039;] as $key =&gt; $value) {
				$currentFile[&#039;name&#039;] = $uploaded[&#039;name&#039;][$key];
				$currentFile[&#039;type&#039;] = $uploaded[&#039;type&#039;][$key];
				$currentFile[&#039;tmp_name&#039;] = $uploaded[&#039;tmp_name&#039;][$key];
				$currentFile[&#039;error&#039;] = $uploaded[&#039;error&#039;][$key];
				$currentFile[&#039;size&#039;] = $uploaded[&#039;size&#039;][$key];
				if ($this-&gt;checkFile&#40;$currentFile&#41;) {
					$this-&gt;moveFile&#40;$currentFile&#41;;
				}
			}
		} else {
			if ($this-&gt;checkFile&#40;$uploaded&#41;) {
				$this-&gt;moveFile&#40;$uploaded&#41;;
			}
		}
	}
	
	public function getMessages()
	{
		return $this-&gt;messages;
	}
	
	protected function checkFile&#40;$file&#41;
	{
		if ($file[&#039;error&#039;] != 0) {
			$this-&gt;getErrorMessage($file);
			return false;
		}
		if (!$this-&gt;checkSize($file)) {
			return false;
		}
		if ($this-&gt;typeCheckingOn) {
		    if (!$this-&gt;checkType($file)) {
			    return false;
			}
		}
		$this-&gt;checkName($file);
		return true;
	}
	
	protected function getErrorMessage($file)
	{
		switch($file[&#039;error&#039;]) {
			case 1:
			case 2:
				$this-&gt;messages[] = $file[&#039;name&#039;] . &#039; is too big: (max: &#039; . 
				self::convertFromBytes($this-&gt;maxSize) . &#039;).&#039;;
				break;
			case 3:
				$this-&gt;messages[] = $file[&#039;name&#039;] . &#039; was only partially uploaded.&#039;;
				break;
			case 4:
				$this-&gt;messages[] = &#039;No file submitted.&#039;;
				break;
			default:
				$this-&gt;messages[] = &#039;Sorry, there was a problem uploading &#039; . $file[&#039;name&#039;];
				break;
		}
	}
	
	protected function checkSize($file)
	{
		if ($file[&#039;size&#039;] == 0) {
			$this-&gt;messages[] = $file[&#039;name&#039;] . &#039; is empty.&#039;;
			return false;
		} elseif ($file[&#039;size&#039;] &gt; $this-&gt;maxSize) {
			$this-&gt;messages[] = $file[&#039;name&#039;] . &#039; exceeds the maximum size for a file &#40;&#039;
					. self::convertFromBytes($this-&gt;maxSize&#41; . &#039;).&#039;;
			return false;
		} else {
			return true;
		}
	}
	
	protected function checkType($file) 
	{
		if (in_array($file[&#039;type&#039;], $this-&gt;permittedTypes)) {
			return true;
		} else {
			$this-&gt;messages[] = $file[&#039;name&#039;] . &#039; is not permitted type of file.&#039;;
			return false;
		}
	}
	
	protected function checkName($file)
	{
		$this-&gt;newName = null;
		$nospaces = str_replace(&#039; &#039;, &#039;_&#039;, $file[&#039;name&#039;]);
		if ($nospaces != $file[&#039;name&#039;]) {
			$this-&gt;newName = $nospaces;
		}
		$nameparts = pathinfo($nospaces);
		$extension = isset($nameparts[&#039;extension&#039;]) ? $nameparts[&#039;extension&#039;] : &#039;&#039;;
		if (!$this-&gt;typeCheckingOn &amp;&amp; !empty($this-&gt;suffix)) {
			if (in_array($extension, $this-&gt;notTrusted) || empty($extension)) {
				$this-&gt;newName = $nospaces . $this-&gt;suffix;
			}
		}
		if ($this-&gt;renameDuplicates) {
			$name = isset($this-&gt;newName) ? $this-&gt;newName : $file[&#039;name&#039;];
			$existing = scandir($this-&gt;destination);
			if (in_array($name, $existing)) {
				$i = 1;
				do {
					$this-&gt;newName = $nameparts[&#039;filename&#039;] . &#039;_&#039; . $i++;
					if (!empty($extension)) {
						$this-&gt;newName .= &amp;quot.$extension&amp;quot
					}
					if (in_array($extension, $this-&gt;notTrusted)) {
						$this-&gt;newName .= $this-&gt;suffix;
					}
				} while (in_array($this-&gt;newName, $existing));
			}
		}
	}
	
	protected function moveFile&#40;$file&#41;
	{
		$filename = isset($this-&gt;newName) ? $this-&gt;newName : $file[&#039;name&#039;];
		$success = move_uploaded_file&#40;$file[&#039;tmp_name&#039;], $this-&gt;destination . $filename&#41;;
		if ($success) {
			$result = $file[&#039;name&#039;] . &#039; was uploaded successfully&#039;;
			if (!is_null($this-&gt;newName)) {
				$result .= &#039;, and was renamed &#039; . $this-&gt;newName;
			}
			$result .= &#039;.&#039;;
			$this-&gt;messages[] = $result;
		} else {
			$this-&gt;messages[] = &#039;Could not upload &#039; . $file[&#039;name&#039;];
		}
	}
}توضیحات تکمیلی در مورد این کلاس و ویژگی‌های آن:امنیت:امکان محدود کردن حجم هر فایل برای آپلودچک کردن MIME هر فایل و تغییر نام فایل‌های مشکوکانعطاف‌پذیری:مدیریت فایل‌های single یا multipleتنظیمات راحت بدون نیاز به تغییر در سورستنظیمات پیش فرض:ماکزیمم حجم برای هر فایل ۵۰ کیلوبایت است.آپلود به فایل‌های تصویری محدود شدهاگر امکان آپلود همهٔ فایل‌ها را فعال کنیم؛ به انتهای فایل‌های ریسکی پسوند .upload اضافه می‌شود.فایل‌های با اسم یکسان، تغییر نام داده می‌شوند.سازگاری:فایل‌های برای سازگاری با فایل سیستم تغییر نام داده شده و space با _ جایگزین می‌شود.به php نسخه ۵.۳ یا بالاتر نیازهنحوهٔ استفاده از کلاس:باید namespace مورد نظر رو use کنیم و یک شی از کلاس UploadFile با یک آرگومان ورودی برای متد سازنده که در واقع مسیر آپلود فایل هست ایجاد کنیم.&lt;?php
use foundationphp\UploadFile;
$max = 5000 * 1024;
$result = array();
if (isset($_POST[&#039;upload&#039;])) {
    require_once &#039;src/foundationphp/UploadFile.php&#039;;
    $destination = __DIR__ . &#039;/uploaded/&#039;;
    try {
        $upload = new UploadFile&#40;$destination&#41;;
        $upload-&gt;setMaxSize($max);
        $upload-&gt;upload();
        $result = $upload-&gt;getMessages();
    } catch (Exception $e) {
        $result[] = $e-&gt;getMessage();
    }
}
?&gt;این کلاس، ۴ تابع public دارد:مورد اول setMaxSize هست که حداکثر حجم هر فایل رو مشخص می‌کنه. ست کردنش optional هست و مقدار دیفالت اون ۵۰ کیلوبایته:setMaxSize($bytes);مورد دوم تابع allowAllTypes هست که به ما این اجازه رو میده که به جای مقدار پیش فرض که فقط شامل تصاویر میشه، تمامی فایل‌ها رو بتونیم آپلود کنیم. یک پارامتر هم داره به اسم suffix که به صورت پیش فرض null هست و می‌تونیم یک مقداری رو پاس بدیم تا به انتهای فایل‌های مشکوک اضافه کنه:allowAllTypes($suffix = null);در حالت پیش فرض، پسوند .upload به انتهای فایل‌های ریسکی اضافه میشه، اما اگر یک رشتهٔ خالی با سینگل یا دابل کوت پاس بدیم؛ نام فایل هیچ تغییری نخواهد کرد.مورد سوم تابع upload هست که یک آرگومان ورودی می‌گیره و اون هم مقدار renameDuplicates هست که مقادیر boolean رو می‌پذیره و اگر فعال باشه، فایل‌هایی که با اسم مشابه روی فایل سیستم باشن رو تغییر نام میده و اگر غیر فعال باشه replace میشن. نکتهٔ دیگه اینکه این متد باید بعد از setMaxSize و allowAllTypes مورد استفاده قرار بگیره.upload($renameDuplicates = true);مورد چهارم تابع getMessages هست که یک آرایه از پیام های log شده رو برمی‌گردونه و باید بعد از متد upload فراخوانی بشه تا وضعیت و یا خطاهای رخ داده حین آپلود رو گزارش کنه.همین‌طور این کلاس، ۴ تابع public از نوع static داره، یعنی توابعی که بدون نیاز به ایجاد شی از کلاس با استفاده از فرمول ClassName::public_static_method قابل دسترسی هستند:مورد اول convertToBytes که می‌تونیم یک مقدار رو به بایت تبدیل کنه و بعدی convertFromBytes که مقدار داده شده رو human readable می‌کنه.توی php یه تابع جالب داریم به نام error_get_last که آخرین error های اتفاق افتاده رو به ما نشون میده که اینجا نمونه خروجی اون رو می‌بینیم:Array
(
    [type] =&gt; 2
    [message] =&gt; POST Content-Length of 1169141131 bytes exceeds the limit of 41943040 bytes
    [file] =&gt; Unknown
    [line] =&gt; 0
)توی php اگر به max_file_uploads برسیم و اقدام به آپلود تعداد بیشتری از این مقدار کرده باشیم، فقط تا همون تعداد آپلود میشن و بقیه discard خواهد شد؛ این رو تنها زمانی که پروسهٔ آپلود به خاتمه رسیده متوجه میشیم و این user-friendly نیست.یک راهکار برای اینکه جلوی این کار رو بگیریم و تازه بعد از اینکه کلی دیتا آپلود شد؛ بگیم مشکلی داشتیم و... اینه که محدودیت های سرور و php رو توی متغییرهای session ذخیره کنیم و به کاربر نشون بدیم. اما این هم زیاد جالب نیست و اگر طوری بود که اگر کاربر چیزی رو select می‌کرد و با محدودیت‌های ما مچ می‌شد فوراً هشدار میداد خیلی خوب بود؛ اما php یک زبان سمت server هست و توی این مورد کمک چندانی نمی تونه بکنه، بنابراین باید دست به دامان JS بشیم. البته استفاده از input با name ثابت MAX_FILE_SIZE برای سینگل فایل جوابه، اما برای آپلود چندگانه و Userfriendly سازی پروژه استفادهٔ ترکیبی از JS و PHP بهتره.یه نکتهٔ مهم در مورد attributeهای با فرمت:data-*=&amp;quot&amp;quot https://www.w3schools.com/tags/att_data-.asp فایل JS که برای مدیریت محدودیت‌های آپلود فایل در سمت کاربر طراحی شده:var field = document.getElementById(&#039;filename&#039;);
field.addEventListener(&#039;change&#039;, countFiles, false);

function countFiles(e) {
    if (this.files != undefined) {
        var elems = this.form.elements,
            submitButton,
            len = this.files.length, 
            max = document.getElementsByName(&#039;MAX_FILE_SIZE&#039;)[0].value,
            maxfiles = this.getAttribute(&#039;data-maxfiles&#039;),
            maxpost = this.getAttribute(&#039;data-postmax&#039;),
            displaymax = this.getAttribute(&#039;data-displaymax&#039;),
            filesize,
            toobig = [],
            total = 0,
            message = &#039;&#039;;

        for (var i = 0; i &lt; elems.length; i++) {
            if (elems[i].type == &#039;submit&#039;) {
                submitButton = elems[i];
                break;
            }
        }
        
        for (i = 0; i &lt; len; i++) {
            filesize = this.files[i].size;
            if (filesize &gt; max) {
                toobig.push(this.files[i].name);
            }
            total += filesize;
        }
        if (toobig.length &gt; 0) {
            message = &#039;The following file&#40;s&#41; are too big:\n&#039;
                    + toobig.join(&#039;\n&#039;) + &#039;\n\n&#039;;
        }
        if (total &gt; maxpost) {
            message += &#039;The combined total exceeds &#039; + displaymax + &#039;\n\n&#039;;
        }
        if (len &gt; maxfiles) {
            message += &#039;You have selected more than &#039; + maxfiles + &#039; files&#039;;
        }
        if (message.length &gt; 0) {
            submitButton.disabled = true;
            alert&#40;message&#41;;
        } else {
            submitButton.disabled = false;
        }
    }
}لیستی از تمامی MIME Types: https://www.iana.org/assignments/media-types/media-types.xhtml </description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Fri, 03 Jun 2022 14:36:17 +0430</pubDate>
            </item>
                    <item>
                <title>یادگیری مقدماتی PHPUnit</title>
                <link>https://virgool.io/@maslak/%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-phpunit-%DA%A9%D8%A7%D9%85%D9%84-%D9%86%D8%B4%D8%AF%D9%87-x2c4mfcqlcht</link>
                <description>تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.انواع تست‌ها:بر حسب اینکه بخوایم از چه زاویه‌ای به دسته‌بندی تست‌ها نگاه کنیم؛ ممکنه که انواع مختلفی از تست‌ها رو داشته باشیم؛ اما اینجا تست‌ها بر حسب «درجهٔ اختصاصی بودن» دسته‌بندی شده‌اند؛ بر اساس این نوع دسته‌بندی، ما ۴ نوع تست داریم:۱- تست واحد (Unit Testing):کوچک‌ترین واحد عملکردی برنامه را تست می‌کند. اگر بخوایم از دید یک دولوپر به قضیه نگاه کنیم؛ در واقع با انجام این نوع تست، قصد داریم که بفهمیم یک آیا یک واحد عملکردی (مثل یک تابع) از برنامه، دقیقا همون چیزی که ازش انتظار میره رو انجام میده یا خیر. این نوع از تست‌ها چون باید کوچکترین واحد عملکردی یک برنامه رو مورد آزمایش قرار بدن، باید تا حد ممکن از سایر توابع/کلاس‌های برنامه مجزا باشن. این نوع از تست‌ها باید داخل حافظه صورت بگیرن! یعنی چی؟ یعنی این نوع از تست‌ها نباید به دیتابیس، شبکه، فایل سیستم و... متصل باشند. این نوع از تست‌ها باید در سادگی کامل باشند.۲- تست مجتمع (Integration Testing):توی این سطح، چندین واحد از کد با هم ترکیب می‌شن و درستی این ادغام چک میشه. این نوع تست بر روی Unit Testing بنا شده و عملکرد چندین قسمت در کنار هم رو چک می‌کنه، مثلا اینکه آیا کلاس الف با کلاس ب به خوبی کار می‌کنه یا خیر؟۳- تست سیستم (System Testing):این مرحله از تست نرم‌افزار هم بر عهدهٔ توسعه دهنده هست و عملکرد کل سیستم رو در یک سیستم شبیه‌سازی از محیط واقعی تست می‌کنه تا مطمئن بشه، اون app نهایی که به دست مشتری میرسه، مشکلی نداره. در واقع سناریوهای واقعی رو شبیه‌سازی می‌کنه.۴- تست پذیرش (Acceptance Testing):این مرحله به وسیله کاربران انجام میشه، کاربر براش مهم نیست که جزئیات پیاده‌سازی توابع و واحدهای مختلف یک برنامه به چه صورتی هست؛ بلکه یک انتظار خاصی از برنامه داره که باید برآورده بشه.انوع مراحل تست نرم‌افزار بر اساس میزان اختصاصی بودنفریمورک PHPUnit چیه؟بر اساس هرم بالا می‌تونیم بگیم که تست واحد، بلوک یا واحد سازندهٔ تمامی تست‌های بعدی محسوب میشه. بنابراین وقتی پایه کار قوی و مستحکم باشه، در نهایت یک نرم‌افزار Solid خواهیم داشت. باید توجه داشته باشیم که نوشتن دستی تست‌ها و اجرای اون‌ها بعد از ایجاد هر تغییر در برنامه، خسته کننده هست.اما اگر ابزاری برای خودکارسازی این فرآیند وجود داشته باشد؛ نوشتن تست‌ها لذت‌بخش خواهد شد. در واقع PHPUnit برای حل این مشکل پا به عرصه گذاشته. این فریمورک در حال حاضر محبوب ترین فریمورک تست واحد در PHP هست. که از ویژگی‌هایی مثل شیء ساختگی، آنالیز پوشش کد، لاگ کردن و ... برخورداره.نصب PHPUnit:۱- دانلود: این فریمورک رو می‌تونیم دانلود کنیم که در قالب یک فایل PHAR یا آرشیو PHP ارائه میشه.۲- اضافه کردن این فریمورک به متغیر PATH سیستم. بعد از دانلود PHAR باید بهش دسترسی execute بدیم و بعدش به PATH اضافه کنیم:PATH=$PATH:~/opt/binتوی لینوکس می‌تونیم به شکل ساده این پروسه رو طی کنیم:wget https://phar.phpunit.de/phpunit.phar
chmod +x phpunit.phar
sudo mv phpunit.phar /usr/local/bin/phpunit
phpunit --versionاما راه ساده تری هم هست؛ کافیه با Package Manager نصبش کنیم:sudo apt install phpunitاولین تست واحد:برای اینکه اولین تست خودمون رو بنویسم نیاز به یک کلاس خیلی ساده داریم:File: Calculator.php&lt;?php
class Calculator
{
    public function add($a, $b)
    {
        return $a + $b;
    }
 }یک فایل ایجاد کردیم به اسم Calculator.php که داخل یک کلاس داریم به همون اسم که تنها یک متد داره و جمع دو تا پارامتر a و b رو برمی‌گردونه.حالا برای تست این کلاس باید یک فایل ایجاد کنیم به اسم CalculatorTest.php و کدهای زیر رو توش قرار بدیم (کد اولیه، ظاهرا کمی قدیمی بود و با نسخه جدید PHPUnit هم‌خوانی نداشت؛ بنابراین void به نوع تابع اضافه شده؛ این قضیه اینجا توضیحش اومده)، به علاوه باید TestCase رو use کرد که اینجا توضیح داده.&lt;?php
use PHPUnit\Framework\TestCase;
require &#039;Calculator.php&#039;;

class CalculatorTests extends TestCase
{
    private $calculator;
    protected function setUp(): void
    {
        $this-&gt;calculator = new Calculator();
    }

    protected function tearDown(): void
    {
        $this-&gt;calculator = NULL;
    }

    public function testAdd()
    {
        $result = $this-&gt;calculator-&gt;add(1, 2);
        $this-&gt;assertEquals(3, $result);
    }

}توی قطعه کد بالا، فایل Calculator.php رو require کردیم. در واقع همون کلاسی هست که قصد داریم تستش کنیم.متد SetUp قبل از اینکه تست‌های ما ران بشه، اجرا میشه، باید دقت کنیم که این متد قبل از ران شدن هر تستی ران میشه، یعنی اینکه اگر یک متد تست دومی هم داشته باشیم، قبل از اینکه اون تست دوم ران بشه، یک بار دیگه هم این متد اجرا میشه، بعدش میره سراغ تست.متد tearDown هم مثل setUp هست با این تفاوت که بعد از پایان کار هر متد ران میشه.متد testAdd، متد add از کلاس Calculator رو تست می‌کنه، فریمورک PHPUnit هر متدی که با test شروع بشه رو به عنوان تست در نظر می‌گیره و به شکل اتوماتیک اجراشون می‌کنه. این متد خیلی ساده هست، اول تابع Calculator.add رو فراخوانی می‌کنیم تا مقدار ۱ و ۲ رو محاسبه کنه و بعد با استفاده از تابع assertEquals انتظار خودمون رو از نتیجه اعلام می‌کنیم، یعنی آرگومان اول که ۳ هست، انتظاری هست که از جمع ۱ و ۲ داریم و آرگومان دوم نتیجه‌ای که تابع add ما حساب کرده.برای تست:phpunit CalculatorTest.phpاگر تست بدون مشکل باشه، این تصویر یا مشابه اون رو خواهیم دید:نتیجهٔ PHPUnitقسمت دوم: ارائه دهندهٔ داده (Data Provider):چه زمانی باید از data provider در کد خود استفاده می‌کنیم؟زمانی که یک تابع می‌نویسیم، می‌خوایم اطمینان حاصل کنیم که با فراهم آوردن ورودی‌های مختلف برای اون، جواب‌های خاصی رو دریافت کنیم. حالا اگر چیزی به اسم data provider وجود نداشته باشیم و ما بخوایم که اون تابع رو به ازای مقادیر مختلفی تست کنیم، باید برای هر یک از اون حالات یک تست بنویسیم که چیز جالبی نیست و نمونهٔ اون رو می‌تونیم ببینیم:&lt;?php
use PHPUnit\Framework\TestCase;
require &#039;Calculator.php&#039;;

class CalculatorTests extends TestCase
{
        private $calculator;

        protected function setUp(): void
        {
            $this-&gt;calculator = new Calculator();
        }

        protected function tearDown(): void
        {
            $this-&gt;calculator = NULL;
        }

        public function testAdd()
        {
            $result = $this-&gt;calculator-&gt;add(1, 2);
            $this-&gt;assertEquals(3, $result);
        }

        public function testAddWithZero()
        {
            $result = $this-&gt;calculator-&gt;add(0, 0);
            $this-&gt;assertEquals(0, $result);
        }

        public function testAddWithNegative()
        {
            $result = $this-&gt;calculator-&gt;add(-1, -1);
            $this-&gt;assertEquals(-2, $result);
        }

}بنابراین برای جلوگیری از تکرار، می‌تونیم از data provider استفاده کنیم.چطور از Data Provider استفاده کنیم؟یک متد data provider یک آرایه از آرایه‌ها یا یک شی که Iterator interface رو پیاده‌سازی می‌کنه، برمی‌گردونه. متد تست به کمک annotation متد data provider خودش رو فراخوانی می‌کنه و مقادیر رو آرایه‌ها رو به عنوان آرگومان دریافت می‌کنه.یک سری نکات مهم در مورد data provider وجود داره:متد data provider باید public باشه.این متد یک آرایه از دیتای جمع آوری شده برمی‌گردونهاین متد از annotation برای فراخوانی متد data provider خودش استفاده می‌کنهخب، برای استفاده از data provider کافیه که یک متد public بسازیم که یک آرایه از آرایه‌ها رو به عنوان آرگومان به متد تست پاس میده. بعدش باید به متد تست خودمون annotation اضافه می‌کنیم تا به PHPUnit بگیم که کدام متد data مورد نیاز متد تست رو فراهم می‌کنه.&lt;?php
use PHPUnit\Framework\TestCase;
require &#039;Calculator.php&#039;;
 
class CalculatorTests extends TestCase
{
    private $calculator;
 
    protected function setUp(): void
    {
        $this-&gt;calculator = new Calculator();
    }
 
    protected function tearDown(): void
    {
        $this-&gt;calculator = NULL;
    }
 
    public function addDataProvider() {
        return array(
            array(1,2,3),
            array(0,0,0),
            array(-1,-1,-2),
        );
    }
 
    /**
     * @dataProvider addDataProvider
     */
    public function testAdd($a, $b, $expected)
    {
        $result = $this-&gt;calculator-&gt;add($a, $b);
        $this-&gt;assertEquals($expected, $result);
    }
 
}نکتهٔ مهم: نحوهٔ کامنت کردن یک annotation هم خیلی مهمه و باید از فرمول بالا پیروی کنه.</description>
                <category>عارف</category>
                <author>عارف</author>
                <pubDate>Tue, 31 May 2022 20:45:29 +0430</pubDate>
            </item>
            </channel>
</rss>