با یک تیر دو نشان بزنید🎯 یک هدیه ۳ میلیون تومانی به همراه ۲۵٪ تخفیف روی همه دوره‌های متخصص😍
۰ ثانیه
۰ دقیقه
۰ ساعت
۶ دیدگاه نظر رضا زیدی
آموزش بهینه سازی روابط در لاراول با Eager loading
آموزش بهینه سازی روابط در لاراول با Eager loading

یکی از امکانات مهم در لاراول، قابلیت Eager Loading است. از طریق این قابلیت می‌توانیم کوئری‌های بهینه‌تری پیاده سازی کنیم تا در زمان دریافت اطلاعات از دیتابیس صرفه‌جویی کنیم. این کار باعث ارتقاء سرعت برنامه و همچنین دریافت نکردن اطلاعات اضافی در هنگام بارگذاری اطلاعات می‌شود. می‌توانیم فقط اطلاعاتی که نیاز داریم را دریافت کنیم و مشکلاتی از قبیل N+1 را رفع کنیم. در ادامه، بهینه سازی روابط در لاراول با Eager loading را با یک مثال عملی به شما نشان می‌دهیم با ما همراه باشید.

اگر به یادگیری بیشتر لاراول علاقه داری می‌توانی در دوره آموزش لاراول کاربردی (بسته پروژه محور) شرکت کنی، این دوره شامل ۱۲ پروژه کاربردی و پر استفاده در دنیای واقعی است، که تمامی پروژه‌ها به صورت کامل برنامه‌ نویسی خواهند شد، تا دانشجو بتواند با روند ایجاد و تکمیل پروژه به صورت کامل آشنا شود.

چرا برنامه نویسان از Eager loading در برنامه نویسی استفاده نمی‌کنند؟

شاید یکی از مهم‌ترین سؤالاتی که برای شما پیش آمده باشد این است که چرا برنامه نویسان معدودی از امکان Eager loading در لاراول استفاده می‌کنند؟ همان‌طور که می‌دانید یکی از غنی‌ترین منابعی که برای یادگیری فریم ورک لاراول وجود دارد، سایت اصلی این فریم ورک می‌باشد و بسیاری از برنامه نویسانی که از این فریم ورک برای برنامه نویسی استفاده می‌کنند برنامه نویسی با فریم ورک لاراول را از این سایت یاد می‌گیرند. اما در سایت اصلی فریم ورک لاراول توضیحات چندانی راجع به Eager loading در لاراول داده نشده است. (در نسخه‌ی جدید آن یعنی 7.12 به طور کامل‌تری توضیح داده شده است) به همین علت بسیاری از برنامه نویسان نتوانسته‌اند این موضوع را درک کنند و همین عامل باعث شده است که لزوم یادگیری و استفاده از آن را در برنامه‌هایی که می‌نویسند درک نکنند، به همین علت در این مطلب قصد داریم توضیحاتی راجع به استفاده از این ابزار ارائه دهیم.

بهینه سازی روابط در لاراول با Eager loading

خطای رایج N+1 چیست؟

قبل از این که بخواهیم در رابطه با Eager loading در لاراول صحبت کنیم بهتر است با خطای رایج N+1 آشنا شویم. یکی از معمول‌ترین خطاها در حین برنامه نویسی با فریم ورک لاراول، خطای N+1 می‌باشد. این خطا زمانی رخ می‌دهد که برنامه نویس یک رکورد مشخص را واکشی کرده و به دنبال آن فریم ورک لاراول خود به خود N کوئری را به اجرا در می‌آورد که دلیل این کار این است که فریم ورک لاراول قصد دارد رابطه‌های بین جدول‌ها را از روی بانک اطلاعاتی به اجرا در بیاورد. در ادامه قصد داریم یک مثال واضح و آسان درباره‌ی این موضوع ارائه دهیم که به طور کامل متوجه این موضوع شوید.

بهینه سازی روابط در لاراول با Eager loading

مثالی برای خطای N+1

فرض کنید در طول برنامه نویسی با فریم ورک لاراول دو مدل را ایجاد کرده‌اید، مدل اول با نام student و مدل دوم با نام book ایجاد شده است، رابطه‌ای که بین این دو جدول وجود دارد به این صورت است که هر دانش آموز می‌تواند چند کتاب داشته باشد. همان‌طور که می‌دانید برای این کار تنها کافی است یک رابطه بین مدل students و book ایجاد کنیم:

public function books(){
    $this->hasMany(Book::class);
}

حال برای دریافت اطلاعات تنها کافی است کدهای زیر را وارد کنید:

$students = Student::all();
foreach( $students as $student ) {
    $studentName = $student->name;
    $books = $student->books;
}

