مدل Eloquent یک ابزار پیادهسازی رابط اشیاء یا ORM است که به صورت پیشفرض در بستر لاراول وجود دارد. در این مطلب قرار است که با نحوه محدودسازی و صفحهبندی نتایج کوئری در لاراول Eloquent آشنا شویم. برای پیشبرد مراحل این آموزش، یک اپلیکیشن آزمایشی خواهیم داشت که مدلها و روابط جدید را در مورد آن به کار خواهیم گرفت.
در طول سری مطالب قبلی در مورد Eloquent، یک سری لینک جدید به اپلیکیشن آزمایشی اضافه شد که برای تست ویژگیهای لاراول Eloquent مورد استفاده قرار گرفتند. شاید تاکنون متوجه شدهاید که با اضافهکردن لینکهای جدید، صفحه فهرست اصلی طولانیتر میشود. چرا که هیچ محدودیتی در لینکهای نمایشی در اپلیکیشن وجود ندارد. البته این موضوع در هنگام داشتن ورودیهای اندک پایگاه داده، چیز چندان مهمی نیست. ولی در درازمدت، زمان بارگذاری صفحه شما افزایش یافته و با توجه به میزان محتوای موجود در صفحه، طرح کلی چندان خوانا نخواهد بود.
در این آموزش، نحوه محدودسازی تعداد نتایج در کوئری لاراول Eloquent را همراه با متد limit() بررسی خواهیم کرد و همچنین چگونگی صفحهبندی نتایج با متد simplePaginate() را فراخواهیم گرفت.
محدودسازی نتایج کوئری
برای شروع کار، مسیر اصلی اپلیکیشن (/) را برای محدودسازی تعداد لینک لیستشده در صفحه فهرست بروزرسانی میکنیم.
ابتدا فایل مسیرهای وب را در ویرایشگر کد باز میکنیم.
routes/web.php
سپس موقعیت تعریف مسیر اصلی را پیدا میکنیم.
Route::get('/', function () { $links = Link::all()->sortDesc(); return view('index', [ 'links' => $links, 'lists' => LinkList::all() ]); });
کد بالا نشان میدهد که کوئری تمام لینکهای موجود در پایگاه داده را از طریق مدل Link در متد all() نمایش میدهد. همانطور که در مطالب قبلی در این رابطه توضیح داده شد، این متد از از کلاس مادر Model گرفته شده و مجموعهای همرا با تمام رکوردهای پایگاه داده مطابق با این مدل را برگشت میدهد. متد sortDesc() نیز برای ترتیب نزولی مجموعه خرپجی به کار گرفته شده است.
در حال حاضر، کد را برای استفاده از متد ترتیببندی کوئری پایگاه داده orderBy() تغییر دادهاید. این متد باعت ترتیب نتایج کوئری در سطح پایگاه داده میشود و جایگزین استفاده از all() برای ترتیببندی کلی ردیف نتایج به صورت کالکشن Eloquent شده است. همچنین یک فراخوانی زنجیرهای به متد limit() برای محدودسازی نتایج کوئری دارید. نهایتاً از متد get() نیز برای دستیابی به سری نتایج فیلتر ده به عنوان کالکشن Eloquent استفاده میشود.
مسیر اصلی خود را با کد زیر جایگزین کنید. دقت کنید که تغییرات چگونه انجام شدهاند.
Route::get('/', function () { $links = Link::orderBy('created_at', 'desc')->limit(4)->get(); return view('index', [ 'links' => $links, 'lists' => LinkList::all() ]); });
هماکنون کد بروزرسانیشده چهار لینک آخر اضافهشده به پایگاه داده را فارغ از موقعیت آنها در لیستهای مختلف، برمیگرداند. البته تمام لینکها به لیستها اضافه شدهاند و بازدیدکنندگان میتوانند برای پیدا کردن یک لینک خاص، لیست کامل لینکها را مشاهده کنند.
در مرحله بعد، به سراغ نحوه صفحهبندی نتایج کوئری برای اطمینان از دسترسی کامل به تمام لینکها میرویم. به این ترتیب، نیازی به بارگذاری تمام لینکها در یک صفحه جداگانه نخواهد بود.
صفحهبندی نتایج کوئری
صفحه فهرست شما در حال حاضر، تعداد محدودی از لینکها را نمایش میدهد. در نتیجه، صفحه با بارگذاری بیش از حد محتوا روبرو نیست و در مدتزمان کمتری نمایش داده میشود. با اینکه این رویکرد در بسیاری از موارد جواب میدهد، ولی گاهی اوقات لازم است که بازدیدکنندگان همچنان بتوانند به تمام لینکها، حتی آنهایی که در حالت پیشفرض نمایش داده نمیشوند، دسترسی داشته باشند. کارسازترین روش برای این منظور، بکارگیری یک صفحهبندی به صورتی است که کاربران بتوانند در صفحات مختلف نتایج حرکت کنند.
لاراول Eloquent دارای روشهای پایه برای تسهیل بکارگیری صفحهبندی در نتایج کوئری پایگاه داده است. متدهای paginate() و simplePaginate() وظیفه صفحهبندی لینکها را برعهده دارند و از پارامترهای HTTP برای مشخصکردن صفحه موردتقاضا استفاده میکنند. این متدها با محدودسازی و ترتیب درست نتایج کوئری پایگاه داده، سری مورد انتظار نتایج را بر اساس تعداد دلخواه لینک در هر صفحه ایجاد میکنند.
اکنون کوئریهای Eloquent در فایل routes/web.php را بروزرسانی میکنیم تا از متد simplePaginate() استفاده کنند. این متد موجب ایجاد یک ناوبری ساده بین لینکهای قبلی و بعدی میشود. برخلاف متد paginate()،simplePaginate() اطلاعات مربوط به کل صفحات را در نتایج کوئری نشان میدهد.
فایل routes/web.php را در ویرایشگر کد دلخواه باز کنید. با بروزرسانی مسیر / شروع میکنیم. در اینجا، فراخوانی متد limit(4)->get() با simplePaginate() جایگزین میشود.
... Route::get('/', function () { $links = Link::orderBy('created_at', 'desc')->simplePaginate(4); return view('index', [ 'links' => $links, 'lists' => LinkList::all() ]); }); ...
سپس به سراغ تعریف مسیر /{slug} در همین فایل میرویم و متد get() را با simplePaginate() جایگزین میکنیم. در نتیجه، شکل کلی کد میبایست به صورت زیر باشد.
... Route::get('/{slug}', function ($slug) { $list = LinkList::where('slug', $slug)->first(); if (!$list) { abort(404); } return view('index', [ 'list' => $list, 'links' => $list->links()->orderBy('created_at', 'desc')->simplePaginate(4), 'lists' => LinkList::all() ]); })->name('link-list'); ...
در نتیجه، فایل نهایی routes/web.php باید پس از تغییرات به صورت زیر باشد.
<?php use Illuminate\Support\Facades\Route; use App\Models\Link; use App\Models\LinkList; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', function () { $links = Link::orderBy('created_at', 'desc')->simplePaginate(4); return view('index', [ 'links' => $links, 'lists' => LinkList::all() ]); }); Route::get('/{slug}', function ($slug) { $list = LinkList::where('slug', $slug)->first(); if (!$list) { abort(404); } return view('index', [ 'list' => $list, 'links' => $list->links()->orderBy('created_at', 'desc')->simplePaginate(4), 'lists' => LinkList::all() ]); })->name('link-list');
پس از اتمام کار، فایل را ذخیره کنید.
کوئریهای پایگاه داده هماکنون بروزرسانی شدهاند، ولی هنوز بروزرسانی لایه فرونتاِند برای استفاده از کدی که نوار ناوبری را رندر میکند، بروزرسانی نشده است. مجموعه کالکشن Eloquent که از simplePaginate() منتج شده است، شامل متدی با نام links() است. این متد میتواند از طریق فرونتاِند برای گرفتن کد اچتمل لازم برای نمای بخش ناوبری بر اساس کوئری فراخوانی شود.
همچنین میتوان از متد links() در یک کالکشن صفحهبندی شده Eloquent برای دسترسی به آبجکت صفحهبندی استفاده کرد. در نتیجه، متدهای مفیدی برای استخراج اطلاعات محتوایی مانند صفحه کنونی و تعداد صفحات محتوای باقیمانده در دسترس قرار میگیرند.
بر این اساس، فیال نمایش اپلیکیشن را در مسیر resources/views/index.blade.php باز کنید.
resources/views/index.blade.php
انتهای بخشی که با برچسب links class است، پیدا کنید؛ جایی که حلقه foreach در آن به کار رفته و لینکها در آن رندر میشوند. خطوط کد زیر را بعد از این بخش و قبل از تگ پایانی </div> در صفحه قرار دهید.
@if ($links->links()->paginator->hasPages()) <div class="mt-4 p-4 box has-text-centered"> {{ $links->links() }} </div> @endif
این کد وجود صفحات چندگانه نتایج را با دسترسی به آبجکت صفحهبندی بررسی میکند و متد hasPages() را فراخوانی مینماید. وقتی این متد با نتیجه true مواجه شد، صفحه یک المان جدید div را رندر کرده و با فراخوانی متد links()، لینکهای ناوبری را برای کوئری مربوطه Eloquent چاپ میکند.
در اینجا، کد نهایی صفحه index.blade.php را پس از بروزرسانی مشاهده میکنید.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>My Awesome Links</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css"> <style> html { background: url("https://i.imgur.com/BWIdYTM.jpeg") no-repeat center center fixed; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; } div.link h3 { font-size: large; } div.link p { font-size: small; color: #718096; } </style> </head> <body> <section class="section"> <div class="container"> <h1 class="title"> @if (isset($list)) {{ $list->title }} @else Check out my awesome links @endif </h1> <p class="subtitle"> @foreach ($lists as $list)<a href="{{ route('link-list', $list->slug) }}" title="{{ $list->title }}" class="tag is-info is-light">{{ $list->title }} ({{ $list->links()->count() }})</a> @endforeach </p> <section class="links"> @foreach ($links as $link) <div class="box link"> <h3><a href="{{ $link->url }}" target="_blank" title="Visit Link: {{ $link->url }}">{{ $link->description }}</a></h3> <p>{{$link->url}}</p> <p class="mt-2"><a href="{{ route('link-list', $link->link_list->slug) }}" title="{{ $link->link_list->title }}" class="tag is-info">{{ $link->link_list->title }}</a></p> </div> @endforeach </section> @if ($links->links()->paginator->hasPages()) <div class="mt-4 p-4 box has-text-centered"> {{ $links->links() }} </div> @endif </div> </section> </body> </html>
پس از بروزرسانی، فایل را ذخیره کنید. اگر به پنجره مرورگرتان برگردید و صفحه اپلیکیشن را دوباره بارگذاری کنید، متوجه یک نوار ناوبری جدید خواهید شد. این نوار فارغ از اینکه ۴ لینک یا بیشتر در لیستها داشته باشید، نمایش داده میشود.
با انجام صفحهبندی، میتوانید همچنان به محتوای خود اضافه کنید، بدون اینکه دسترسی کاربران و موتورهای جستجو به آیتمهای قدیمیتر قطع گردد. در صورتی که تنها به مقدار مشخصی از نتایج بر اساس یک معیار خاص احتیاج پیدا میکنید و به صفحهبندی نیز نیازی ندارید، میتوانید با متد limit() کوئریهای خود را سادهسازی کرده و از ارائه یک سری نتایج محدود مطمئن شوید.