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": "[email protected]",
"skilllevel": "professional",
"teacher": "Peter"
},
{
"name": "Gustave Cummings II",
"email": "[email protected]",
"skilllevel": "intermediate",
"teacher": "Markus"
},
{
"name": "Ms. Ethelyn Bergnaum MD",
"email": "[email protected]",
"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": "[email protected]",
"skilllevel": "professional",
"teacher": "Chris"
}
],
"beginner": [
{
"name": "Miss Ophelia Ryan Jr.",
"email": "[email protected]",
"skilllevel": "beginner",
"teacher": "Chris"
},
{
"name": "Furman Hahn",
"email": "[email protected]",
"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": "[email protected]",
"skilllevel": "intermediate",
"teacher": "Peter"
}
],
"Chris": [
{
"name": "Rosemarie Barrows",
"email": "[email protected]",
"skilllevel": "intermediate",
"teacher": "Chris"
}
]
},
"professional": {
"Markus": [
{
"name": "Katrine Streich",
"email": "[email protected]",
"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": {
"[email protected]": [
{
"name": "Mrs. Ella McClure",
"email": "[email protected]",
"skilllevel": "professional",
"teacher": "Peter"
}
]
}
},
"Chris": {
"Miss Assunta Predovic PhD": {
"[email protected]": [
{
"name": "Miss Assunta Predovic PhD",
"email": "[email protected]",
"skilllevel": "professional",
"teacher": "Chris"
}
]
}
}
},
"intermediate": {
"Markus": {
"Keshawn Crona DVM": {
"[email protected]": [
{
"name": "Keshawn Crona DVM",
"email": "[email protected]",
"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.