3

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
}
Jonathon
  • 312
  • 2
  • 10

2 Answers2

1

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.

Jonathon
  • 312
  • 2
  • 10
0

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);
  • These still return the same results. – Jonathon Jan 29 '22 at 18:59
  • Sorry, I can't comment on your answer because my reputation is still less than 50. So can continue the discussion in this thread. so is it working now? I think my third approach is the most optimal one and should be using that one, I'm not sure about the difference in collection model issue. – Kaung Khant Jan 29 '22 at 19:20
  • `$blog_post_ids = collect($posts->items())->pluck('id')->toArray(); $blog_posts = BlogPost::whereIn('id', $blog_post_ids)->get(); $this->blogPosts->push(...$blog_posts);` – Kaung Khant Jan 29 '22 at 19:23
  • I wasn't able to get it to work with your suggestions. I tried each one and each time they returned the same mixed results. To solve it I changed Illuminate\Support\Collection to Illuminate\Database\Eloquent\Collection – Jonathon Jan 29 '22 at 19:42