یکی از امکانات مهم در لاراول، قابلیت Eager Loading است. از طریق این قابلیت میتوانیم کوئریهای بهینهتری پیاده سازی کنیم تا در زمان دریافت اطلاعات از دیتابیس صرفهجویی کنیم. این کار باعث ارتقاء سرعت برنامه و همچنین دریافت نکردن اطلاعات اضافی در هنگام بارگذاری اطلاعات میشود. میتوانیم فقط اطلاعاتی که نیاز داریم را دریافت کنیم و مشکلاتی از قبیل N+1 را رفع کنیم. در ادامه، بهینه سازی روابط در لاراول با Eager loading را با یک مثال عملی به شما نشان میدهیم با ما همراه باشید.
اگر به یادگیری بیشتر لاراول علاقه داری میتوانی در دوره آموزش لاراول کاربردی (بسته پروژه محور) شرکت کنی، این دوره شامل ۱۲ پروژه کاربردی و پر استفاده در دنیای واقعی است، که تمامی پروژهها به صورت کامل برنامه نویسی خواهند شد، تا دانشجو بتواند با روند ایجاد و تکمیل پروژه به صورت کامل آشنا شود.
چرا برنامه نویسان از Eager loading در برنامه نویسی استفاده نمیکنند؟
شاید یکی از مهمترین سؤالاتی که برای شما پیش آمده باشد این است که چرا برنامه نویسان معدودی از امکان Eager loading در لاراول استفاده میکنند؟ همانطور که میدانید یکی از غنیترین منابعی که برای یادگیری فریم ورک لاراول وجود دارد، سایت اصلی این فریم ورک میباشد و بسیاری از برنامه نویسانی که از این فریم ورک برای برنامه نویسی استفاده میکنند برنامه نویسی با فریم ورک لاراول را از این سایت یاد میگیرند. اما در سایت اصلی فریم ورک لاراول توضیحات چندانی راجع به Eager loading در لاراول داده نشده است. (در نسخهی جدید آن یعنی 7.12 به طور کاملتری توضیح داده شده است) به همین علت بسیاری از برنامه نویسان نتوانستهاند این موضوع را درک کنند و همین عامل باعث شده است که لزوم یادگیری و استفاده از آن را در برنامههایی که مینویسند درک نکنند، به همین علت در این مطلب قصد داریم توضیحاتی راجع به استفاده از این ابزار ارائه دهیم.
خطای رایج N+1 چیست؟
قبل از این که بخواهیم در رابطه با Eager loading در لاراول صحبت کنیم بهتر است با خطای رایج N+1 آشنا شویم. یکی از معمولترین خطاها در حین برنامه نویسی با فریم ورک لاراول، خطای N+1 میباشد. این خطا زمانی رخ میدهد که برنامه نویس یک رکورد مشخص را واکشی کرده و به دنبال آن فریم ورک لاراول خود به خود N کوئری را به اجرا در میآورد که دلیل این کار این است که فریم ورک لاراول قصد دارد رابطههای بین جدولها را از روی بانک اطلاعاتی به اجرا در بیاورد. در ادامه قصد داریم یک مثال واضح و آسان دربارهی این موضوع ارائه دهیم که به طور کامل متوجه این موضوع شوید.
مثالی برای خطای N+1
فرض کنید در طول برنامه نویسی با فریم ورک لاراول دو مدل را ایجاد کردهاید، مدل اول با نام student و مدل دوم با نام book ایجاد شده است، رابطهای که بین این دو جدول وجود دارد به این صورت است که هر دانش آموز میتواند چند کتاب داشته باشد. همانطور که میدانید برای این کار تنها کافی است یک رابطه بین مدل students و book ایجاد کنیم:
public function books(){
$this->hasMany(Book::class);
}
حال برای دریافت اطلاعات تنها کافی است کدهای زیر را وارد کنید:
همانطور که مشاهده میکنید در این مثال تمامی اطلاعات دانش آموزان دریافت میشود و بعد از آن N کوئری دیگر نیز اطلاعات کتابها را به صورت تک تک دریافت میکنند که این در واقع همان خطای N+1 است که در صورت بزرگ بودن جداول میتواند وب سایت شما را به خطر بیاندازد. در ادامه به بررسی یک روش بسیار جالب که همان استفاده از Eager loading در لاراول میباشد خواهیم پرداخت تا بتوانید این مشکل را حل کنید.
Eager loading در لاراول چیست؟
Eager loading در لاراول در واقع راه حلی بسیار مناسب برای از بین بردن خطای N+1 در برنامه نویسی میباشد. در واقع Eager loading در لاراول راه حلی بسیار مناسب را به شما پیشنهاد میدهد که به کمک آن میتوانید خطای N+1 را که در مثال قبلی توضیح دادیم حل کنید. برای این که بهتر با مفهوم Eager loading در لاراول آشنا شوید بهتر است این مفهوم را نیز با یک مثال به صورت کامل توضیح دهیم.
یک مثال از Eager Loading در لاراول
در این مطلب قصد داریم مثالی را برای این مورد ارائه دهیم:
همانطور که میدانید لاراول با استفاده از دستور with متوجه میشود که باید به صورت همزمان اطلاعات را از لیستی که نام دانش آموزان در آن قرار دارد دریافت کرده و به نمایش در بیاورد، از آن جایی که لاراول دارای معماری ORM میباشد، تنها دو کوئری را دریافت میکند. همانطور که میتوانید در این مورد مشاهده کنید، استفاده از Eager loading در لاراول روش بسیار بهینهتری نسبت به سایر روشها ( که یکی از آنها را در قسمت قبلی بیان کردیم ) میباشد.
لود کردن روابط بیشتر با Eager loading در لاراول
فرض کنید که میخواهید از اطلاعات تمامی کلاسهایی که یک دانش آموز خاص در آن قرار دارد باخبر شوید. در این صورت میتوانید از قطعه کد زیر برای این کار استفاده کنید:
اگر برای هر کلاس، چند معلم را تعریف کرده باشیم و نیاز باشد که به رابطهی بین کلاس و معلم هم دسترسی داشته باشیم، میتوانیم به شکل زیر معلمان هر کلاس را نیز لود کنیم:
در اینجا، از رابطهی books، ما فقط ستونهای id و title را از دیتابیس گرفتهایم.
لود رابطه به صورت پیشفرض
ممکن است نیاز داشته باشید که موقع دسترسی به یک مدل، رابطه یا روابط خاصی را بدون نیاز به استفاده از متد ()with، همواره لود کنید. برای این حالت میتوانید خصوصیت with$ را به صورت protected و با مقدار آرایه به شکل زیر تعریف کنید:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Student extends Model
{
/**
* The relationships that should always be loaded.
*
* @var array
*/
protected $with = ['books'];
/**
* Get all the books that the student has.
*/
public function books()
{
return $this->hasMany('App\Models\Book');
}
}
اگر برای یک کوئری خاص نیاز داشتید که رابطهی موردنظرتان را از خصوصیت with$ خارج کنید تا لود نشود، میتوانید به شکل زیر عمل کنید:
گاهی اوقات نیاز دارید محدودیتها یا شرایطی را بر رابطهای که میخواهید لود شود، اعمال کنید. برای این کار میتوانید از اکثر متدهای Query Builder مانند مثال زیر استفاده کنید:
در مثال بالا رابطهی books فقط کتابهایی را شامل شود که در title آنها از کلمهی سیب استفاده شده باشد و این کتابها برحسب ستون order و به صورت نزولی مرتب میشوند. دقت داشته باشید که هنگام اعمال محدودیت بر روابط از متدهای ()limit و ()take نمیتوانید استفاده کنید.
Lazy Eager Loading در لاراول
گاهی ممکن است که فقط تحت شرایط خاصی نیاز به لود رابطه داشته باشید. برای این کار میتوانید به شکل زیر عمل کنید:
$students = App\Models\Student::latest()->get();
if ($condition) {
$students->load('books', 'classes');
}
اگر در این حالت نیاز داشته باشید شرایطی را بر رابطه اعمال کنید:
$students = App\Models\Student::latest()->get();
if ($condition) {
$students->load(['books' => function ($query) {
$query->latest();
}, 'classes']);
}
اگر مطمئن نیستید که رابطه لود شده باشد و میخواهید در صورتی که لود نشده، آن را لود کنید، باید به شکل زیر عمل کنید:
بعد از این که با مفهوم Eager loading در لاراول آشنا شدید، بهتر است از این امکان و ابزار فوق العادهای که در فریم ورک لاراول برای شما فراهم شده، بیشتر استفاده کنید تا برنامههایی کارآمد را طراحی کنید. در واقع شما تا به امروز به دلیل این که با این مفهوم آشنایی کاملی نداشتید، نمیتوانستید از آن در برنامههای خود استفاده کنید به همین دلیل برنامههایی که به وسیلهی لاراول طراحی میکنید دچار مشکلاتی میشوند که یکی از مهمترین این مشکلات را میتوان همان مشکل معروف N+1 دانست. حال که با مفهوم Eager loading در فریم ورک لاراول آشنا شدید، میتوانید بهراحتی این مشکل را حل کنید.
۶ دیدگاه
مهیار۱۰ مهر ۱۴۰۲، ۰۶:۵۰
سلام وقت بخیر
ممنون بابت مقاله خیلی خوبی که نوشتید
میشه لطفا در مورد preventLazyLoading توضیح بدید
نازنین کریمی مقدم۲۲ مهر ۱۴۰۲، ۰۸:۳۰
درود
پیشنهادتون رو بررسی میکنیم و در صورت امکان مقاله ای رو در تقویم محتوایی مون قرار میدیم.
ممنون که با ما همراه هستید.
cryptodev۰۱ اسفند ۱۳۹۹، ۱۲:۰۸
ممنون بابت مقاله خوبتون، یه سوالی برای بنده پیش اومد
تفاورت Eager و Lazy و حالت معمولی که قبل Eager توضیح دادید در چیه؟
در کل Eager که از همه بهتر و کارآمدتره، ولی خب جایی ممکنه نیاز باشه از Lazy هم استفاده بشه؟
نازنین کریمی مقدم۰۲ اسفند ۱۳۹۹، ۱۹:۴۳
درود.
در حالت lazy، ما فقط زمانی که شرط خاصی داشته باشیم اون کوئری رو میزنیم (همونطور که در مثال condition رو قبل از کوئری آوردیم) ولی eager، از همون ابتدا ما کوئری رو میزنیم و هیچ شرطی نداریم. در حقیقت، lazy میشه حالت شرطی eager.
تفاوت حالت معمولی هم با این دوتا در این هست که در حالت معمولی شما دو کوئری رو n بار صدا میزنید، اما در حالت eager و lazy با with و استفاده از ویژگیهای لاراول، در پشت پرده فقط همون دو کوئری صدا زده میشوند. در اصل بهینه سازی در نوع صدا زدن کوئری تفاوتشون هست.
در مورد سوال دومتون، بله ممکنه از حالت lazy هم استفاده بشه. برای مثال در لود محتوای یک بخش از سایت (مثل خبرهای قدیمی) خیلی رایج هست که تا وقتی اسکرول نشده یا موس روش نرفته، مطالب خالی هستند و لود نشدند. یا مثال دیگه ای که میشه زد بخش نظرات و کامنتهای قدیمیتر هست.
m3hdi۰۸ آذر ۱۳۹۹، ۱۱:۰۹
great
Morteza۲۳ آذر ۱۳۹۷، ۲۱:۲۹
در مثال آخر که کد رو نوشتید انتهای خط اول به اشتباه ()ger نوشته شده !?
راهنمای مقاله
چرا برنامه نویسان از Eager loading در برنامه نویسی استفاده نمیکنند؟
خطای رایج N+1 چیست؟
مثالی برای خطای N+1
Eager loading در لاراول چیست؟
یک مثال از Eager Loading در لاراول
لود کردن روابط بیشتر با Eager loading در لاراول
روابط تو در تو
لود ستونهای خاص
لود رابطه به صورت پیشفرض
ایجاد محدودیت در Eager Loading در لاراول
Lazy Eager Loading در لاراول
راهنما و فهرست مقاله
چرا برنامه نویسان از Eager loading در برنامه نویسی استفاده نمیکنند؟