Photo of Christoph Rumpel

GroupBy multiple levels in 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.

Let's stay in touch

Sign up for my newsletter and I will let you know about more content and new projects of mine once a month.