همان‌طور که مشاهده می‌کنید در این مثال تمامی اطلاعات دانش آموزان دریافت می‌شود و بعد از آن N کوئری دیگر نیز اطلاعات کتاب‌ها را به صورت تک تک دریافت می‌کنند که این در واقع همان خطای N+1 است که در صورت بزرگ بودن جداول می‌تواند وب سایت شما را به خطر بیاندازد. در ادامه به بررسی یک روش بسیار جالب که همان استفاده از Eager loading در لاراول می‌باشد خواهیم پرداخت تا بتوانید این مشکل را حل کنید.

بهینه سازی روابط در لاراول با Eager loading

Eager loading در لاراول چیست؟

Eager loading در لاراول در واقع راه حلی بسیار مناسب برای از بین بردن خطای N+1 در برنامه نویسی می‌باشد. در واقع Eager loading در لاراول راه حلی بسیار مناسب را به شما پیشنهاد می‌دهد که به کمک آن می‌توانید خطای N+1 را که در مثال قبلی توضیح دادیم حل کنید. برای این که بهتر با مفهوم Eager loading در لاراول آشنا شوید بهتر است این مفهوم را نیز با یک مثال به صورت کامل توضیح دهیم.

بهینه سازی روابط در لاراول با Eager loading

یک مثال از Eager Loading در لاراول

در این مطلب قصد داریم مثالی را برای این مورد ارائه دهیم:

$sudents = Student::with('books')->get();
foreach( $students as $student ) {
    $studentName = $student->name;
    $books = $student->book
}

همان‌طور که می‌دانید لاراول با استفاده از دستور with متوجه می‌شود که باید به صورت همزمان اطلاعات را از لیستی که نام دانش آموزان در آن قرار دارد دریافت کرده و به نمایش در بیاورد، از آن جایی که لاراول دارای معماری ORM می‌باشد، تنها دو کوئری را دریافت می‌کند. همان‌طور که می‌توانید در این مورد مشاهده کنید، استفاده از Eager loading در لاراول روش بسیار بهینه‌‌تری نسبت به سایر روش‌ها ( که یکی از آن‌ها را در قسمت قبلی بیان کردیم ) می‌باشد.

لود کردن روابط بیشتر با Eager loading در لاراول

فرض کنید که می‌خواهید از اطلاعات تمامی کلاس‌هایی که یک دانش آموز خاص در آن قرار دارد باخبر شوید. در این صورت می‌توانید از قطعه کد زیر برای این کار استفاده کنید:

$students = Student::with( [ 'books', 'classes'] )->get();
foreach( $students as $student ) {
    $studentName = $student->name;
    $books = $student->books;
    $classes = $student->classes;
}

روابط تو در تو

اگر برای هر کلاس، چند معلم را تعریف کرده باشیم و نیاز باشد که به رابطه‌ی بین کلاس و معلم هم دسترسی داشته باشیم، می‌توانیم به شکل زیر معلمان هر کلاس را نیز لود کنیم:

$students = Student::with( [ 'books', 'classes.teachers'] )->get();
foreach( $students as $student ) {
    $studentName = $student->name;
    $books = $student->books;
    $classes = $student->classes;
    Foreach($classes as $class) {
        $teachers = $class->teachers;
    }
}

در این حالت هم رابطه‌ی classes و هم رابطه‌ی teachers لود می‌شوند.

لود ستون‌های خاص

اگر بخواهیم ستون‌های خاصی را از رابطه‌ی موردنظر به دست آوریم، می‌توانیم به شکل زیر عمل کنیم:

$students = Student::with('books:id,title')->get();
foreach( $students as $student ) {
    $studentName = $student->name;
    $books = $student->books;
    Foreach($books as $book) {
        $id = $book->id;
        $title = $book->title;
    }
}

در اینجا، از رابطه‌ی 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$ خارج کنید تا لود نشود، می‌توانید به شکل زیر عمل کنید:

$students = App\Models\Student::without('books')->get();

ایجاد محدودیت در Eager Loading در لاراول

گاهی اوقات نیاز دارید محدودیت‌ها یا شرایطی را بر رابطه‌ای که می‌خواهید لود شود، اعمال کنید. برای این کار می‌توانید از اکثر متدهای Query Builder مانند مثال زیر استفاده کنید:

$students = App\Models\Student::with(['books' => function ($query) {
    $query->where('title', 'like', '%سیب%')->lastest('order');
}])->get();

در مثال بالا رابطه‌ی 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']);
}

اگر مطمئن نیستید که رابطه لود شده باشد و می‌خواهید در صورتی که لود نشده، آن را لود کنید، باید به شکل زیر عمل کنید:

$students = App\Models\Student::latest()->get();
$students->loadMissing('books');

بهینه سازی روابط در لاراول با Eager loading

جمع‌ بندی:

بعد از این که با مفهوم 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 در لاراول
اشتراک گذاری مقاله در :