Graeme1995's avatar

Laravel collection always returns an object instead of an array

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: [

            ],
},

How would I be able to force $allPosts to return as an array?

0 likes
5 replies
jlrdw's avatar

There is a toArray() method. I.e.

    public function testg() {
        $dogid = Request::input('somevar');
        $dog = Dog::select('dogname', 'comments')
                ->where('dogid', '<', $dogid)
                ->get()
                ->toArray();   ///////////here
        return Response::json($dog);  /// whatever you need
    }
Graeme1995's avatar

@douglasakula I believe i'm already paginating the collection correctly. @jlrdw I have tried toArray() however this doesn't make a difference in this instance.

The closest I've gotten to resolving this issue is doing

        $allPosts = $allPosts->sortByDesc('created_at')->values()->paginate(15);

        return response()->json($allPosts);

This returns the data as an array on the first page, however on the second page the data is still returned as an object.

1 like
Deekshith's avatar

@graeme1995 any solution you got it for this? i am facing the same problem. it would be really helpful if you paste the code here. i am getting correct array structure in first page but in second page i am getting in objects.

burlresearch's avatar

This is a perfect candidate for which to use an Eloquent: API Resource.

In fact, because you have a hybrid model here you could even use 5 API Resources. One each, to model the 4 pure models, and a 5th to model the hybrid representation that you want to model in your API response.

I have actually built a system very similar to what you're working on, and this is how I do it. The API resources make a perfect solution, not just for exporting to your front-end, but also later for flat-file export and BI-layer consumption.

Please or to participate in this conversation.