2016-11-04 133 views
2

我有两个表magazines,其中一个字段为product_code,另一个表为issues。他们有belongsToMany的关系。Laravel - 按关系对象的属性对集合进行排序

杂志模特:

public function issues() 
{ 
    return $this->hasMany('App\Issue'); 
} 

发行模式:

public function magazine() 
{ 
    return $this->belongsTo('App\Magazine'); 
} 

目前,我有一个查询从哪里获得的由杂志ID分组和最后一个问题的日期排序问题的集合。

$issues = Issue::orderBy('date', 'desc')->get()->groupBy('magazine_id'); 

这是我的查询结果的样子:

Collection {#431 ▼ 
    #items: array:23 [▼ 
    103 => Collection {#206 ▼ 
     #items: array:52 [▶] 
    } 
    106 => Collection {#216 ▶} 
    124 => Collection {#452 ▶} 
    112 => Collection {#451 ▶} 
    115 => Collection {#450 ▶} 
    123 => Collection {#449 ▶} 
    107 => Collection {#448 ▶} 
    113 => Collection {#447 ▶} 
    117 => Collection {#446 ▶} 
    109 => Collection {#445 ▶} 
    110 => Collection {#444 ▶} 
    121 => Collection {#443 ▶} 
    120 => Collection {#442 ▶} 
    114 => Collection {#441 ▶} 
    116 => Collection {#440 ▶} 
    118 => Collection {#439 ▶} 
    126 => Collection {#438 ▶} 
    125 => Collection {#437 ▶} 
    119 => Collection {#436 ▶} 
    122 => Collection {#435 ▶} 
    105 => Collection {#434 ▶} 
    111 => Collection {#433 ▶} 
    104 => Collection {#432 ▶} 
    ] 
} 

那么,既然我已经24本杂志,还有数组中的issues 24个集,每collectionissues属于一个magazine。集合按每个集合的最新发布日期排序,并且每个集合中的问题也按日期排序。因此,阵列中的第一个集合将是表issues中具有最新问题的集合,第二个集合将是具有同一表中的第二个最新问题的集合,依此类推。

因为我会得到用户订阅的阵列,其中将包括product codes这样的:

$productCodes = ['aa1', 'bb2', 'cc3']; 

我需要扩大这个查询并通过$productCodes阵列,我将获得集合进一步梳理。我需要检查magazines表中productCodes阵列的代码,我在那里有product_code字段。应按magazine分组的问题集合进行排序,以便第一个集合是它们所属的magazineproductCodes中的代码具有相同product_code的集合,其中第一个集合的集合具有迄今为止的最新一期。然后其余的收藏应该按日期排序。我怎样才能做这种查询?

更新

我曾尝试与@保罗周刊的答案中建议的代码,现在我得到集合数组,与杂志的问题集合。每个杂志集合中的问题按日期排序,与$productCodes阵列具有相同product_code的杂志集合位于该数组的开头,但杂志集合的排列依然未按照最新版本的日期排序每个杂志集合。

+0

'问题:: GROUPBY( 'magazine_id' ) - > orderBy('date','desc') - > orderBy('product_codes','desc') - > get()'。按照您的意愿可以多次调用顺序。 – Andrew

+0

但问题是'product_code'不是'问题'表中的字段,它在关系表'杂志' – Marco

+0

啊,我明白了。我的错。那么:'issues :: groupBy('magazine_id') - > with('magazine'=> function($ q){$ q-> orderBy('product_codes','desc') - > get();}) - > orderBy('date','desc') - > get();'。至少可以说,我的疯狂技能有点生疏,但我认为这应该起作用。 – Andrew

回答

2

首先:如果不使用Collection::groupBy()的你可以使用关系:

$magazines = Magazine::with(['issues' => function($query) { 
    $query->orderBy('date', 'desc'); 
}]); 

这会给你一个杂志的集合,包括数组中的相关问题。 issues数组排序方式为date desc

现在你可以使用两个查询条件不同拆分结果:

$baseQuery = Magazine::with(['issues' => function($query) { 
    $query->orderBy('date', 'desc'); 
}]); 

$query1 = clone $baseQuery; 
$query2 = clone $baseQuery; 

$query1->whereIn('product_code', $productCodes); 
$query2->whereNotIn('product_code', $productCodes); 

$query1product_code返回所有杂志从阵列中。 $query2将返回所有其他杂志。

你现在两种方法的两个结果结合起来:

1)使用Eloquent::unionAll()

$unionQuery = $query1->unionAll($query2); 
$magazines = $unionQuery->get(); 

2)使用Collection::merge()

$magazines1 = $query1->get(); 
$magazines2 = $query2->get(); 
$magazines = $magazines1->merge($magazines2); 

在这两种情况下,你会得到相同的结果:杂志的集合。来自$productCodes阵列的product_code杂志首先排序。每个Magazine对象包含一个数组issues及其相关的Issue对象。

如果你想摆脱Magazine对象和真正需要的结果看起来像你的问题,你仍然可以做:

$issues = $magazines->keyBy('id')->pluck('issue'); 

但有可能是没有必要的。

更新

如果你真的需要Magazine收集的两个部分由最新Issue进行排序。我看不出不使用JOIN或PHP与闭合排序任何方式。用连接对杂志排序也需要聚合。

$baseQuery = Issue::join('magazines', 'magazines.id', '=', 'issues.magazine_id'); 
$baseQuery->orderBy('issues.date', 'desc'); 
$baseQuery->select('issues.*'); 

$query1 = clone $baseQuery; 
$query2 = clone $baseQuery; 

$query1->whereIn('magazines.product_code', $productCodes); 
$query2->whereNotIn('magazines.product_code', $productCodes); 

然后

$issues1 = $query1->get()->groupBy('magazine_id'); 
$issues2 = $query2->get()->groupBy('magazine_id'); 

$issues = $issues1->union($issues2); 

$issues = $query1->unionAll($query2)->get()->groupBy('optionGroupId'); 
+0

这适用于让杂志具有相同的产品代码首先放入数组中,但其他不具有相同产品代码的杂志不按其最新版本的日期排序。 – Marco

+0

刚刚意识到,具有与阵列产品代码中相同产品代码的杂志或不具有相同产品代码的杂志都不会在其最新期刊的日期之前订购。 – Marco

+1

我希望,最新的问题排序不是要求,但只是发生:-) - 我会看看它。 –

0

的第一件事是with需求是一个数组,并使用子查询时,不叫get()因为它会复制查询每个Issue给你一个可爱的小N + 1点的问题。对于groupBy只需将id作为第二个参数来帮助解决only_full_group_by错误。

它应该是这样的:

$issues = Issue::groupBy('magazine_id', 'id') 
    ->with('magazine') 
    ->orderBy('date', 'desc') 
    ->get(); 

,如果你想通过date订购此一个查询,然后再将由日期顺序您Issue集合,然后在每个$issues->magazineproduct_code订购product_code你需要做一个像加入:

$issues = Issue::select('issues.*') 
    ->groupBy('magazine_id', 'id') 
    ->join('magazine', 'magazine.id', '=', 'issues.magazine_id') 
    ->with('magazine') 
    ->orderBy('issues.date', 'desc') 
    ->orderBy('magazine.product_code', $productCodes) 
    ->get(); 

UPDATE

下应该给你按日期每本杂志收集由产品代码在$ productCodes数组中指定的顺序排序排序分组杂志类别:

$issues = Issue::groupBy('magazine_id', 'id') 
    ->with('magazine') 
    ->orderBy('date', 'desc') 
    ->get() 
    ->groupBy('magazine_id') 
    ->sortBy(function($issue) use ($productCodes) { 
     $index = array_search($issue->first()->magazine->product_id, $productCodes); 

     return $index !== false ? $index : $issue->magazine_date; 
    }); 
+0

谢谢你的努力,但这个查询不适合我。通过我的查询,我得到了一个由杂志id分组的24个问题集合。现在我在一个数组中获取所有问题。我仍然需要获得由杂志ID分组的问题集合,但我需要做的唯一事情是以某种方式对这些问题集合进行排序,并收集其所属杂志与其所属杂志具有相同产品编码的问题集合在数组中首先在productCodes数组中编码。 – Marco

+0

哎呀,没有看到那是一个数组。更新后的查询应该按给定数组进行排序 –

+0

我仍然可以获取一个数组中的所有问题,而不是问题集合的数组。我也看到我没有清楚我从查询中得到的是什么。我在我的问题中纠正了这个问题。 – Marco

2

代码第一:那么作为显示上述,我将与加入扩展它切换回原来的查询,并把它分解为两个部分,最后一步一步解释:

Magazine::all() 
    ->load('issues') 
    ->keyBy('id') 
    ->sort(function ($a, $b) use ($product_codes) { 
     return 
      (in_array($b->product_code, $product_codes) - in_array($a->product_code, $product_codes)) 
      ?: 
      (strtotime($b->latestIssueDate) - strtotime($a->latestIssueDate)); 
    }) 
    ->map(function ($m) { 
     return $m->issues->sortByDesc('created_at'); 
    }); 

这可以让你:

  • 所有杂志
  • 与他们的每一个问题的热切加载
  • 的集合,其中键是各杂志id
  • 被2个标准排序:先不管product_code是所要求的代码之一,然后是最新期刊(*)
  • 的日期,对于每个杂志,您想收集其问题,按日期降序排序。

如果我正确理解您的要求,这会给你你想要的。

(假定日期列被称为created_at。)


(*)为方便起见,我使用属性访问器:

public function getLatestIssueDateAttribute() 
{ 
    // this breaks for Magazines with no Issues. Fix if necessary 
    return $this->issues->sortByDesc('created_at')->first()->created_at; 
} 
+0

这将执行每个杂志一个查询,对吧? –

+0

@PaulSpiegel否,'load'方法将在1个查询中获取所有相关模型。所以2查询总数 – alepeino

+0

我的意思是'getLatestIssueDateAttribute()'方法。 –

相关问题