I have a news page which is being loaded by Vue and Vue Isotope. Vue isotope requires that my data is an array so that I can append more articles to the isotope when the user clicks the “Load more” button. The way this works is, when the user clicks “load more”, using Laravel’s pagination function, I can simply append the next load of articles to my existing array of articles in my Vue instance.
The issue I am having is that my Laravel collection always returns an object instead of an array, even when I use toArray()
.
If I load my articles straight from eloquent using get()
, this works fine and is an array, however because I need to merge multiple eloquent queries together, I am forced to use a collection and merge()
to merge 4 eloquent queries into one large collection. This is what I believe is forcing the collection to always return as an object.
Below is my function from the controller:
public function apiIndex()
{
$articles = Article::latest('featuredArticle')->latest()->published()->get();
$twitterPosts = TwitterPost::where('published', 'published')->get();
$facebookPosts = FacebookPost::where('published', 'published')->get();
$vimeoPosts = VimeoPost::where('published', 'published')->get();
$articles->map(function ($post) {
$post['type'] = 'Article';
return $post;
});
$twitterPosts->map(function ($post) {
$post['type'] = 'Twitter';
return $post;
});
$facebookPosts->map(function ($post) {
$post['type'] = 'Facebook';
return $post;
});
$vimeoPosts->map(function ($post) {
$post['type'] = 'Vimeo';
return $post;
});
$allPosts = collect();
$allPosts = $allPosts->merge($articles);
$allPosts = $allPosts->merge($twitterPosts);
$allPosts = $allPosts->merge($facebookPosts);
$allPosts = $allPosts->merge($vimeoPosts);
$allPosts = $allPosts->sortByDesc('created_at')->paginate(15);
return response()->json($allPosts);
}
The closest I have been able to get is by adding ->values()
to the end of $allPosts
however this removes the pagination link for the next page and only retrieves the actual posts so I cannot use this.
Here is a small snippet for retrieving the initial $allPosts
data into Vue
mounted () {
axios.get(this.page)
.then(response => ([
this.list = response.data.data,
this.page = response.data.next_page_url
]))
},
Here is a snippet of what method is called when the user clicks the “Load more” button
loadMore: function() {
axios.get(this.page)
.then(response => ([
this.paginatedList = response.data.data,
this.list.push(this.list, this.paginatedList),
this.page = response.data.next_page_url
]))
},
Because the response is an object, using this.list.push()
does not work and throws an error because this.list
should be an array.
Additionally, here is a snippet of my Vue data object
data: {
page: "{{route('api-news')}}",
list: [
],
paginatedList: [
],
},
Image of my list object (where list should be an array instead):
How would I be able to force $allPosts
to return as an array?
3
Answers
I would try adding a call to
values()
before thepaginate()
instead of after.The way Vue determines whether an array is treated as an array vs an object is by looking at the keys: if they are numeric, zero-indexed and sequential, it will treat it an array, otherwise, it treats it as an object.
In your case the
sortByDesc()
collection method maintains the original keys so Vue sees non-sequential keys and handles it as an object. By running your sorted collection throughvalues()
you should get a correctly indexed collection to pass to Vue.Update:
Based on your comment that the above solution is only working for the first page of results but not the others, I suspect that the
paginate()
method is also maintaining the original keys. So the first chunk has keys starting at 0, but the second chunk has keys starting at 15. You may need to override that so that each chunk gets re-indexed to 0.You may also be able to do this in your Vue code before pushing the results into your model. Something like
response.data.list.filter(item => item)
might work.Coming across this issue as well, as @Graeme states, this works on the first page, but not the second. In the end I ended up manually overriding the items in the
LengthAwarePaginator
and resetting the array indexes.Another way: