首页 文章

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

提问于
浏览
8

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

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

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

现在, $item 的所需顺序作为 $list 属性 $list 下的属性存在,它只是用户所需顺序中的 $item ID数组,特定于正在迭代的 $list .

是否有一种可行的方法来根据 $list 模型具有的 item_order 数组来命令 $items 附加 $items

(我不能只在'item'表中添加'order'列,b / c顺序根据特定的'list'关系进行更改) .

谢谢你的帮助!

2 回答

  • 11

    您可以尝试设置一个关系,按照您要查找的顺序返回结果 . 您仍然可以急切加载关系,并按指定的顺序生成结果 . 这都是假设 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或类似的东西上尝试这个确切的代码,你就会遇到问题 .

  • 1

    你可以这样做:

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

相关问题