48

My Laravel pagination output is like laravel pagination used to be, but I need to change the data array for each object. My output is:

Output

As you can see, the data object has 2 items, which I need to change.

My code is:

$items = $this->items()
        ->where('position', '=', null)
        ->paginate(15);

Which returns the user items with pivot table, but I don't like the way the pivot table is shown in the JSON, so I decided to change the items and organize each item with the pivot before the item.

For this purpose, I tried to use foreach

foreach ($items->data as $item)
        {

        }

which giving my an error, for a reason I don't know:

Undefined property: Illuminate\Pagination\LengthAwarePaginator::$data"
status_code: 500

Any help?

TheUnreal
  • 23,434
  • 46
  • 157
  • 277

13 Answers13

141

The paginator's items is a collection. You can grab it and transform the data like so:

$paginator = $this->items()->where('position', '=', null)->paginate(15);
$paginator->getCollection()->transform(function ($value) {
    // Your code here
    return $value;
});

If you are familiar with tap helper here is the snippet that does exact same.

$paginator = tap($this->items()->where('position', '=', null)->paginate(15),function($paginatedInstance){
    return $paginatedInstance->getCollection()->transform(function ($value) {
        return $value;
    });
});

We can't chain method getCollection to paginator instance because AbstractPaginator will return paginator's underlying collection. so the paginator instance will be transformed to Collection. So fix that we can use tap helper.

ManojKiran A
  • 5,896
  • 4
  • 30
  • 43
Murwa
  • 2,238
  • 1
  • 20
  • 21
52

If you'd like to keep items paginated:

        $itemsPaginated = $this->items()
             ->paginate(15);

        $itemsTransformed = $itemsPaginated
            ->getCollection()
            ->map(function($item) {
                return [
                    'id' => $item->id,
                ];
        })->toArray();

        $itemsTransformedAndPaginated = new \Illuminate\Pagination\LengthAwarePaginator(
            $itemsTransformed,
            $itemsPaginated->total(),
            $itemsPaginated->perPage(),
            $itemsPaginated->currentPage(), [
                'path' => \Request::url(),
                'query' => [
                    'page' => $itemsPaginated->currentPage()
                ]
            ]
        );
Akash Kumar Verma
  • 3,185
  • 2
  • 16
  • 32
