دیتابیس: صفحه‌بندی

لاراول 5.6.29 در حال تکمیل

مقدمه

در سایر فریمورک‌ها، صفحه‌بندی مشکل است. paginator لاراول با کوئری بیلدر و الوکوئنت ORM ادغام می‌شود و بر اساس نتایج استخراج شده از دیتابیس، لینک‌های صفحه‌بندی را به شکلی مناسب و راحت برای استفاده ارائه می‌دهد. HTML تولید شده توسط paginator کاملا با فریمورک سی‌اس‌اس بوت‌استرپ سازگاری دارد.

کاربرد پایه

صفحه‌بندی نتایج کوئری بیلدر

راه‌های مختلفی برای تولید آیتم‌های صفحه‌بندی وجود دارد. راحت‌ترین روش، استفاده از متد paginate در کوئری بیلدر و یا یک کوئری الوکوئنت است. متد paginate به صورت خودکار و بر اساس صفحه جاری که کاربر در حال مشاهده آن است، لیمیت و آفست مناسب را برای آن تنظیم می‌کند. به صورت پیش‌فرض، صفحه جاری از مقدار آرگومان page کوئری استرینگِ ریکوئست HTTP به دست می‌آید. طبیعتا، این مقدار به صورت خودکار توسط لاراول تشخیص داده می‌شود و همچنین به صورت خودکار به لینک‌های تولید شده توسط paginator وارد می‌شود.

در مثال زیر، تنها آرگومانی که به متد paginate ارسال شده است، تعداد آیتم‌هایی است که می‌خواهید در هر صفحه نمایش دهید. در این مثال، تعداد آیتم‌های هر صفحه برای نمایش را 15 عدد انتخاب کرده‌ایم:

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Show all of the users for the application.
     *
     * @return Response
     */
    public function index()
    {
        $users = DB::table('users')->paginate(15);

        return view('user.index', ['users' => $users]);
    }
}

در حال حاضر، عملیات صفحه‌بندی که از دستور groupBy استفاده کنند، نمی‌توانند به خوبی توسط لاراول اجرا شوند. اگر نیاز به استفاده از groupBy در مجموعه نتایج صفحه‌بندی شده خود دارید، توصیه می‌کنیم که با کوئری زدن داده‌ها را از دیتابیس گرفته و یک paginator دستی بسازید.

صفحه‌بندی ساده (Simple Pagination)

اگر تنها نیاز به نمایش لینک‌های "قبلی" و "بعدی" در ویوی صفحه‌بندی خود دارد، می‌توانید از متد simplePaginate استفاده کنید تا کوئری بهینه‌تری داشته باشید. اگر هنگامی که ویو رندر می‌شود، نمی‌خواهید برای هر شماره صفحه‌ای، یک لینک نمایش دهید و خصوصا در دیتاست‌های بزرگ بسیار کاربردی است.

$users = DB::table('users')->simplePaginate(15);

صفحه‌بندی نتایج الوکوئنت

برای کوئری‌های الوکوئنتی نیز می‌توانید صفحه‌بندی را ایجاد کنید. در مثال زیر، مدل User را با محدودیت 15 آیتم در صفحه، صفحه‌بندی می‌کنیم. همانطور که می‌بینید، سینتکس صفحه‌بندی الوکوئنتی بسیار شبیه همتای ساخته شده با نتایج کوئری بیلدری را دارد:

$users = App\User::paginate(15);

البته، متد paginate را بعد از سِت کردن سایر محدودیت‌ها روی کوئری مانند شرط‌های where، نیز می‌توانید اجرا کنید:

$users = User::where('votes', '>', 100)->paginate(15);

در هنگام صفحه‌بندی مدل‌های الوکوئنت نیز می‌توانید از متد simplePaginate استفاده کنید:

$users = User::where('votes', '>', 100)->simplePaginate(15);

صفحه‌بندی به صورت دستی

