Collections are one of the more useful but overlooked features of Laravel. Here are some quick and handy tips for manipulating your data results using them. WARNING: This is not a "best practice" page; I am only showing a few tricks that might be useful in the right place in your code. Don't run them blindly over 100,000 rows and then complain to me when your server starts smoking, m'kay?
Start by making the following table and an empty Eloquent Model class called Person for it:
CREATE TABLE `people` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `first_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `last_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `type` varchar(50) COLLATE utf8_unicode_ci NOT NULL PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; INSERT INTO `people` (`id`, `first_name`, `last_name`, `type`) VALUES (1, 'Jeff', 'Madsen', 'programmer'), (2, 'Mickey', 'Mouse', 'engineer'), (3, 'Daffy', 'Duck', 'critic'), (4, 'Mighty', 'Mouse', 'programmer');
Trick #1: find()
What? find()? That's like Day 1 Laravel. I already know everything about that.
Oh, really? Did you know:
// returns single row as Collection $collection = App\Person::find(); dd($collection); // can return multiple rows as collection $collection = App\Person::find([1, 2, 3]); dd($collection);
It can be really nice to work this way and know we are always getting a Collection returned.
Trick #2: $collection->where()
With our data above, how would you get all the programmers, all the engineers and all the critics into separate arrays? Three queries? No longer!
$collection = App\Person::all(); $programmers = $collection->where('type', 'programmer'); $critic = $collection->where('type', 'critic'); $engineer = $collection->where('type', 'engineer'); dd($engineer);
I used ::all() above, but you might just want to restrict with a where clause to make your resultset smaller first.
Trick #3: where() & lists() - WP Meta Table Blaster
You love WordPress data, don't you? Especially when you get to run 50 queries to get all the User Meta data for a single person? How about this instead?
$collection = App\Person::all(); $engineer = $collection->where('type', 'engineer')->lists('first_name'); dd($engineer);
That returns an array (UPDATE: as of v5.1, it returns a collection) because we are looking for all the engineers. In the WP example I mentioned, you'd probably do something like this (warning: untested & working from memory, but I think I have this right):
// gets all the meta records for user 1 $collection = App\WP_Meta::whereUserId(1)->get(); // I want the first & last name meta values $first_name = $collection->where('meta_key', 'first_name')->lists('value'); $last_name = $collection->where('meta_key', 'last_name')->lists('value'); ...any other fields you want to pluck
Trick #4: $collection->implode()
Every notice how marketing manages to turn a 2-D Excel spreadsheet into an 8-D Thing from the Matrix? And they want you to do it in your reports? Well, no cure for that, but here's how you can cram a one-to-many into a single cell:
$collection = App\Person::all(); $names = $collection->implode('first_name', ','); echo $names;
I'm still experimenting with that one to see nice ways of getting the first and last name together, and checking if it works correctly in relationships. Sometimes you just have to fall back on the old array_maps and loops. But it is nice to know that it is there.
Trick #5: $collection->groupBy()
If you make reports and don't know this one, you've really been missing out. Run the code below and give yourself a nice facepalm:
$collection = App\Person::all(); $grouped = $collection->groupBy('type'); dd($grouped);
Trick #6: As a union
First the code, then some words. We'll fake that we have 3 different models by just reusing the same one with a where clause.
// the point is to actually combine resutls from different models $programmers = \App\Person::where('type', 'programmer')->get(); $critic = \App\Person::where('type', 'critic')->get(); $engineer = \App\Person::where('type', 'engineer')->get(); $collection = new Collection; $all = $collection->merge($programmers)->merge($critic)->merge($engineer); dd($all);
Now the caveats: Be careful what you chose to do with this resultset. Because you are essentially combining instances of different classes, when you start looping over them you may get some surprises. Some data fields might not always be there, or there might be a mutator on one, or all kinds of mischief. It may be fine today and then someone comes along and adds a mutator to one model and messes up your results.
So either be very small and tight with this, make sure you use models that all have the same interface, or be sure you have a Unit Test and Continuous Integration - ie, use at your own risk. But as long as you are aware and cautious, it can be a helpful tool to have.
That's just a few - there are tons more, and Taylor is rolling out new additions and improvements in 5.1. Go dig in the source code a bit and try out all your crazy ideas.
Hope that helped!