通过ID数组对Laravel Collection进行排序

时间:2015-01-23 20:39:40

标签: php laravel

是否可以在仍然通过关系访问时使用单独的ID数组来订购关系集合?

设置Checklist有很多ChecklistItems,我以正常方式访问此关系:

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

现在$item的所需顺序作为属性$list$list->item_order的属性存在,它只是$item ID中的一个数组{用户所需的订单,特定于正在迭代的$list

是否有可行的方法根据$items模型所拥有的$list数组订购item_order附加的$list

(我无法在'项目表中添加'订单'列,b / c订单根据特定的'列表更改'关系)。

感谢您的帮助!

2 个答案:

答案 0 :(得分:11)

你可以这样做:

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

您还可以向模型添加属性访问器,该属性访问器执行相同的

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;
}

用法:

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

如果您需要在多个地方使用此类功能,我建议您创建自己的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);
        }
    }
}

然后,在模型中实际使用该类覆盖newCollection()。在这种情况下,它将在ChecklistItems类:

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

答案 1 :(得分:1)

您可以尝试设置一个关系,按照您要查找的顺序返回结果。您仍然可以急切加载关系,并按指定的顺序生成结果。这是假设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)');
}

或者,如果您不想对表格/字段进行硬编码:

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.')');
}

现在,您可以:

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

只是一个警告:这是相当实验性的代码。它看起来可以使用我可用的少量测试数据,但我在生产环境中没有做过这样的事情。此外,这仅在HasMany关系上进行测试。如果你在BelongsToMany或类似的东西上尝试这个确切的代码,你就会遇到问题。