How to use laravel pagination with filter

5.4k Views Asked by At

I have getCanSeeAttribute function in post model, and I try to get posts with pagination using filter()

$posts = Post::where(function ($query1) use ($users) {
            $query1->where('posted_type', 'user')->whereIn('posted_id', $users);
        })
        ->orWhere(function ($query2) use ($stores) {
            $query2->where('posted_type', 'store')->whereIn('posted_id', $stores);
        })
        ->with('likes', 'postable')
        ->withCount('comments', 'likes')
        ->latest()->paginate($paginate)->filter(function($post){
            return $post->can_see == true;
        });

The problem is when I use filter, it gets data attribute only, but I need all pagination attributes.

first_page_url": "http://localhost:8000/api/timeline?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "http://localhost:8000/api/timeline?page=1",
"next_page_url": null,
"path": "http://localhost:8000/api/timeline",
"per_page": 10,
"prev_page_url": null,
"to": 6,
"total": 6

can_see not column in table it is Accessor

2

There are 2 best solutions below

4
On

First of all, I hope you know what are you doing. Assuming you need to get results that has can_see field set to true, you should rather use:

$posts = Post::where('can_see', true)
         ->where(function($q) {
            $q->where(function ($query1) use ($users) {
              $query1->where('posted_type', 'user')->whereIn('posted_id', $users);
            })->orWhere(function ($query2) use ($stores) {
               $query2->where('posted_type', 'store')->whereIn('posted_id', $stores);
            })
        })->with('likes', 'postable')
        ->withCount('comments', 'likes')
        ->latest()
        ->paginate($paginate);

As you see I additionally wrapped (where .. orWhere) in additional where closure to make sure valid query will be generated.

Otherwise you should use:

$posts = Post::where(function($q) {
            $q->where(function ($query1) use ($users) {
              $query1->where('posted_type', 'user')->whereIn('posted_id', $users);
            })->orWhere(function ($query2) use ($stores) {
               $query2->where('posted_type', 'store')->whereIn('posted_id', $stores);
            })
        })->with('likes', 'postable')
        ->withCount('comments', 'likes')
        ->latest()
        ->paginate($paginate);

$posts = $posts->setCollection($posts->getCollection()->filter(function($post){
        return $post->can_see == true;
   })
);

However it's very unlikely the 2nd way is better one in your case. Assuming you have 1 mln of matching records and then of them has can_see set to true, and the rest have it set to false, it would cause that you will get 1 mln of records from database and then you will filter only 10 of them what would be performance killer for your application.

0
On

You can add this following code, define after $post

$post->appends($request->only('posted_type'));