وصیت نامه لاراول

حدود دوساله که با این پدیده بی نظیر در پی اچ پی سر و کله میزنم و میتونم بگم واقعا یه شاهکاره.به خصوص بعد اینکه متوجه این سبک عمو تیلور برای کامنت نویسی در کد ها شدم واقعا شیفته لاراول شدم.این حالت شبیه چاقو تقریبا همه جای سورس لاراول هست و واقعا قابل تحسینه این حجم از ظرافت.و خب میشه گفت من اوایل که پی اچ پی شروع کرده بودم فاز ساختن سیستم مدیریت محتوای خودم رو از پایه داشتم. بعدش هم فاز ساخت فریمورک خودم رو داشتم. ولی لاراول به خوبی حالیم کرد من از فریمورک ساختن فقط توهم ش رو داشتم!حالا از اونجایی که من در جایگاهی نیستم که بخام به کسی نصیحت کنم میخام یکم براتون وصیت بنویسم (به خصوص برای تازه کار ها) که یه سری چیز ها تو لاراول هست که باید ازشون استفاده کنین و در راه درست استفاده کنین.(که وقتی من لاراول رو شروع کرده بودم ای کاش یکی بود و به من میگفت)استفاده از Eloquentتازه کارهایی رو دیدم با اینکه به لاراول اومدن ولی هنوز به همون روش سنتی و استفاده از چیزایی مثل`DB::statement` , `DB::raw` کارشون رو پیش میبرن در صورتی که Eloquent یکی از قوی ترین و مهم ترین ویژگی های لاراول هست.امااستفاده درست از قابلیت های Eloquentاول اینکه حتما بهتون توصیه میکنم از Relationهای لاراول بهره ببرید و روش سنتی دستی کد زدن رو کنار بذارین.یکی از نکاتی که در مورد ریلیشن ها هست قابلیتی به نامEagerloadهست.اماEagerloadچطوری میتونه باعث افزایش/کاهش سرعت سایت بشه؟برای مثال من یک وبلاگ میسازم. یک صفحه لیست مطالب دارم. حالا کنار عنوان هر پست دسته بندی اون هم آورده میشه. هر دسته بندی چند پست داره و هر پست فقط یه دسته بندی پس رابطه بین شون یک به چند هست.در اینجا داخل حلقه foreach به ازای نمایش دسته بندی هر پست یه کوئری به جدول دسته بندی ها زده که حتی در مواردی اون کوئری تکراری هست.اگه دقت کنین تو این مورد خاص 45 کوئری به دیتابیس زده شده (همه کوئری ها به خاطر دسته بندی نیست فقط میخام مقایسه کنم).من دسته بندی هر پست رو تقریبا همه جای سایت لازم دارم پس با اضافه کردن متغیر$withبه مدل پستEagerloadرو برای دسته بندی هر پست به صورت خودکار فعال میکنم. https://gist.github.com/amir9480/d2b3719f53b0d04a9053a0e789d6e308#file-post-php خب حالا دوباره همون صفحه رو باز میکنم تا مقایسه انجام بدم. این بار یک بار برای همیشه به جدول دسته بندی ها کوئری میزنه و در همه ی پست ها دسته بندی ها از قبل لود شدن و همچنین از 45 کوئری به 26 کوئری رسوندیمش اما لزوما این روش همیشه بهینه نیست.هر پست چند کامنت داره و هر کامنت برای یه پست هست پس رابطه بین شون یک به چند هست.اگه ایگر لود برای کامنت ها به این شکل انجام بدیم هر پست کلی کامنت داره و همشون باهم لود بشن تو این صفحه ازشون هیچ استفاده ای نمیشه و فقط الکی حافظه رو پر میکنن.اینطوری نه تنها سرعت رو بهتر نمیکنیم بلکه قهوه ای ترش هم میکنیم.برای کامنت به جای این روش داخل کنترل نمایش پست تکی که کامنت ها داخل باید نمایش داده بشنEagerload رو انجام میدم.BlogPost::where('slug', $value)->firstOrFail()حالا اگه به جای کد بالاBlogPost::where('slug', $value)->with('comments')->firstOrFail()بنویسم تغییری در تعداد کوئری ها نمیبینم چون وقتی کامنت ها رو داخل حلقه میندازم در هر صورت یه بار کوئری میخوره و با ایگر لود هم بازم یه بار کوئری میخوره اما یه فرقی دارناگه شما نتیجه کارتون رو کش کنین بعد کش دیگه برای کامنت کوئری زده نمیشه ولی اگه eagerloadرو انجام ندین حتی بعد کش کردن یک بار کامنت ها کوئری میخورن.حالا هر کامنت یه تعداد جواب داره و هر جواب خودش یه کامنت هست. پس کامنت با خودش رابطه یک به چند داره. برای اینکه ماeagerloadرو برای جواب کامنت ها از طریق پست انجام بدیم اینکارو میکنیم.BlogPost::where('slug', $value)->with('comments.replies')->firstOrFail();خب کوئری ها کاهش پیدا کرد اما تو سایت من ممکنه جواب ها چند سطحی باشن یعنی ممکنه یه جواب خودش یه جواب دیگه داشته باشه و ... این کار رو انجام میدمBlogPost::where('slug', $value)->with('comments.replies.replies.replies.replies')->firstOrFail();اینم نتیجه ولی خب انصافا 133 کوئری کجا و 33 کوئری کجا.Eagerloadمثل شمشیر دولبه س.میتونه پرفورمنس رو از قهوه ای به طلایی برسونه یا میتونه قهوه ای رو قهوه ای تر کنه!قابلیت مهم بعدی که میخام بهتون درباره ش بگم مدیریت ایونت ها هستش که به نظرم راحت ترین روش مدیریت ایونت مدل های لاراول استفاده از Observerهست.خب این ایونت چی هست و چیکار میکنه.خیلی ساده بخام بگم این یعنی به لاراول میگین مثلا این مدل من اگه ازش یدونه ساخته شد این کار رو واسم انجام بدهاگه مقادیر داخلش به روز رسانی شد اون کار رو انجام بدهاگه هم این مدلم حذف شدش فلان کار رو انجام بده. و ....حالا میخام یه مثال بزنم که در مورد همین کامنت هست.اول اینکه میخایم IP کسی که کامنت رو میفرسته ذخیره کنیم.دوم اینکه میخایم وقتی کامنت حذف شد جواب هایی که برای اون کامنت بودن هم حذف بشه.برای مثال من https://gist.github.com/amir9480/71bd3e4f48a403eb2abe4f9605326a09 و برای ثبت این Observerدر تابع bootدر کلاسapp/Providers/EventServiceProvider.phpاین خط کد رو اضافه میکنم.BlogComment::observe(BlogCommentObserver::class);حالا دیگه لازم نیست هر جایی که کامنت اضافه میکنم هر بار آی پی کاربر رو خودم دستی همونجا مقدار بدم یا وقتی کامنت رو حذف میکنم دیگه دستی برای حذفش جواب ها دوباره کد اضافه بنویسم. کارهای عادی رو انجام میدم و اینا هم خودشون کارهای لازم رو انجام میدن برام.اماGlobal scopeقابلیت باحالیه که بهتون اجازه میده هر وقت به مدلتون کوئری میزنین یه چیزی به کوئری اضافه کنهمثلا میتونین قابلیت بذارین در قسمت پست ها همیشه بر اساس تاریخ ساخته شدن مرتب بشن و دیگه فرقی نمیکنه شما کجا از مدلتون استفاده کنین. همیشه بر اساس تاریخ مرتبشون میکنه حالا برای مثال کامنت ها همیشه از من قابلیتی میخان که کامنت ها توسط ادمین ها تایید بشن بعد تو سایت نمایش داده بشن.این کار به راحتی توسط global scopeقابل انجام هست و دیگه نیاز نگران سوتی دادن یه جایی باشین و از دستتون در بره. مثلا اگه جواب های یه کامنت رو براش شرط نذارین کامنت های تایید نشده هم بالا میاد اما باglobal scopeخیالتون از همه جهت راحته. https://gist.github.com/amir9480/246ed40d4c5e0110d5aa751d41ff86c3#file-blogpost-php خب حالا همیشه هر جا کوئری بزنین خودش یه ->where('confirmed', 1)بهش اضافه میکنه. خب حالا این جا یه مشکلی پیش میاد و اونم اینه که ما برای اینکه به ادمین اجازه بدیم کامنت های تایید نشده رو ببینه و بتونه تایید کنه.کافیه دقیقا فقط قسمت هایی که میخایم استثنا باشن رو باwithoutGlobalScopeبراش استثنا مشخص کنیم.BlogComment::withoutGlobalScope('confirmed')->paginate();قابلیت بعدی که میخام باهاتون در میون بذارمcastکردن هستفرض کنین میخایم تو کامنت ها به مقدارconfirmedدسترسی داشته باشیم.در حالت عادی مقدارش به شکل 0 , 1 هست.اما درست ترش اینه که یه بولین باشه برای با اضافه کردن متغیر$castsبه مدلمون هست https://gist.github.com/amir9480/0269df40e3c4733e387c4aa709bfc6f0#file-blogpost-php حالا از این به بعد confirmedخودش خود به خود تبدیل به بولین میشه و دو مقدار true, falseهمراه خودش دارهشاید بگین این که درد خاصی از من دوا نمیکنه شاید راست بگین اما خفن ترین تبدیل نوعی که میتونه انجام بده تبدیل آرایه هست.برای تبدیل آرایه باید نکاتی رو درنظر بگیرین.آیا اون آرایه ای که میخاین ذخیره بشه سرچ کردن/ مرتب کردن بر اساسش اهمیت داره یا نه و دوم اینکه تعدادشون خیلی زیاد هست یا نه.اگه سرچ براتون مهم نیست و تعدادشون هم اونقدرها زیاد نیست پس به جای ساخت یه مدل اضافه و شلوغ کردن کد از این قابلیت خفن استفاده کنین.بیشترین جایی که از این قابلیت استفاده کردم ساخت گالری عکس بوده.فرض کنین برای هر پست یه تعداد عکس داریم.اول کست رو به مدل پست اضافه میکنیم.protected $casts = [ 'images' => 'array' ];و بعد جایی که داریم ذخیره میکنیم یه آرایه بهش میدیم.$post->images = ['image1.png', 'image2.jpg'];و به همین راحتی ما قابلیت ذخیره کردن یه آرایه تو دیتابیس رو بدون درد کشیدن داریم!قابلیت بعدی که میخام باهاتو در میون بذارمAccessor هست. این قابلیت بهتون اجازه میده یه تعداد فیلد اضافه کنین به مدلتون بدون اینکه نیاز باشه تو دیتابیس دستکاری کنین. بیشترین جایی که از این قابلیت استفاده کردم ذخیره لینک هر چیزی داخلش بوده.یکی از رو اعصاب ترین کارایی که معمولا در کد اطرافیانم میبینم استفاده از route همه جا هست.{{ route('test', ['post' => $post->slug]) }}یا مثلا همه جا برای دسترسی به لینک فایل آپلود شده دستی آدرس رو بهش اضافه میکنن.همه جا هی کپی کپی کپی . ? https://gist.github.com/amir9480/494b19b5cb0fce3604b28adfd425f643#file-blogpost-php حالا برای دسترسی به لینک و آدرس عکسی کافیه به این شکل عمل کنیم.{{ $post->link }}{{ $post->image_link }}بهترین مزیتی که داره اینه که اگه یه روزی مجبور بشین لینک یا آدرس عکس رو عوض کنین دیگه لازم نیست برید تک تک جاهایی که کدتون رو کپی کردین دونه به دونه تغییر بدین و دهنتون آسفالت شه. آینده نگری و DRY که میگن از همین چیزای کوچیک شروع میشه. بازم اگه عمر و انگیزه ای بود وصیت های بی خاصیت بیشتری رو باهاتون در میون میذارم. https://virgool.io/@amiralizadeh9480/%DA%86%DB%8C-%D8%B4%D8%AF-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3-%D8%B4%D8%AF%D9%85-kpsescdv5rci https://virgool.io/@amiralizadeh9480/%D8%B3%D8%A7%D8%AE%D8%AA-%D9%81%D8%B1%D9%85-%D8%AA%D9%85%D8%A7%D8%B3-%D8%A8%D8%A7-%D9%85%D8%A7-%D8%B3%D8%A7%D8%AF%D9%87-%D8%AF%D8%B1-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-nlkf9yro0a0c https://virgool.io/@amiralizadeh9480/%D8%A7%D9%81%D8%B2%D9%88%D9%86%D9%87-%D9%85%D9%86-%D8%A8%D8%B1%D8%A7%DB%8C-vscode-%D9%88-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-mw9bcpvidbrd