Melvin Koopmans
Melvin Koopmans

Reputation: 3060

Laravel Multiple Pagination in one page

I'm having some trouble with my pagination. I'm having two tables with data from a database and I paginated it with laravel Paginator.

Now my problem is when you go to, for example, page 2 it adds ?page=2 but that makes the first table go to page 2 too.

Is there anyway to get something like this ?

page_table1={number}&page_table2={number}

so you don't apply the page change on other tables.

Upvotes: 48

Views: 57013

Answers (11)

mosleim
mosleim

Reputation: 682

In laravel 5.3+, I use paginate like this:

    $product = Product::paginate(5, ['*'], 'product');
    $region = Region::paginate(5, ['*'], 'region');

and in a blade template, I append the current page to the link of the other paginator to keep the position of both paginator when navigating:

    {{$product->appends(['region' => $region->currentPage()])->links()}}    
    {{$region->appends(['product' => $product->currentPage()])->links()}}    

Upvotes: 26

Justin Kolnick
Justin Kolnick

Reputation: 41

Working in Laravel 8, paginate takes 4 params:

Snippet: (API Docs, Github)

public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)

Modifying the third param $pageName for each model, and appending all query params except the $pageName for that paginator should work:

FirstModel::query()
    ->paginate(null, ['*'], 'first_model')
    ->appends(request()->except('first_model'));

SecondModel::query()
    ->paginate(null, ['*'], 'second_model')
    ->appends(request()->except('second_model'));

Note, using ->appends(...) here ensures that all of the pagination links for that paginator will keep the current query param values.

For example, if you're on the url ?first_model=2&second_model=3 the links for the first_model paginator will look something like:

  • Previous: ?first_model=1&second_model=3
  • Next: ?first_model=3&second_model=3

Upvotes: 4

Ahmard
Ahmard

Reputation: 97

This is late, but it works with laravel 7.x

$messages = Message::where('status', 0)
    ->paginate(10, ['*'], 'p')
    ->appends(Arr::except(Request::query(), 'p'));

In your view, example(inbox.blade.php)

{{$messages->links()}}

Upvotes: 1

Rubens
Rubens

Reputation: 143

Laravel 5.7

Model->paginate(10, ['*'], 'paramName');

10 = Max items per page

['*'] = colums

paramName = pagination param name

Illuminate\Database\Eloquent\Builder

Upvotes: 12

SaidbakR
SaidbakR

Reputation: 13544

The answer of anonym may be extended to be very handy in the views that have related models. tested in :

Suppose we have a view for the CustomerController show.blade.php and we pass the Customer model to it using:

```

return view(['customer' => \App\Customer::find($customerId)]);

``` Each Customer has many Product, and has many Location and we want list with pagination both his/her Products and Locations, simply we may do it in the view like:

@foreach($customer->products()->paginate(10,['*'],'productsPage') as $product)
HTML list
@endforeach
{!! $customer->products()->paginate(10,['*'],'productsPage')->links() !!}

@foreach($customer->locations()->paginate(10,['*'],'locationsPage') as $location)
HTML list
@endforeach
{!! $customer->locations()->paginate(10,['*'],'locationsPage')->links() !!}

Upvotes: 1

anonym
anonym

Reputation: 4850

$publishedArticles = Article::paginate(10, ['*'], 'published');
$unpublishedArticles = Article::paginate(10, ['*'], 'unpublished');

The third argument is used as follows:

laravel/public/articles?published=3

laravel/public/articles?unpublished=1

Upvotes: 82

LauroRafael
LauroRafael

Reputation: 27

Use:

$var1 = DB1::orderBy('...')->paginate(5, ['*'], '1pagination');

$var2 = DB2::orderBy('...')->paginate(5, ['*'], '2pagination');

For Laravel 5.2

Upvotes: 1

Jeremy Postlethwaite
Jeremy Postlethwaite

Reputation: 1324

In Laravel 5.2, declare the page name when using paginate().

Here is an example that works with multiple paginators on a page.

  • Be sure to specify a different $pageName for other models.

