我有一对多的多态关系,就像描述in documentation一样,除了我在投票表中有投票而不是照片和整数投票列。像那样:
-votes
--id
--vote
--voteable_id
--voteable_type
投票模型有可投票的方法:
public function voteable()
{
return $this->morphTo();
}
但是对于我希望附加投票的模型(本例中的页面),我创建了一个VoteableTrait,它具有votes()morphMany方法和我试图开始工作的范围。范围将添加属于某个模型的所有投票的总和,并按总和对父模型进行排序。我试过的范围:
public function scopeWithVotesTrait($query)
{
return $query->with(['votes' => function($q){
$q->select(\DB::raw("SUM(vote) AS votes"), 'voteable_id')->first();
}]);
}
但有了这个,我无法订购,因为Laravel执行了2个不同的查询,一个用于父模型,一个用于投票。 Page :: withVotesTrait() - > first()的结果将投票放在数组中:
{
"id": 1,
"votes": [
{
"votes": "-1",
"voteable_id": 1
}
]
}
我的另一个想法是执行连接而不是 - > with(),如:
public function scopeWithVotesTrait($query, $model, $table)
{
return $query->join('votes', function($join) use($model, $table)
{
$join->on('votes.voteable_id', '=', $table . '.id')
->where('votes.voteable_type', '=', $model);
})->select('*', \DB::raw('sum(vote) as votes'))
->orderBy('votes')->groupBy($table . '.id');
}
但是如果我每次使用示波器时都必须手动输入$ model('App \ ParentModel')和$ table('parent_models'),那么这就是将其置于特征中的目的。
那么,在我的第一个范围示例中,是否有一种方法可以通过投票总和进行有效排序?或者是否有任何静态方法可以从Eloquent Model调用返回模型名称,还有一个用于模型表以使第二个示例有效?
任何其他想法如何制作一个范围,将所有投票的总和附加到父模型并由它订购?
答案 0 :(得分:4)
我已经使用了第二个使用join的例子。 Eloquent \ Model有一个静态方法getTable(),它返回模型表名,所以我可以动态地将它作为$ model参数传递。我的php函数get_class()将返回名称为$ model参数的父模型的名称空间。
正确查询我想要达到的目标:
public function scopeWithVotesTrait($query)
{
return $query->leftJoin('votes', function($join) use($model, $table)
{
$join->on('votes.voteable_id', '=', $table . '.id')
->where('votes.voteable_type', '=', $model);
})->addSelect('*', $table . '.id', \DB::raw('COALESCE(SUM(vote),0) as votes'))
->groupBy($table . '.id')->orderBy('votes');
}
我还添加了COALESCE,如果没有找到投票,将使查询返回0。这样,orderBy始终是正确的。
我还指定了*和parent_table.id来选择语句。如果没有*,查询将只返回select语句中指定的内容。如果没有parent_table.id,查询将不会返回我们可能使用关系方法查询的任何模型(例如:Model :: withVotesTrait() - > with('some_other_child')将不会将some_other_child添加到结果中。
我还使用了leftJoin而不是join,这样无论连接的票数多少,它都会返回所有父母。