livewire: groupBy and Method Illuminate\Database\Eloquent\Collection::getKey does not exist

Description

When binding the value of an Eloquent groupBy method into a Livewire component, you receive the following error. Method Illuminate\Database\Eloquent\Collection::getKey does not exist.

The same code works fine when used in a non-livewire component, such as a component or view composer.

Steps to reproduce

  1. Create a livewire component
  2. In the mount method, try bind a property to User::all()->groupBy('created_at');

Context

  • Livewire version: [e.g. 11.0]
  • Laravel version: [e.g. 7.0.0]

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 19 (5 by maintainers)

Most upvoted comments

I can confirm this is still not working. My temporary solution is to wrap it with collect() which is an instance of Illuminate\Support\Collection instead of Illuminate\Database\Eloquent\Collection

collect(Permission::all()->groupBy('group'))

Livewire doesn’t support using groupBy on eloquent collections.

The reason for that is, they way Livewire dehydrates and rehydrates eloquent collections, is by getting all of the keys of the items in the collection, and sending them to the front end (but not the rest of the data).

When it the eloquent collection is restored on the next request, those keys are used to get the records from the database, rehydrating the eloquent collection with all the records it had before.

But when you do a groupBy, Livewire can’t serialise the eloquent collection, as it can’t call getKey on each of the items as groupBy puts them into nested collections (hence the error that is being displayed).

Livewire has no way of knowing how the collection has been grouped.

To work around this, you could use computed properties, so lets say we have a list of users that we want grouped by name, first just get all the users and assign the standard eloquent collection to a public property

public $users;

public function mount() {
    $this->users = User::all();
}

Then you could have a computed property $this->usersByName and use this in your blade view

public function getUsersByNameProperty {
    return $this->users->groupBy('name');
}

Hope this helps!

Another valid option is to use the toBase method. Example:

$query->get()->groupBy('some_column')->toBase();

@marky291 yep, but computed properties don’t get serialised, so you won’t run into that issue.

Instead the computer property will compute the group by when it gets used in your blade view. But all the data is still serialised using the non-grouped public users property (as it doesn’t touch the users property, just uses it).

Computed properties also get memoized within a request too, so it will only compute it the first time it gets called (within a single request that is).

In your blade view, you would use the computed property, not the public property, so something like this

@foreach ($this->usersByName as $name => $nameGroup)
    // Do stuff with group here
@endforeach

Hope that make sense 🙂

I have the same error. I fix it by having an array value instead of collection after grouping.

public function mount()
{
    $this->eventTypes = EventType::all()->groupBy(function ($eventType) {
        return $eventType->group;
    })->toArray();
} 

I think the issue here is the casting of data.

Issue still exists when we use collection that has groupBy applied.