相关模型的 Laravel 计数列
Posted
技术标签:
【中文标题】相关模型的 Laravel 计数列【英文标题】:Laravel count column of related model 【发布时间】:2014-11-24 02:07:29 【问题描述】:我想统计某个Script
中来自ScripRating
的赞成票和反对票的数量。
Script.php:
public function ratings()
return $this->hasMany('ScriptRating');
ScriptRating.php:
public function script()
return $this->belongsTo('Script');
script_rating 数据库表:
id (primary, increments)
script_id(integer)
rating(integer) <-- Can be either 1 (upvote) or -1 (downvote)
检索脚本并显示评分:
$script = Script::where('title', '=', $title)->get();
$script->ratings
这很好用,它返回一个数组:["id":1,"script_id":1,"rating":1]
。但在这一点上,我被困住了。我如何计算某个脚本的赞成票和反对票总数?
我还有一个小问题,让我感到困惑。这与上面的代码相同:
$script = Script::where('title', '=', $title)->with('ratings')->get();
$script->ratings
这两种方法有什么区别,我应该使用哪一种?
提前致谢!
编辑
我做了三个范围:
public function scopeTotalRating($query, $scriptId)
return $query->where('script_id', $scriptId)->get()->sum('rating');
public function scopeThumbsUp($query, $scriptId)
return $query->where('script_id', $scriptId)->having('rating', '=', 1)->get()->sum('rating');
public function scopeThumbsDown($query, $scriptId)
return $query->where('script_id', $scriptId)->having('rating', '=', -1)->get()->sum('rating');
并显示如下:
ScriptRating::thumbsUp($script->id)
【问题讨论】:
【参考方案1】:你可以使用
$script->ratings->count()
这将显示一个脚本的总评分数。
但是,您感兴趣的是将评级分组为upvotes
和downvotes
,因此您需要通过group by
子句查询您的关系。
Script::where('title', '=', $title)->with([
'ratings' => function($query)
$query->groupBy('rating');
)
])->get();
我认为返回的集合现在应该按1
和-1
分组。让我知道结果!
编辑:您还可以在此处查看有关查询关系的文档:
http://laravel.com/docs/4.2/eloquent#querying-relations
编辑回复:
不使用 group by 的最简单方法是单独查询:
$script = Script::where('title', $title)->first();
if ($script)
$upvotes = ScriptRating::where('script_id', $script->id)->having('rating', '>', 0)->get()->count();
$downvotes = ScriptRating::where('script_id', $script->id)->having('rating', '<', 0)->get()->count();
您提到的脚本之间的差异也称为eager loading
或lazy loading
。当您在查询中指定 ->with()
时,这称为预加载。如果你不这样做,查询将在你指定$script->ratings
时运行
更多关于急切/延迟加载的信息:
http://laravel.com/docs/4.2/eloquent#eager-loading
编辑其他回复:
如果您想收集仅有评级的脚本,您可以使用->whereHas('ratings')
函数。您还可以通过执行 if 语句来检查具有评级的脚本是否存在:
if ($script->ratings->count() > 0)
// The script has ratings
else
// The script does not have ratings
如果您不想重复此代码,您可以随时使用以下代码在 Script.php
模型中添加一个函数:
public function hasRatings()
return $this->ratings->count() > 0;
那么你可以这样做:
if ($script->hasRatings())
【讨论】:
感谢您的评论。我尝试了上面的代码,但我不知道如何使用它。如何检索反对/赞成票?另外,你知道我第二个问题的答案吗? 谢谢!你可以在我的第一篇文章中看到我做了什么。我现在遇到的唯一问题是这个错误:Object of class Illuminate\Database\Eloquent\Builder could not be converted to string
当脚本没有任何评级时会发生这种情况。你知道如何解决这个问题吗?【参考方案2】:
您可以将这 2 个函数添加到 Script
模型类中:
public function ratingsSumRelation()
return $this->hasOne('ScriptRating')->selectRaw('script_id, sum(rating) as sum_all')
->groupBy('script_id');
public function getRatingSumAttribute()
return $this->ratingsSumRelation ?
$this->ratingsSumRelation->sum_all: 0;
现在显示总和:
$script->rating_sum
【讨论】:
我确实做到了,但它给了我 SQL 语法错误。另外,如果我理解这一点,这将返回反对票和赞成票的总和。我需要这些数据,但我也需要单独的赞成票和反对票。请原谅我的英语不好。 @JasonK 有一个错字。现在看,如果你有错误,你应该总是提供完整的错误信息 是的,我已经注意到 't' 不见了。语法错误:near '*) as sum from script_rating where script_rating.script_id = ? group by ' at line 1 (SQL: select script_id, sum(*) as sum from script_rating where script_rating.script_id = 1 group by script_id limit 1) (View: C:\xampp\htdocs\laravel\laravel\app\views\script.blade.php)
@JasonK 我已经更改了答案 - 将 as
sum`` 更改为 as sum_all
并在第二个函数中相同
仍然出现语法错误:) as sum_all from script_rating
where script_rating
.script_id
= ? group b' at line 1 (SQL: select script_id, sum() as sum_all from script_rating
where script_rating
.script_id
= 1 group by script_id
limit 1以上是关于相关模型的 Laravel 计数列的主要内容,如果未能解决你的问题,请参考以下文章