Sort Laravel collection via ID array

Is it possible to order a collection of relations using a separate ID array while still accessing it through the connection?

There are many ChecklistItems installation, and I get this relation in the usual way:

 foreach ($list->items as $item) { // More Code Here } 

Now the desired order $item exists as a property on $list in the attribute $list->item_order , it is just an array of the identifier $item in the desired user order specific to $list , which is repeated.

Is there a possible way to order $items attached to $list based on the item_order array that the $list model has?

(I can't just add the "order" column to the "item" table, changing the b / c order based on the specific "list" relationship).

Thanks for any help!

+6
source share
2 answers

You can do it:

 $order = $list->item_order; $list->items->sortBy(function($model) use ($order){ return array_search($model->getKey(), $order); } 

You can also add an accessor attribute to your model, which does the same.

 public function getSortedItemsAttribute() { if ( ! is_null($this->item_order)) { $order = $this->item_order; $list = $this->items->sortBy(function($model) use ($order){ return array_search($model->getKey(), $order); }); return $list; } return $this->items; } 

Using:

 foreach ($list->sortedItems as $item) { // More Code Here } 

If you need this functionality in several places, I suggest creating your own Collection class:

 class MyCollection extends Illuminate\Database\Eloquent\Collection { public function sortByIds(array $ids){ return $this->sortBy(function($model) use ($ids){ return array_search($model->getKey(), $ids); } } } 

Then, to actually use the newCollection() override class in your model. In this case, it will be in the ChecklistItems class:

 public function newCollection(array $models = array()) { return new MyCollection($models); } 
+8
source

You can try to set up relationships that return results in the order for which you are looking. You should still be able to load relationships and have the results in that order. It is all assumed that the item_order field is a list of strings with a comma identifier.

 public function itemsOrdered() { /** * Adds conditions to the original 'items' relationship. Due to the * join, we must specify to only select the fields for the related * table. Then, order by the results of the FIND_IN_SET function. */ return $this->items() ->select('checklist_items.*') ->join('checklists', 'checklist_items.checklist_id', '=', 'checklists.id') ->orderByRaw('FIND_IN_SET(checklist_items.id, checklists.item_order)'); } 

Or, if you do not want to hardcode tables / fields:

 public function itemsOrdered() { $orderField = 'item_order'; $relation = $this->items(); $parentTable = $relation->getParent()->getTable(); $related = $relation->getRelated(); $relatedTable = $related->getTable(); return $relation ->select($relatedTable.'.*') ->join($parentTable, $relation->getForeignKey(), '=', $relation->getQualifiedParentKeyName()) ->orderByRaw('FIND_IN_SET('.$related->getQualifiedKeyName().', '.$parentTable.'.'.$orderField.')'); } 

Now you can:

 $list = Checklist::with('items', 'itemsOrdered')->first(); var_export($list->item_order); var_export($list->items->lists('id')); var_export($list->itemsOrdered->lists('id')); 

Just a warning: this is pretty experimental code. It works with a small amount of test data that is available to me, but I have not done anything like this in a production environment. In addition, this is checked only for HasMany relationships. If you try this exact code on BelongsToMany or something like that, you will have problems.

+1
source

Source: https://habr.com/ru/post/981473/


All Articles