یادگیری مقدماتی لاراول - پارت سیزدهم

تذکر: این یک پست آموزشی برای عموم نیست! بلکه تنها جایی برای یادداشت‌های من حین یادگیریه تا بهتر به خاطر بسپارم و در صورت لزوم به اون‌ها مراجعه کنم.

قسمت ۱۷۵ تا ۱۸۹

آشنایی با تغییرات و ویژگی های جدید Laravel 9:

لاراول ۹ به php نسخه ۸ یا بالاتر نیاز داره.

توی نسخهٔ ۹ پوشهٔ lang از پوشهٔ resources خارج و در root پروژه قرار گرفته.

مبحث Controller Route Group:

در واقع این ویژگی به ما کمک می‌کنه تا از تکرار اسم کنترل برای Routeهای مختلف جلوگیری بشه. فرض کنیم که یک کنترلر داریم به اسم PostController و براش Routeهای زیر رو تعریف کردیم:

حالا برای سادگی کار توی ورژن ۹ چیزی اومده به اسم Controller Route Group که از تکرار اسم Controller جلوگیری می‌کنه:

حالا اگر route:list بگیریم؛ این رو خواهیم داشت:

مایگریشن‌های ناشناس:

کلاس‌های ناشناس در ورژن ۷ php معرفی شدن، اما لاراول نسخهٔ ۹ اومده و از اون‌ها داخل migrationها استفاده کرده، این کلاس‌ها از نظر عملکردی هیچ تفاوتی با کلاس‌های با اسم ندارن و این حذف شدن اسم از ساختار این کلاس‌ها از بروز تداخل در پروژه‌های بزرگ جلوگیری می‌کنه.

قبلا ساختار کلاس migration اینطوری بود:

اما الان اینطوری شده:

آشنایی با Helper Functionهای جدید:

str('Aref')->append(' Alikhani'); //returns: 'Aref Alikhani'
str('ArefAlikhani')->snake(); //returns: aref_alikhani


یک متد جدید هم اومده برای redirect کردن، برای redirect می‌تونیم از راه‌های زیر استفاده کنیم:

و یا ست کردن یک name برای یک route و استفاده از متد route مربوط به redirect:

اما با اومدن متد to_route به جای اینکه بنویسیم:

return redirect()->route('home');

کافیه بنویسیم:

return to_route('home');

در واقع تابع 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=&quotLaravel\Scout\ScoutServiceProvider&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('text')->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->update(['status' => 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 ما هست: