Сортировка коллекции Laravel через массив идентификаторов

можно ли заказать коллекцию отношений, используя отдельный массив идентификаторов, все еще получая доступ через отношения?

установка 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 ответов


вы можете сделать это:

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

также вы можете добавить атрибут accessor к вашей модели, которая делает то же самое

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
}

если нужна такая функциональность в нескольких местах я предлагаю вам создать свой собственный класс коллекции:

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

затем, чтобы фактически использовать этот класс override newCollection() в вашей модели. В этом случае это будет в ChecklistItems класс:

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

вы можете попробовать настроить отношения, которые возвращают результаты в порядке, который вы ищете. Вы все равно должны иметь возможность загружать отношения и получать результаты в указанном порядке. Это все предполагает item_order поле представляет собой разделенный запятыми список идентификаторов.

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 или что-то в этом роде, у вас будут проблемы.