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": "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.

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.