Laravel 9, split collection by column value - laravel

I have a table with types, and second table "cleanings" with a related type_id column.
I get a collection of all cleanings:
$cleanings= Cleaning::with(['propierty'])->get();
And now, I need to split this collection in many collections(or arrays), one for every type_id.
Is there any trick to do it? I can't find any method in collections page for that.

$array = [];
foreach($cleanings as $cleaning) {
$array[$cleaning->type_id][] = $cleaning;
}
This'll give you an array of arrays, keyed by type_id.

Related

How can i sort a laravel's elequent list by another ids

I have a challenge that if I want to sort the records when getting using Laravel's ORM based on a list of IDs, how should I do it?!!!!!!!
I mean :
Suppose we have a table called users, which contains 100 records and each record has a unique ID.
We also have an array of IDs.
$ids = [4,1,2,3]
Now I want to get the list of users, but only the users who are first in the ids array and secondly according to the same order as they are listed in this array.
User::whereIn('id' , $Ids)->sortBy('id',$ids)->get();
Can you think of a solution to do this?
User::whereIn('id' , $Ids)->sortBy('id',$ids)->get();
The collections sortBy() function can take a custom call back this way:
$users = User::whereIn('id', $Ids)->get()
->sortBy(function($user, $key) use($ids) {
return array_search($user->id, $ids);
});
This will sort your collection according to the given array.
You can also reference the docs for more information.
Note that the sortBy() function must act upon a collection, which means that the get() function must come before it.

SOLVED: Looking for a smarter way to sync and order entries in Laravel/Eloquent pivot table

