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

حدود دوساله که با این پدیده بی نظیر در پی اچ پی سر و کله میزنم و میتونم بگم واقعا یه شاهکاره.

به خصوص بعد اینکه متوجه این سبک عمو تیلور برای کامنت نویسی در کد ها شدم واقعا شیفته لاراول شدم.

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

ولی لاراول به خوبی حالیم کرد من از فریمورک ساختن فقط توهم ش رو داشتم!
حالا از اونجایی که من در جایگاهی نیستم که بخام به کسی نصیحت کنم

میخام یکم براتون وصیت بنویسم (به خصوص برای تازه کار ها) که یه سری چیز ها تو لاراول هست که باید ازشون استفاده کنین و در راه درست استفاده کنین.(که وقتی من لاراول رو شروع کرده بودم ای کاش یکی بود و به من میگفت)

استفاده از 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