vladshoob's avatar

How to make pagination with different size for first page? ->paginate(X)

Hello, dear Laracast community.

Never encountered such problem, but in need to implement this for sake of project.

I have a model Article, as an example.

And on the first page I'd like to present grid 3x4 of cards, but with "load more" card as last. So 11 Article cards and load more card. But for next page I need all 12 article cards.

For now, I make ->paginate(12) and simply hide last one (@if($loop->last) @continue @endif). But hiding is not a valid solution, as I'd like to have all articles to be presented.

If I missed something or not clear, let me know. Thanks in advance for all replies.

0 likes
3 replies
vladshoob's avatar
vladshoob
OP
Best Answer
Level 10

@Vilfago, thanks for a hint. Here is what I ended up with as I required Paginator object and its functionality.

Basically, I make the required query outside and put in this helper function, that generates LengthAwarePaginator for me.

/**
 * Custom paginator for "load more" models excluding one element on first page.
 *
 * @param \Illuminate\Database\Eloquent\Builder $query
 * @param int $per_page
 * @return \Illuminate\Pagination\LengthAwarePaginator
 */
function getLoadMoreLengthAwarePaginator(\Illuminate\Database\Eloquent\Builder $query, $per_page = 12) {
    // query total count from DB
    $count = $query->count();

    // get a page number from request
    $page = request()->get('page') ?? 1;

    // recalculate number of items for first page
    $first_page = $per_page - 1;

    // calculate offset
    $perPage = $page == 1 ? $first_page : $per_page;
    $offset = ($page - 2) * $perPage + $first_page;

    // get a collection from DB
    $reviews = $query->skip($offset)->take($perPage)->get();

    // return Paginator instance
    return new \Illuminate\Pagination\LengthAwarePaginator(
        $reviews, $count, $per_page, $page, ['path'  => request()->url(), 'query' => request()->query()]
    );
}
yaroslawww's avatar

Simple pagination example:

use Illuminate\Container\Container;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;

class CustomFirstPagePaginator {
    public static function simplePaginate( $query, $perFirstPage = null, $perPage = null, $columns = [ '*' ], $pageName = 'page', $page = null ) {
        $page = $page ?: Paginator::resolveCurrentPage( $pageName );

        $perPage      = $perPage ?: $query->model->getPerPage();
        $perFirstPage = $perFirstPage ?: $perPage;

        $skip        = match ( $page ) {
            1 => 0,
            default => $perFirstPage + ( $page - 2 ) * $perPage,
        };
        $realPerPage = match ( $page ) {
            1 => $perFirstPage,
            default => $perPage,
        };
        $query->skip( $skip )->take( $realPerPage + 1 );

        return Container::getInstance()->makeWith( Paginator::class, [
            'items'       => $query->get( $columns ),
            'perPage'     => $realPerPage,
            'currentPage' => $page,
            'options'     => [
                'path'         => Paginator::resolveCurrentPath(),
                'pageName'     => $pageName,
                'perPage'      => $perPage,
                'perFirstPage' => $perFirstPage,
            ],
        ] );
    }


    public static function paginate( $query, $perFirstPage = null, $perPage = null, $columns = [ '*' ], $pageName = 'page', $page = null ) {
        $page = $page ?: Paginator::resolveCurrentPage( $pageName );

        $perPage      = $perPage ?: $query->model->getPerPage();
        $perFirstPage = $perFirstPage ?: $perPage;

        $skip        = match ( $page ) {
            1 => 0,
            default => $perFirstPage + ( $page - 2 ) * $perPage,
        };
        $realPerPage = match ( $page ) {
            1 => $perFirstPage,
            default => $perPage,
        };
        $results     = ( $total = $query->toBase()->getCountForPagination() )
            ? $query->offset( $skip )->limit( $realPerPage )->get( $columns )
            : $query->model->newCollection();

        return Container::getInstance()->makeWith( LengthAwarePaginator::class, [
            'items'       => $results,
            'total'       => $total,
            'perPage'     => $realPerPage,
            'currentPage' => $page,
            'options'     => [
                'path'         => Paginator::resolveCurrentPath(),
                'pageName'     => $pageName,
                'perPage'      => $perPage,
                'perFirstPage' => $perFirstPage,
            ],
        ] );
    }
}

Then anywhere:

        $query = Article::published()->latests();

        $articles = CustomFirstPagePaginator::simplePaginate(
            $query, 7, 9
        );

Please or to participate in this conversation.