In my Laravel 5.1 app, I have classes Page (models a webpage) and Media (models an image). A Page contains a collection of Media objects and this relationship is maintained in a "media_page" pivot table. The pivot table has columns for page_id, media_id and sort_order.
A utility form on the site allows an Admin to manually associate one or more Media items to a Page and specify the order in which the Media items render in the view. When the form submits, the Controller receives a sorted list of media ids. The association is saved in the Controller store() and update() methods as follows:
[STORE] $page->media()->attach($mediaIds);
[UPDATE] $page->media()->sync($mediaIds);
This works fine but doesn't allow me to save the sort_order specified in the mediaIds request param. As such, Media items are always returned to the view in the order in which they appear in the database, regardless of how the Admin manually ordered them. I know how to attach extra data for the pivot table when saving a single record, but don't know how to do this (or if it's even possible) when passing an array to attach() or sync(), as shown above.
The only ways I can see to do it are:
loop over the array, calling attach() once for each entry and passing along the current counter index as sort_order.
first detach() all associations and then pass mediaIds array to attach() or sync(). A side benefit would be that it eliminates the need for a sort_order column at all.
I'm hoping there is an easier solution that requires fewer trips to the database. Or am I just overthinking it and, in reality, doing the loop myself is really no different than letting Laravel do it further down the line when it receives the array?
[SOLUTION] I got it working by reshaping the array as follows. It explodes the comma-delimited 'mediaIds' request param and loops over the resulting array, assigning each media id as the key in the $mediaIds array, setting the sort_order value equal to the key's position within the array.
$rawMediaIds = explode(',', request('mediaIds'));
foreach($rawMediaIds as $mediaId) {
$mediaIds[$mediaId] = ['sort_order' => array_search($mediaId, $rawMediaIds)];
}
And then sorted by sort_order when retrieving the Page's associated media:
public function media() {
return $this->belongsToMany(Media::class)->orderBy('sort_order', 'asc');
}
You can add data to the pivot table while attaching or syncing, like so:
$mediaIds = [
1 => ['sort_order' => 'order_for_1'],
3 => ['sort_order' => 'order_for_3']
];
//[STORE]
$page->media()->attach($mediaIds;
//[UPDATE]
$page->media()->sync($mediaIds);

Laravel - How to extract one field for items in a model to an array?

I have a table that has a one-to-many relationship with another table.
Call them tables parent and child
child has a field called field1
I am trying to get an array of all field1 values.
In the parent Model, I have this function that gets me all children of parent
public function children()
{
return $this->hasMany(Child::class);
}
Now, in the parent model, I also want to get all field1 values.
I can get all the children like so:
$children = $this->children->all();
But I just can't figure out how to then index into this to get all the field1 values.
Maybe it is better to just use $this->children in which case it is a Collection and then use some sort of Collection method to extract field1?
I tried
$this->children->only(['field1'])
but that returned nothing, even though field1 certain exists.
Ideas?
Thanks!
You can use pluck method of collecion to get an array of field1 values:
$parent->child->pluck('field1');
Or better, you can use pluck method of query builder (see section Retrieving A List Of Column Values), being even more efficient, with same result:
$parent->child()->pluck('field1');
map() is the function you are looking.
$children = $this->children->all()->map(function($children) {
return $children->field1;
});
A shorter version using Higher Order Message.
$children = $this->children->all()->map->field1;

Sort Collection By id set

I have an array of ids that i want to sort a given column by them in the collection.
For example,
$suppliersOrder = [8,7,5,1,3,4];
$items = Items::get()->sortBy(function($model) use ($suppliersOrder) {
return array_search($model->supplier_id, $suppliersOrder);
})->values();
This acts like ordering items as [1,3,4,5,7,8] instead of the given order. And if I try sortByDesc, likewise [8,7,5,4,3,1] but I couldn't figure out the way to actually sort them as my given array's order.
My ultimate goal is then running $items->groupBy('supplier.name') so I can have my desired order.
What Alexander Villalobos suggested in the comments, I changed my code like this:
$items = Items::get()->sortBy(function($model) use ($suppliersOrder) {
return rsort($model->supplier_id, $suppliersOrder);
});
Indirect modification of overloaded property App\Item::$supplier_id has no effect
$suppliersOrder = [8,7,5,1,3,4];
$items = Items::get()->sortBy(function($row,$key) use ($suppliersOrder) {
return array_search($row->supplier_id, $suppliersOrder);
});
This should give you sorted collection of items by the order you described in $suppliersOrder. As per Laravel docs, the parameters to the callback function include one being the row for the collection and another being the key of that row in the collection.

select certain columns from eloquent collection after the query has executed

Using Laravel 5.3, I have a model with the following function
public function myData() {
return $this->hasMany(MyData::class);
}
and in my collection I have the following
$my_data = MyModel->myData()->get();
All good so far. If I return $my_data I get an eloquent collection with three items.
What I need now though is to create a duplicate of that collection but containing only three of the fields.
I have tried several different things, each of which return an error. The following is the closest I have got, but this returns an empty array - I assume because the fields are located one level deeper than the collection object.
$new_collection = $my_data->only(['field_1', 'field_2', 'field_3']);
What would be the correct way to create a new collection containing all three items, each with only the three selected fields?
Thanks for your help
You could use map:
$slimmed_down = $collection->map(function ($item, $key) {
return [
'field_1' => $item->field_1,
'field_2' => $item->field_2,
'field_3' => $item->field_3
];
});
This will return a new Collection with just the values you want. As far as I know there isn't any other method that does what you want, so iterating over every item and selecting the fields this way is one of the few solutions.
The advantage of using map instead of a standard foreach loop is that when you use map it returns a new instance of Collection.
Edit:
After some thoughts and research about this, the problem you'll have created is that the all the values in the Collection aren't instances of anything anymore. If you don't mind this effect, an even prettier and faster way would be to do this:
$slimmed_down = $collection->toArray()->only(['field_1', 'field_2', 'field_3']);
This basically has the same result.
Using Laravel 9, I just had the same issue :
$my_data->only(['field_1', 'field_2', 'field_3']);
returning an empty array.
I solved it with :
$my_data->map->only(['field_1', 'field_2', 'field_3']);

Resources