hellovoid
  • 611
  • 6
  • 6
  • 2
    Thanks so much for this, I think this should be the accepted answer. Mainly because pagination functionality will continue to work in your views and you can still call functions such as "appends" and "links" in your blade files. These functions won't be available if you are getting a Collection passed to your blade views. – Carlton Nov 27 '18 at 11:36
  • @hellovoid , Great Answer , You have saved my day :) – Saurabh Mistry Mar 05 '19 at 20:19
  • Best answer ever! – realtebo Jul 31 '19 at 13:59
  • I suggest to remove `->toArray()` after transormation, to keep unaltered the object kind of the items. I explain: the paginator has a Collection as 'items'; if your use `toArray()`, the new Paginator works, but in the view you will not be able to access items attributes using object notation – realtebo Oct 24 '19 at 08:45
  • This is the best answer. – graphicmist May 06 '21 at 07:50
  • You should use `'query' => \Request::query()` – Luke Vincent Aug 02 '21 at 15:41
  • I'm using `with('countries')`, with the result `'country' => [ 'country_id => 10, 'countryname' => 'Australia']`, how to get that `countryname` in map? – Fahmi Oct 04 '21 at 06:35
  • This helped me because it taught me how to create a LengthAwarePaginator from scratch, useful for when creating highly customized cases such as when wanting the items to be groupedBy something - I want to keep the meta info in-tact, but only the items are grouped – jcsoriano Apr 02 '22 at 03:22
  • 1
    Laravel 8.9.0 has added the `through` method, which will keep the items paginated. `$itemsPaginated->through(function ($item) { return ['id' => $item->id ]; });` See my answer. – dragonfire Jun 03 '22 at 13:56
43

There is a setCollection method for such purpose.

$items = Model::paginate(10);

$updatedItems = $items->getCollection();

// data manipulation
// ...

$items->setCollection($updateItems);

From the source code of /Illuminate/Pagination/AbstractPaginator.php

/**
* Set the paginator's underlying collection.
*
* @param  \Illuminate\Support\Collection  $collection
* @return $this
*/
public function setCollection(Collection $collection)
{
    $this->items = $collection;

    return $this;
}

Source

Scofield
  • 4,195
  • 2
  • 27
  • 31
  • 1
    this does not update other vital properties like `total` and `lastPage`, so it breaks pagination. – user3803848 Aug 27 '20 at 11:22
  • 4
    @user3803848 yeah you only want to update the collection for those data based on the current pagination data. Why do you want to change the total and lastPage? – Syazany Oct 05 '21 at 07:16
4

Laravel 8.9.0 has added the through method to AbstractPaginator.
It transforms each item in the slice of items using a callback, and keeps the items paginated.

$paginator = $this->items()->where('position', '=', null)->paginate(15);
$paginator->through(function ($value) {
    // Your code here
    return $value;
});

The source code:

/**
 * Transform each item in the slice of items using a callback.
 *
 * @param  callable  $callback
 * @return $this
 */
public function through(callable $callback)
{
    $this->items->transform($callback);

    return $this;
}
dragonfire
  • 407
  • 7
  • 16
3

I could make shorter way. This returns edited $array instead of simple $paginated. This example modify file names. This doc was useful for me.

    $paginated=$query->paginate(12);
    $array=$paginated->toArray();
    foreach ($array['data'] as $r=>$record) {
        $array['data'][$r]->gif=$array['data'][$r]->gif.".gif";
    }
    return $array;
umihico
  • 179
  • 2
  • 5
3

Sample Example :

    $franchiseData=[ 'id'=>1 ,'name'=>'PAnkaj'];
    $models = $bookingsQuery->paginate(10);
    $models->setCollection(collect($franchiseData));
    return  $models;

Note that $models->setCollection(collect($franchiseData)); you have to use collect() else you will get error.

pankaj
  • 1
  • 17
  • 36
2
getCollection

is one way to get the items. Another way is to use this For ex- Assuming, user doesn't have name param and only have first_name and last_name

$userPaginatedData = User::paginate(15);
$users = $userPaginatedData->items();

foreach($users as $user) {
   $user->name = $user->first_name . ' ' . $user->last_name;
}


return $userPaginatedData;

Now in the data key, you would see that each user has name param with it.

Koushik Das
  • 9,678
  • 3
  • 51
  • 50
1

-Laravel 5.4

// example update column "photo" 
// from "/path/to/photo.png" 
// to "abc.com/path/to/photo.png"
foreach ($items as $item)
{
    $path = $item->photo;

  // Remove 
  $item->offsetUnset("photo");

  // Set
  $item->offsetSet("photo", url($path));
}
Dzung Nguyen
  • 67
  • 1
  • 9
0

Laravel AbstractPaginator has methods setCollection() and getCollection()

<?php

$itemsPaginated = $this->items()->paginate(15);
$itemsPaginated->setCollection(
    $itemsPaginated->getCollection()->transform(function ($item) {
       // Your code here
       return $item;
    })
)
  • I use filter() instead of transform, and it works: getCollection()->filter(function ($item) { // Your code here return $item; }) – Noname Aug 04 '22 at 07:14
0

This is your paginated items...

$items = $this->items()
        ->where('position', '=', null)
        ->paginate(15);

I am using Laravel 8, can simply use each

$items->each(function ($item) {
 // your code here
 $item->custom_data = calcSomeData(); // sample
});

It does the same as this...

$items->getCollection()->transform(function ($item) {
 // your code here
 $item->custom_data = calcSomeData(); // sample
});
Kidd Tang
  • 1,821
  • 1
  • 14
  • 17
-1
<?php 

$itemsPaginated = $this->items()->paginate(15);

$itemsPaginated = json_encode($itemsPaginated);

foreach ($itemsPaginated->data as $key => $item) {
    $results->data[$key]; //Modify
}

$itemsPaginated = json_encode($results);

dipenparmar12
  • 3,042
  • 1
  • 29
  • 39
  • 3
    Please don't post only code as an answer, but include an explanation what your code does and how it solves the problem of the question. Answers with an explanation are generally of higher quality, and are more likely to attract upvotes. – Mark Rotteveel Feb 10 '20 at 11:01
  • This will make loose all the information about pagination, eg: current_page, total pages... – Angus Simons Mar 20 '20 at 16:39
-11

you have to use below code in your blade

{!! $items->render() !!}
Veerendra Borra
  • 1,286
  • 14
  • 24
-11

Ignore the pagination in laravel and hit the normal data

foreach ($items as $item)
{

}
Taha Paksu
  • 15,371
  • 2
  • 44
  • 78
hamedkke
  • 47
  • 1
  • 3