Interested how Laravel works under the hood? → Check my Laravel Core Adventures Course

GroupBy multiple levels in Laravel

#laravel

Since 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();

});
Note: We use the make method instead of the create method, because we have no database set up.

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.

Do you enjoy my posts?

Sign up for my newsletter to receive updates on my latest content.

You will receive monthly updates on my latest articles and products. I do care about the protection of your data. Read my Privacy Policy.