2015-01-23 3 views
6

Можно ли заказать коллекцию отношений, используя отдельный массив идентификаторов, все еще получая доступ через отношения?Сортировка коллекции 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 модели имеет?

(Я не могу просто добавить столбец «заказ» в таблицу «item», изменения порядка b/c на основе определенного отношения «список»).

Спасибо за помощь!

+0

Я не знаю, как вы это сделаете в laravel, но я знаю, что в прямом запросе mysql, если вы хотите что-то упорядоченное по заранее определенному порядку, вы можете использовать 'field' или' find_in_set' как часть ваш заказ и просто перечисление ценностей в любом порядке. Или вы можете использовать оператор case в select (или order), который возвращает числа и порядок в этом столбце. Если у laravel нет другого пути, единственный способ, которым я могу думать, - хранить элементы в массиве и использовать их массив, чтобы получить желаемый порядок. –

+0

@Matthew Brown '$ list-> item_order' упорядочен массив элементов ids ?? – voodoo417

+0

@ voodoo417 да, это правильно –

ответ

8

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

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

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

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

Я в конечном итоге использовал этот метод, отлично работает. Я понятия не имел, что функция sortBy также принимает закрытие. –

1

Вы можете попробовать настройки отношения, которая возвращает результаты в порядке, для которого вы ищете. Вы все равно должны иметь возможность загружать отношения и иметь результаты в указанном порядке. Это все предполагается, что поле 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 или что-нибудь в этом роде, у вас появятся проблемы.

+0

Спасибо, что нашли время, чтобы написать это для меня :) –

Смежные вопросы