Laravel Multiple Pagination in one page
Asked Answered
I

11

47

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.

Ilowell answered 6/6, 2014 at 16:8 Comment(0)
G
28

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!

Gent answered 6/6, 2014 at 19:39 Comment(7)
I was not able to get anything working using the method you described. Where you?Tureen
$collection2->setPageName('other_page'); >>> call_user_func_array() expects parameter 1 to be a valid callback, class 'Illuminate\Support\Collection' does not have a method 'setPageName'Hime
To the downvoters, consider that this answer might refer to a different Laravel version from the one you're using now and what worked then might not work in a newer version. Just saying.Gent
@DamienPirsy Do you know if this still works in laravel 5.6 ? I know it has been a whileElviselvish
I don't think this answer ever worked, I've checked back in versions and you must provide the page name on ::paginate for it to take effect, always haveTrainbearer
i think its currently in AbstractPaginator.php also there is a getPageName()Quip
and yes its still working setPageName()/getPageName() laravel 5.8Quip
P
81
$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

Plastometer answered 18/8, 2016 at 7:57 Comment(6)
Thanks, works great for Laravel 5.4, but you still need to append other pages if you want them to work at the same time.Electrophilic
That's the solution for Laravel 5.4Pesky
Very good solution specially when using pagination without prepare directly from the the view for the related model, Suppose Customer, model that has many products, @foreach ($customer->products()->paginate(10,['*'],'anotherPageParam') as $product) And then for links, {!! $customer->products()->paginate(10,['*'],'anotherPageParam')->links() !!}Tributary
This is way more elegant and cleaner than the accepted answer, thanks!Akers
Works in laravel 8 as well. ThanksIcecold
What is the second param for?Keats
H
30

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();
Hime answered 28/8, 2014 at 16:4 Comment(0)
G
28

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!

Gent answered 6/6, 2014 at 19:39 Comment(7)
I was not able to get anything working using the method you described. Where you?Tureen
$collection2->setPageName('other_page'); >>> call_user_func_array() expects parameter 1 to be a valid callback, class 'Illuminate\Support\Collection' does not have a method 'setPageName'Hime
To the downvoters, consider that this answer might refer to a different Laravel version from the one you're using now and what worked then might not work in a newer version. Just saying.Gent
@DamienPirsy Do you know if this still works in laravel 5.6 ? I know it has been a whileElviselvish
I don't think this answer ever worked, I've checked back in versions and you must provide the page name on ::paginate for it to take effect, always haveTrainbearer
i think its currently in AbstractPaginator.php also there is a getPageName()Quip
and yes its still working setPageName()/getPageName() laravel 5.8Quip
U
24

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()}}    
Unreality answered 6/12, 2016 at 7:48 Comment(2)
I can confirm that this works on Laravel 5.8...just implemented it myself, thank youEphemeron
this is the correct answer in laravel 8 tooHoffmann
G
12

Laravel 5.7

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

10 = Max items per page

['*'] = colums

paramName = pagination param name

Illuminate\Database\Eloquent\Builder

Grace answered 24/10, 2018 at 14:30 Comment(0)
Q
6

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'

Quirinal answered 12/6, 2016 at 18:10 Comment(0)
T
4

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
Tressietressure answered 21/6, 2021 at 15:25 Comment(1)
Instead of ->appends, we can use ->withQueryString(). Something like this FirstModel::query() ->paginate(null, ['*'], 'first_model')->->withQueryString(). Works for meRecrimination
A
1

Use:

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

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

For Laravel 5.2

Ancalin answered 3/10, 2016 at 15:36 Comment(1)
Duplicate answer.Plastometer
T
1

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() !!}
Tributary answered 6/3, 2018 at 16:59 Comment(0)
C
1

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()}}
Cottager answered 1/6, 2020 at 14:24 Comment(0)
M
0

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
}
Mini answered 6/6, 2014 at 16:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.