در قسمت قبلی استفاده از شرط در توابع aggregation را دیدیم. در این بخش می خواهیم نگاهی به روش بهینه سازی روابط چرخشی بیندازیم. پروژه را دریافت کرده و مراحل اولیه راه اندازی را انجام دهید. پروژه را دریافت کنید و به یکی از صفحه ها مانند
13/ features/13127.0.0.1:8000
می رویم.
در این صفحه می بینیم که یک کاربر قابلیتی را درخواست داده است و سایر کاربر ها به آن پاسخ داده اند. قصد داریم نشانه ای را به پاسخ کاربر ها اضافه کنیم تا در صورتی که کاربری زیر درخواست خود کامنت گذاشته بود، مشخص شود. به فایل feature.blade
می رویم تا کد های مربوط به این بخش را از حالت کامنت شده خارج کنیم. (خط 52 تا 57)
<div class="flex items-center text-yellow-400">
<svg class="fill-current w-3 h-3" viewBox="0 0 20 20">
<path d="M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z"/>
</svg>
<div class="ml-1 text-xs font-medium">Author</div>
</div>
اکنون صفحه را در مروگر refresh می کنیم.
همان طور که می بینید نشان نویسنده برای همه اضافه شده است. به همین دلیل شرطی را اضافه می کنیم تا مطمئن شویم این نشانه را تنها برای نویسنده نمایش می دهیم.
@if ($comment->isAuthor())
<div class="flex items-center text-yellow-400">
<svg class="fill-current w-3 h-3" viewBox="0 0 20 20">
<path d="M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z"/>
</svg>
<div class="ml-1 text-xs font-medium">Author</div>
</div>
@endif
البته متد isAuthor هنوز وجود ندارد. آن را به مدل Comment اضافه می کنیم.
public function isAuthor()
{
return $this->feature->comments->first()->user_id === $this->user_id;
}
در این متد بررسی می کنیم که آیدی نویسنده ی feature
مربوط به این نظر با آیدی نویسنده ی نظر برابر است یا خیر. اکنون اگر صفحه را refresh کنیم می بینیم که کار می کند.
نشان نویسنده تنها برای کاربری که قابلیت را درخواست داده است نمایش داده می شود که همان چیزی است که می خواستیم. اما اگر به debugbar نگاه کنیم، متوجه مشکلات جدی ای می شویم. در این صفحه ی 35 کوئری اجرا می شود که تعداد زیادی است. ما مشکل n+1 ایجاد کرده ایم. از آن بدتر سربرگ مدل ها است که نشان می دهد 299 مدل از دیتابیس فراخوانی شده است.
در صورتی که قبل از اضافه کردن این قابلیت، debugbar 3 کوئری و 27 مدل را نشان می داد.
اما چگونه می توان شرایط را مانند قبل نگه داشت؟ به کنترلر FeatureController
می رویم تا با کمک eager load
این مشکل را حل کنیم.
public function show(Feature $feature)
{
$feature->load('comments.user', 'comments.feature.comments');
return view('feature', ['feature' => $feature]);
}
در اینجا برای اینکه نیازهای متد isAuthor
برطرف شود، feature های هر کدام از کامنت ها و کامنت های هر کدام از feature ها eager load شده است.
صفحه را refresh می کنیم تا ببینیم این تغییر کمکی کرده یا خیر.
همان طور که می بینید eager load
کردن بهبود هایی ایجاد کرده است اما هنوز مدل های اضافی از دیتابیس فراخوانی شده اند. با اینکه این تغییر بهبود دهنده بوده اما احساس می شود تمام مشکلات حل نشده اند. اگر دوباره و با دقت بیشتری به eager load ای که انجام شد نگاه کنیم، کمی عجیب به نظر می رسد.
public function show(Feature $feature)
{
$feature->load('comments.user', 'comments.feature.comments');
return view('feature', ['feature' => $feature]);
}
feature را از دیتابیس خواندیم سپس کامنت های آن را load کردیم که دوباره به feature برسیم و دوباره کامنت های آن را load کنیم!
در چنین شرایطی که روابط مورد نیاز از قبل load شده اند، من از متد setRelation
استفاده می کنم.
public function show(Feature $feature)
{
$feature->load('comments.user');
$feature->comments->each->setRelation('feature', $feature);
return view('feature', ['feature' => $feature]);
}
با کمک این متد روی کامنت ها پیمایش انجام می دهیم و برای هر کدام رابطه feature را به صورت دستی مقدار دهی می کنیم.
اکنون اگر صفحه را refresh کنیم می بینیم که موفق شدیم بدون کوئری اضافه و فراخوانی مدل های زیاد این قابلیت را به شکلی بهینه پیاده سازی کنیم. در واقع چون مقدار رابطه را از قبل در مموری داشتیم کار eager load را دستی انجام دادیم. هر جایی که شرایطی مانند مثال این مطلب داشتید که روابط را به صورت چرخشی load می کردید حتما متد setRelation
را به خاطر بیاورید.
امیدوارم این مطلب برای شما مفید بوده باشد.