I'm implementing infinite pagination into a Livewire component and I'm noticing that on initial page load it's loading objects, but when I scroll and load more data its adding arrays to the collection so I'm ending up with a collection with mixed values of array and object. Any idea how to solve this so that its only objects? The reason I'd prefer to have all objects is I'm using a method in the view which I can't access if it's an array.
/**
* Loads more blog posts.
*
* @return void
*/
public function loadBlogs()
{
if ($this->hasMorePages !== null && !$this->hasMorePages) {
return;
}
if ($this->category_id) {
$posts = BlogPost::where('user_id', request()->user()->id)
->where('category_id', $this->category_id)
->with('category')
->orderBy('id', 'desc')
->cursorPaginate(16, ['*'], 'cursor', Cursor::fromEncoded($this->nextCursor));
} else {
$posts = BlogPost::where('user_id', request()->user()->id)
->with('category')
->orderBy('id', 'desc')
->cursorPaginate(16, ['*'], 'cursor', Cursor::fromEncoded($this->nextCursor));
}
$this->blogPosts->push(...$posts->items());
$this->hasMorePages = $posts->hasMorePages();
if ($this->hasMorePages === true) {
$this->nextCursor = $posts->nextCursor()->encode();
}
}
The dd:
Illuminate\Support\Collection {#1751 ▼
#items: array:32 [▼
0 => array:10 [▶]
1 => array:10 [▶]
2 => array:10 [▶]
3 => array:10 [▶]
4 => array:10 [▶]
5 => array:10 [▶]
6 => array:10 [▶]
7 => array:10 [▶]
8 => array:10 [▶]
9 => array:10 [▶]
10 => array:10 [▶]
11 => array:10 [▶]
12 => array:10 [▶]
13 => array:10 [▶]
14 => array:10 [▶]
15 => array:10 [▶]
16 => App\Models\BlogPost {#1775 ▶}
17 => App\Models\BlogPost {#1776 ▶}
18 => App\Models\BlogPost {#1777 ▶}
19 => App\Models\BlogPost {#1778 ▶}
20 => App\Models\BlogPost {#1779 ▶}
21 => App\Models\BlogPost {#1780 ▶}
22 => App\Models\BlogPost {#1781 ▶}
23 => App\Models\BlogPost {#1782 ▶}
24 => App\Models\BlogPost {#1783 ▶}
25 => App\Models\BlogPost {#1784 ▶}
26 => App\Models\BlogPost {#1785 ▶}
27 => App\Models\BlogPost {#1786 ▶}
28 => App\Models\BlogPost {#1787 ▶}
29 => App\Models\BlogPost {#1788 ▶}
30 => App\Models\BlogPost {#1789 ▶}
31 => App\Models\BlogPost {#1790 ▶}
]
#escapeWhenCastingToString: false
}
CodePudding user response:
I was using Illuminate\Support\Collection, I changed to the Illuminate\Database\Eloquent\Collection and instead of using collect() to set the initial collection I used new Collection()
The results was this:
Illuminate\Database\Eloquent\Collection {#1805 ▼
#items: array:32 [▼
0 => App\Models\BlogPost {#1789 ▶}
1 => App\Models\BlogPost {#1790 ▶}
2 => App\Models\BlogPost {#1791 ▶}
3 => App\Models\BlogPost {#1792 ▶}
4 => App\Models\BlogPost {#1793 ▶}
5 => App\Models\BlogPost {#1794 ▶}
6 => App\Models\BlogPost {#1795 ▶}
7 => App\Models\BlogPost {#1796 ▶}
8 => App\Models\BlogPost {#1797 ▶}
9 => App\Models\BlogPost {#1798 ▶}
10 => App\Models\BlogPost {#1799 ▶}
11 => App\Models\BlogPost {#1800 ▶}
12 => App\Models\BlogPost {#1801 ▶}
13 => App\Models\BlogPost {#1802 ▶}
14 => App\Models\BlogPost {#1803 ▶}
15 => App\Models\BlogPost {#1804 ▶}
16 => App\Models\BlogPost {#1786 ▶}
17 => App\Models\BlogPost {#1787 ▶}
18 => App\Models\BlogPost {#1788 ▶}
19 => App\Models\BlogPost {#1717 ▶}
20 => App\Models\BlogPost {#1764 ▶}
21 => App\Models\BlogPost {#1755 ▶}
22 => App\Models\BlogPost {#1813 ▶}
23 => App\Models\BlogPost {#1814 ▶}
24 => App\Models\BlogPost {#1815 ▶}
25 => App\Models\BlogPost {#1816 ▶}
26 => App\Models\BlogPost {#1817 ▶}
27 => App\Models\BlogPost {#1818 ▶}
28 => App\Models\BlogPost {#1819 ▶}
29 => App\Models\BlogPost {#1820 ▶}
30 => App\Models\BlogPost {#1821 ▶}
31 => App\Models\BlogPost {#1822 ▶}
]
#escapeWhenCastingToString: false
}
**Edit
Using Illuminate\Database\Eloquent\Collection comes with performance issues. Here is what the query looks like:
select * from
'blog posts' where 'blog_posts'. 'id' in (10103, 10102, 10101, 10100, 10099, 10098,
10097, 10096, 10095, 10094, 10093, 10092, 10091, 10090, 10089, 10088, 10087, 10086, 10085, 10084
10083, 10082, 10081, 10080, 10079, 10078, 10077, 10076, 10075, 10074, 10073, 10072, 10071, 10070,
10069, 10068, 10067, 10066,
10065, 10064, 10063, 10062,
10061, 10060, 10059, 10058, 10057, 10056,
10055, 10054, 10053, 10052, 10051, 10050, 10049, 10048, 10047, 10046, 10045, 10044, 10043, 10042,
10041, 10040, 10039, 10038, 10037, 10036, 10035, 10034, 10033, 10032, 10031, 10030, 10029, 10028,
10027, 10026, 10025, 10024, 10023, 10022, 10021, 10020, 10019, 10018, 10017, 10016, 10015, 10014,
10013, 10012, 10011, 10010, 10009, 10008, 10007, 10006, 10005, 10004, 10003, 10002, 10001, 10000,
9999, 9998, 9997, 9996, 9995, 9994, 9993, 9992, 9991, 9990, 9989, 9988, 9987, 9986, 9985, 9984, 9983,
9982.
9981, 9980, 9979, 9978, 9977, 9976)
Each time you go forward a page this query grows bigger and bigger which is where the performance issue is.
I decided to stick with the Illuminate\Support\Collection and work with arrays, the query looks like this:
select * from `blog_posts` where `user_id` = 1 and (`id` < 9848) order by `id` desc limit 17
Which seems to be much more performant.
CodePudding user response:
Not sure if it's the most optimal or elegant solution but you can try converting the arrays to object like so...
foreach($posts->items() as $post) {
$this->blogPosts->push(new BlogPost($post));
}
Edit: Whooops... I think the following logic is more correct.
foreach($posts->items() as $post) {
$this->blogPosts->push(BlogPost::find($post->id));
}
Edit: If you don't feel good about running find query for every record. This approach could be faster.
$blog_post_ids = collect($posts->items())->pluck('id')->toArray();
$blog_posts = BlogPost::whereIn('id', $blog_post_ids)->get();
$this->blogPosts->push(...$blog_posts);