See the method \Illuminate\Database\Eloquent\Builder::paginate()

/**
 * Get things by ownerId
 *
 * @param integer $ownerId The owner ID.
 *
 * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator Returns a pagination instance.
 */
public function getThings($ownerId)
{
    $builder = \App\Models\Things::where('ownerId', '=', (integer) abs($ownerId));

    // dd([
    //     '__METHOD__' => __METHOD__,
    //     '__FILE__' => __FILE__,
    //     '__LINE__' => __LINE__,
    //     '$ownerId' => $ownerId,
    //     'sql' => $builder->toSql(),
    //     '$builder' => $builder,
    //     'paginate' => $builder->paginate($perPage = null, $columns = ['*'], $pageName = 'p', $page = null),
    // ]);

    return $builder->paginate($perPage = null, $columns = ['*'], $pageName = 'p', $page = null);
}

Note: $pageName = 'p'

Upvotes: 6

David Gras
David Gras

Reputation: 1028

This is an easy solution I've found on Laravel 4.

Controller

Change the page name before you make the paginator:

Paginator::setPageName('page_a');
$collection_A = ModelA::paginate(10);

Paginator::setPageName('page_b');
$collection_B = ModelB::paginate(10);

View

Do the same: change the page name before you print the links

Paginator::setPageName('page_a');
$collection_A->links();

Paginator::setPageName('page_b');
$collection_B->links();

If you don't want to lose any page state while you navigate to another page, append to the links the current page from all collections:

Paginator::setPageName('page_a');
$collection_A->appends('page_b', Input::get('page_b',1))->links();

Paginator::setPageName('page_b');
$collection_B->appends('page_a', Input::get('page_a',1))->links();

Upvotes: 30

Damien Pirsy
Damien Pirsy

Reputation: 25445

Unfortunately I can't test this code right now, but browsing at the docs and the code (in Illuminate/Pagination/Environment) I guess you could something like this:

# use default 'page' for this
$collection1 = Model::paginate(20);

# use custom 'other_page' for this
$collection2 = Model2::paginate(20);
$collection2->setPageName('other_page');

The setPageName() method isn't documented in the docs, but it's a public method alongside those indicated in the documentation, so it should be working fine. FOr reference, this is the declaration (l. 171-180 in vendor/laravel/framework/src/Illuminate/Pagination/Environment.php):

/**
 * Set the input page parameter name used by the paginator.
 *
 * @param  string  $pageName
 * @return void
 */
public function setPageName($pageName)
{
    $this->pageName = $pageName;
}

Now take into consideration that you will have another query string appended to the url, so you need to tell the pagination to consider it. Use the appends() method:

$collection1->appends(array_except(Request::query(), 'page'))->links();

$collection2->appends(array_except(Request::query(), 'other_page'))->links();

That is, tell each Presenter to build up the url with all the query strings (the array resulting from Request::query() without the current index used by the paginator, or you'll end up with a double value). array_except() is a Laravel built in array helper that returns the given array (1st parameter) purged of the passed index (2nd parameter).

I'll try to test this code as soon as I can, but it should work. Let me know in any case!

Upvotes: 28

Adam
Adam

Reputation: 359

Sounds to me like you need to update the paging tool so that it has an extra param that identifies the table as it's not smart enough to know that it might have 2 instances of itself on the same page.. DOH!

Rather than having to set the current page for all tables through a URL, ideally you want to store the active page number in the session by table id THEN, your script only has to worry about instructions and not worry about ensuring it carries existing page numbers also.

Here's a really draft concept....

// The page would be $tableId + "," + $pageNum
// Passed as a value of "page"
$goTo = "$tableId,$pageNum";
?page=$goTo

Then, when it gets to the part that's getting your table data it would do something like

if(!empty($_GET["page"])){
    list($tableId,$pageNum) = explode(",",$_GET["page"]);

    // Store table's active page number
    $_SESSION["_tableBrowser"][$tableId] = $pageNum;


    // Your table reader would then look at the session for the active page number
    // and not what is in $_GET
}

Upvotes: 0

Related Questions