گاهی می‌خواهید که یک نمونه pagination را به صورت دستی و با ارسال آرایه‌ای از آیتم‌ها بسازید. بسته به نیازهای خود، اینکاررا با ایجاد instance از یکی از کلاس‌های Illuminate\Pagination\Paginator یا Illuminate\Pagination\LengthAwarePaginator می‌توانید انجام دهید.

کلاس Paginator نیازی به دانستن تعداد کل نتایج موجود در آرایه ندارد؛ هر چند، به همین دلیل، این کلاس متدی برای دریافت اندیس صفحه آخر ندارد. LengthAwarePaginator تقریبا آرگومان‌های مشابهی با Paginator می‌پذیرد؛ با این وجود، نیازی به دانستن تعداد کل آیتم‌های موجود در آرایه ارسالی ندارد.

به عبارت دیگر، Paginator متناظر با متد simplePaginate در کوئری بیلدر و الوکوئنت بوده، در حالی که LengthAwarePaginator متناظر با متد paginate است.

هنگامی که یک instance از paginator را به صورت دستی ایجاد می‌کنید، باید آرایه حاوی نتایجی که به paginator پاس می‌دهید را به صورت دستی بشکنید. اگر روش انجام اینکار را نمی‌دانید، داکیومنت فانکشن array_slice را مطالعه کنید.

نمایش نتایج صفحه‌بندی

هنگامی که متد paginate را فراخوانی می‌کنید، یک instance از Illuminate\Pagination\LengthAwarePaginator را دریافت خواهید کرد. هنگامی که متد simplePaginate را فراخوانی می‌کنید، یک instance از Illuminate\Pagination\Paginator را دریافت خواهید کرد. این آبجکت‌ها چندین متد را در اختیارتان قرار می‌دهند که نتایج را توصیف می‌کنند. علاوه بر این متدهای هلپر، نمونه‌های paginator ایتریتور هستند و مانند آرایه‌ها می‌توان بر روی آن‌ها حلقه زد. بنابراین، هنگامی که نتایج را دریافت می‌کنید، با استفاده از  Blade می‌توانید لینک‌های صفحات را رندر کرده و نتایج را نمایش دهید.

<div class="container">
    @foreach ($users as $user)
        {{ $user->name }}
    @endforeach
</div>

{{ $users->links() }}

متد links لینک‌های باقی صفحات مجموعه نتایج را رندر خواهد کرد. هر کدام از این لینک‌ها، از قبل حاوی متغییر کوئری استرینگ page مربوطه است. به خاطر داشته باشید، HTML تولید شده توسط متد links، کاملا با فریمورک سی‌اس‌اس بوت‌استرپ سازگار است.

کاستومایز کردن URI استفاده شده در paginator

متد امکان کاستومایز کردن URI استفاده شده توسط paginator را در هنگام تولید لینک‌ها در اختیارتان قرار می‌دهد. برای مثال، اگر می‌خواهید که paginator لینک‌هایی مانند http://example.com/custom/url?page=N را تولید کند، باید custom/url را به متد withPath پاس دهید.

Route::get('users', function () {
    $users = App\User::paginate(15);

    $users->withPath('custom/url');

    //
});

اَپِند کردن به لینک‌های صفحه‌بندی

با استفاده از متد appends، می‌توانید به کوئری استرینگ مربوط به لینک‌های صفحه‌بندی اَپِند کنید. برای مثال، برای اَپِند کردن sort=votes به همه لینک‌های صفحه‌بندی، باید appends را به شکل زیر فراخوانی کنید:

{{ $users->appends(['sort' => 'votes'])->links() }}

اگر می‌خواهید یک فرگمنت هش به URLهای paginator اَپِند کنید، می‌توانید از متد fragment استفاده کنید. برای مثال، برای اَپِند کردن #foo به انتهاب تمامی لینک‌های صفحه‌بندی، متد fragment را به شکل زیر فراخوانی کنید:

{{ $users->fragment('foo')->links() }}

تبدیل نتایج به JSON

کلاس‌های نتایج paginator لاراول، اینترفیس کنترکت Illuminate\Contracts\Support\Jsonable را پیاده‌سازی می‌کنند که حاوی متد toJson است و تبدیل نتایج صفحه‌بندی (از شکل آبجکت به) JSON را ساده می‌سازد. همچنین با برگرداندن یک instance از paginator از یک روت یا اکشن کنترلر، می‌توانید این instance را به JSON تبدیل کنید:

Route::get('users', function () {
    return App\User::paginate();
});

JSON که توسط paginator تولید می‌شود، حاوی اطلاعات دیگری مانند total و current_page و last_page بیشتر است. آبجکت‌های نتایج موجود از طریق کلید data در آرایه JSON در دسترس هستند. مثال زیر JSON ایجاد شده در اثر بازگشت یک نمونه‌ی paginator از یک روت تولید شده است.

{
   "total": 50,
   "per_page": 15,
   "current_page": 1,
   "last_page": 4,
   "first_page_url": "http://laravel.app?page=1",
   "last_page_url": "http://laravel.app?page=4",
   "next_page_url": "http://laravel.app?page=2",
   "prev_page_url": null,
   "path": "http://laravel.app",
   "from": 1,
   "to": 15,
   "data":[
        {
            // Result Object
        },
        {
            // Result Object
        }
   ]
}

کاستومایز کردن ویوی صفحه‌بندی

به صورت پیش‌فرض، ویوهای رندر شده برای نمایش لینک‌های صفحه‌بندی، با فریمورک سی‌اس‌اس بوت‌استرپ سازگاری دارند. اما، اگر از بوت‌استرپ استفاده نمی‌کنید، هیچ محدودیتی برای تعریف ویوهایی برای رندر کردن این لینک‌ها ندارید. هنگامی که متد links را بر روی یک نمونه paginator فراخوانی می‌کنید، نام ویو را به عنوان اولین آرگومان به این متد پاس دهید:

{{ $paginator->links('view.name') }}

// Passing data to the view...
{{ $paginator->links('view.name', ['foo' => 'bar']) }}

با این حال، ساده‌ترین روش برای کاستومایز کردن ویوهای صفحه‌بندی، اکسپورت کردن آن‌ها در دایرکتوری resources/views/vendor است، این کار را با کامند vendor:publish می‌توانید انجام دهید:

php artisan vendor:publish --tag=laravel-pagination

این کامند ویوها را در دایرکتوری resources/views/vendor/pagination قرار خواهد داد. فایلbootstrap-4.blade.php درون این دایرکتوری مربوط به ویو پیش‌فرض صفحه‌بندی است. برای اصلاح HTML صفحه‌بندی، می‌توانید این فایل را ویرایش کنید.

از متاگر می‌خواهید فایل متفاوتی را به عنوان ویو پیش‌فرض صفحه‌بندی تعیین کنید، می‌توانید از متدهای defaultView و defaultSimpleView کلاس paginator در AppServiceProvider استفاده کنید:

use Illuminate\Pagination\Paginator;

public function boot()
{
    Paginator::defaultView('pagination::view');

    Paginator::defaultSimpleView('pagination::view');
}

متدهای Instance ایجاد شده از Paginator

هر نمونه‌ای که از paginator ایجاد می‌شود، از طریق متدهای زیر، اطلاعات اضافه‌تری برای صفحه‌بندی را ارائه می‌دهد:

$results->count()
$results->currentPage()
$results->firstItem()
$results->hasMorePages()
$results->lastItem()
$results->lastPage() (Not available when using simplePaginate)
$results->nextPageUrl()
$results->onFirstPage()
$results->perPage()
$results->previousPageUrl()
$results->total() (Not available when using simplePaginate)
$results->url($page)