FrenchFryNinja's avatar

Get the Collection behind a Paginator object

I have a pagination object and need to offer the user the ability to download the collection and its associated objects.

Without redoing the query, is there a way to access the LengthAwarePAginator's underlying collection in order to pass it to a method?

For example:

//Complex and time consuming query sets $orderIds
$results = Order::whereIn('id', $orderIds)->paginate(10);
//something like this:
$underlyingCollection = $results->toCollection();

And now I want to be able to call:

XlsFactory::QueryToXls($underlyingCollection);

and be able to access the collection behind the $results object so that I do not have to repeat a time consuming query.

I imagine that the best way it probably to extend LengthAwarePaginator with a custom implementation that will iterate over all pages like this, but all I'm getting is the first collection repeated over the total size of the initial Collection that built the Paginator:

public class MyLengthAwarePaginator extends LengthAwarePaginator
{

public function toCollection()
{
    $results = new Collection();
    $start = $this->currentPage;
    while ( $start++ <= $this->lastPage()) {
        foreach ($this->getCollection() as $item) {
            $results->push($item);
        }
        $this->setCurrentPage($start, $this->pageName);
    }
    return $results;
}

}

Please help. I know I'm missing a fundamental understanding of something here.

0 likes
8 replies
Snapey's avatar
Snapey
Best Answer
Level 122

the paginator does not have an underlying collection

It is passing your complex query to the database and also giving it an offset and a limit to grab one page's worth from the database

Your only option is to query the data and give it all to the querytoxls function, which after all, is what you are after.

1 like
jlrdw's avatar

Pagination of a collection makes zero sense what if the collection had 1 billion items it defeats the purpose of pagination. Typically in Java you put a small amount in a list and display that in a page. The pagination takes place in the background in a class. One page worth is put in a list for display at a time.

But sorry if I completely misunderstood what you meant.

1 like
FrenchFryNinja's avatar

Thank you, this is helpful. Is there a way to extract the underlying QueryBuilder or some object from the paginator?

It feels like I'd be repeating myself for no reason to rebuild the query in a different part of the controller.

Or what is a best practice to store the Eloquent QueryBuilder for access later? Via some service provider?

jlrdw's avatar

In pagination you are repeating the query each time as the offset increases thus getting the next page of data. That is how it is supposed to work. I would seriously forget about collections altogether and just concentrate on query results.

FrenchFryNinja's avatar

@jlrdw Thanks. That's basically what I'm doing now. Just taking a step back and wrapping the QueryBuilder in a different function and calling that from both endpoints. Which is totally workable.

I guess the question remains, though, is it possible to access the raw query from a Paginator? I guess it doesn't make sense to me how it works if this data is not kept somewhere and I'm having trouble finding it in the source.

jlrdw's avatar

There is no raw query behind a paginator, the query is the sql with the offset and limit. A paginator is to give pagination links. You can run the query alone:

$sql = "SELECT * FROM Orders LIMIT 10, 0";

Would be page 1 show 10

$sql = "SELECT * FROM Orders LIMIT 10, 10";

Is page 2 show 10, etc.

FrenchFryNinja's avatar

Okay, so a paginator pulls the page number from the URL and then defines its offsets that way?

That makes sense now. Thank you. As I said in my initial post, I had a fundamental misunderstanding somewhere. Now its clear.

Please or to participate in this conversation.