通过 ID 数组对 Laravel 集合进行排序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28118361/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sorting Laravel Collection via Array of ID's
提问by Matthew Brown
Is it possible to order a relationship collection using a separate array of ID's while still accessing through the relationship?
是否可以在仍然通过关系访问的同时使用单独的 ID 数组对关系集合进行排序?
The setup is Checklist
has many ChecklistItems
, and I access this relationship the normal way:
设置Checklist
有 many ChecklistItems
,我以正常方式访问此关系:
foreach ($list->items as $item) {
// More Code Here
}
Now the desired order of $item
exists as a property on $list
under the attribute $list->item_order
, it is just an array of $item
ID's in the users desired order, specific to the $list
being iterated.
现在所需的顺序$item
作为属性$list
下的属性存在$list->item_order
,它只是$item
用户所需顺序的ID数组,特定于$list
被迭代的。
Is there a feasible way to order the $items
attached to the $list
based on the item_order
array that the $list
model has?
是否有一种可行的方法可以根据模型具有的数组来订购$items
附加到的?$list
item_order
$list
(I can't just add a 'order' column to the 'item' table, b/c order changes based on specific 'list' relationship).
(我不能只在“项目”表中添加一个“订单”列,b/c 订单会根据特定的“列表”关系进行更改)。
Thanks for any help!
谢谢你的帮助!
回答by lukasgeiter
You can do this:
你可以这样做:
$order = $list->item_order;
$list->items->sortBy(function($model) use ($order){
return array_search($model->getKey(), $order);
}
Also you could add an attribute accessor 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;
}
Usage:
用法:
foreach ($list->sortedItems as $item) {
// More Code Here
}
If you need this sort of functionality in multiple places I suggest you create your own Collection class:
如果您在多个地方需要这种功能,我建议您创建自己的 Collection 类:
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 that class override newCollection()
in your model. In this case it would be in the ChecklistItems
class:
然后,newCollection()
在您的模型中实际使用该类覆盖。在这种情况下,它将在ChecklistItems
类中:
public function newCollection(array $models = array())
{
return new MyCollection($models);
}
回答by patricus
You can try setting up a relationship that returns the results in the order for which you're looking. You should still be able to eager load the relationship, and have the results in the specified order. This is all assuming the item_order
field is a comma separated string list of ids.
您可以尝试建立一个关系,按照您要查找的顺序返回结果。您应该仍然能够预先加载关系,并按指定的顺序获得结果。这一切都假设该item_order
字段是一个逗号分隔的 id 字符串列表。
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 don't want to hardcode the 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 fairly experimental code. It looks to work with the tiny amount of test data I have available to me, but I have not done anything like this in a production environment. Also, this is only tested on a HasMany relationship. If you try this exact code on a BelongsToMany or anything like that, you'll have issues.
只是一个警告:这是相当实验性的代码。它看起来可以处理我可用的少量测试数据,但我没有在生产环境中做过这样的事情。此外,这仅在 HasMany 关系上进行测试。如果你在 BelongsToMany 或类似的东西上尝试这个确切的代码,你会遇到问题。