GroupBy multiple levels in Laravel
#laravelSince Laravel v5.5.29 you can group collections by multiple levels. Let's see what this means and how this works.
Arrange
So this article is about a new feature in Laravel. But before we check that out, let's see how it worked before. groupBy
is a method of the Collection class. For our examples I will create some data to work with. Students will be a factory state of the given User class.
// Default user factory which comes with Laravel
$factory->define(App\User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
'remember_token' => str_random(10),
];
});
// Our new students state
$factory->state(App\User::class, 'students', function (Faker $faker) {
return [
'skilllevel' => collect(['beginner', 'intermediate', 'professional'])->random(),
'teacher' => collect(['Peter', 'Markus', 'Chris'])->random(),
];
});
This new state will add some data to the users. We have a new skilllevel and a teacher value. For our examples we will use a simple route callback. This way we can easily return the result in the browser.
Route::get('/', function () {
return $students = factory(User::class)
->times(3)
->states('students')
->make();
});
This will produce our test data which looks something like this:
[
{
"name": "Jakayla Leffler",
"email": "ocie70@example.com",
"skilllevel": "professional",
"teacher": "Peter"
},
{
"name": "Gustave Cummings II",
"email": "crona.alexandrea@example.net",
"skilllevel": "intermediate",
"teacher": "Markus"
},
{
"name": "Ms. Ethelyn Bergnaum MD",
"email": "deckow.kelvin@example.net",
"skilllevel": "beginner",
"teacher": "Peter"
}
]
Basic example
I said something like
because we are using the Faker package in our factory. So every result will look different. Next we use the groupBy
method, to group our data by the skilllevel.
return $students->groupBy('skilllevel');
You will see the new keys for the skilllevel in our result.
{
"professional": [
{
"name": "Jacquelyn Kilback",
"email": "christopher55@example.net",
"skilllevel": "professional",
"teacher": "Chris"
}
],
"beginner": [
{
"name": "Miss Ophelia Ryan Jr.",
"email": "pollich.tristin@example.net",
"skilllevel": "beginner",
"teacher": "Chris"
},
{
"name": "Furman Hahn",
"email": "blowe@example.net",
"skilllevel": "beginner",
"teacher": "Markus"
}
]
}
Finally some new stuff
Till here, nothing is really new. So what is the new feature I was talking about? Patrizio T. made a pull request so that you're able to group collections by multiple levels. Let's take a look at an example for a better understanding of what that means.
return $students->groupBy(['skilllevel','teacher']);
We are passing an array instead of a string to define multiple levels. First the students should be grouped by the skilllevel and then by the teacher. So this is what we get:
{
"intermediate": {
"Peter": [
{
"name": "Jana McClure III",
"email": "oliver.pagac@example.com",
"skilllevel": "intermediate",
"teacher": "Peter"
}
],
"Chris": [
{
"name": "Rosemarie Barrows",
"email": "epurdy@example.com",
"skilllevel": "intermediate",
"teacher": "Chris"
}
]
},
"professional": {
"Markus": [
{
"name": "Katrine Streich",
"email": "carlee.koepp@example.com",
"skilllevel": "professional",
"teacher": "Markus"
}
]
}
}
This grouping can be helpful when you want to display or compare the data. In this case only Markus has a professional student. Maybe he is already a better teacher?
Going crazy
It wouldn't make sense for our case but we could group the students by their name and email as well.
return $students->groupBy(['skilllevel','teacher', 'name', 'email']);
This is some crazy nested structure, but it is possible :-)
{
"professional": {
"Peter": {
"Mrs. Ella McClure": {
"forest.bernier@example.com": [
{
"name": "Mrs. Ella McClure",
"email": "forest.bernier@example.com",
"skilllevel": "professional",
"teacher": "Peter"
}
]
}
},
"Chris": {
"Miss Assunta Predovic PhD": {
"amari.klocko@example.com": [
{
"name": "Miss Assunta Predovic PhD",
"email": "amari.klocko@example.com",
"skilllevel": "professional",
"teacher": "Chris"
}
]
}
}
},
"intermediate": {
"Markus": {
"Keshawn Crona DVM": {
"vkilback@example.org": [
{
"name": "Keshawn Crona DVM",
"email": "vkilback@example.org",
"skilllevel": "intermediate",
"teacher": "Markus"
}
]
}
}
}
}
Conclusion
I really like how simple it is now to group a collection by multiple values. The syntax is straight forward and it works really well. Do you got other good examples for this new feature? Let me know about it on Twitter.