Eloquent 按关系过滤(仅最后一条记录)
Posted
技术标签:
【中文标题】Eloquent 按关系过滤(仅最后一条记录)【英文标题】:Eloquent Filter by relationship (ONLY last record) 【发布时间】:2020-01-25 19:24:02 【问题描述】:我将我的问题编辑得更明确
我需要建立一个过滤 hasMany 关系的范围。问题是,我想将该查询应用于 last 项 ONLY,我的意思是,我希望我的模型由 HasMany 关系的特定记录的列过滤,是有可能吗?
上下文:一个有帖子的应用程序,每个帖子都有许多状态来保持历史。我想按每个帖子的最后状态过滤帖子。
帖子:
public function statuses()
return $this->hasMany(Status::class); // Only last one is important for querying
然后,在一个范围内,我想做这样的事情:
public function scopeIsActive(Builder $builder)
return $builder->whereHas('statuses', function(Builder $q)
// How to apply this ONLY to last created record???
return $q->whereDate('activation', '<=', today());
);
仅此而已!
编辑:(已解决的查询)
经过一番挖掘,我解决了这个问题:
public function scopeIsActive(Builder $builder)
return $builder->whereHas('statuses', function (Builder $q)
return $q->whereDate('activation', '<=', today()) // applies to all
->where('statuses.id', function ($sub) // applies to specific one
return $sub->select('id')
->from('statuses')
->whereColumn('post_id', 'statuses.id')
->latest()
->limit(1);
);
);
【问题讨论】:
【参考方案1】:而不是
public function others()
return $this->hasMany(Other::class); // Only last one is important for querying
你可以这样做:
public function other()
return $this->hasOne(Other::class)->latest();
然后
public function scopeCurrentActive(Builder $builder)
return $builder->whereHas('other', function(Builder $q)
return $q->whereDate('activation', '<=', today());
);
但是你不能自定义订单,如果你想自定义订单,你必须使用子查询:
return Model::orderByDesc(
Other::select('arrived_at')
->whereColumn('model_id', 'model.id')
->orderBy('activation', 'desc')
->limit(1)
)->get();
如果想详细了解子查询,可以参考:
https://laravel-news.com/eloquent-subquery-enhancements
对于仅使用最后一条记录,您可能必须使用像这样的 having 语句,但我不确定确切的合成器 ^^
public function scopeIsActive(Builder $builder)
return $builder->whereHas('statuses', function (Builder $q)
return $q->havingRaw(Other::select('arrived_at')
->whereColumn('model_id', 'model.id')
->orderBy('activation', 'desc')
->limit(1)->select('activation')->toSql(), '<=' , today());
);
【讨论】:
嗨,对不起,我认为它可以工作,但我意识到不是,HasOne()
关系产生与HasMany()
完全相同的 sql...所以,它不会让 LAST 记录过滤,它将考虑所有相关记录,而不仅仅是最后一个:(
我更新了我的问题。我无法按最后一条记录过滤 T_T 看起来limit(1)
没有生效。我不确定生成的 SQL 语句是否正确或什么
这是因为查询后限制是“应用”,我认为你应该使用have()语句,我会更新我的答案,但我不确定确切的合成器^^
是的!问题出在查询位置!我可以解决这个问题并编辑我的问题。我还没有测试你的解决方案,但它可能也会起作用。谢谢你的帮助!!【参考方案2】:
不确定您对每条记录的最后一条记录的确切含义。
您可以通过以下方式获取与最新帖子具有相同状态的所有帖子:
public function scopeIsActive(Builder $builder)
$lastPostStatus = Post::select('status')
->orderBy('activation', 'desc')
->first()
->status;
return $builder->where('status', $lastPostStatus);
【讨论】:
嗨,谢谢你的回答,但不,那不是我想要的。我需要“其最新状态满足条件的所有帖子”。我需要通过 hasMany 关系的属性过滤所有帖子,但只有 hasMany 的最后一个孩子【参考方案3】:按今天激活的项目过滤:
public function scopeCurrentActive()
return $this->whereHas('other', function($q)
return $q->whereDate('activation', '==', Carbon::now()->toDateString());
);
【讨论】:
以上是关于Eloquent 按关系过滤(仅最后一条记录)的主要内容,如果未能解决你的问题,请参考以下文章
在最近的关系记录中搜索 - Laravel (Eloquent)
Django按时间序列中的ID过滤和选择每个月的最后一条记录的方法?
Lumen/Laravel Eloquent - 按数据透视表中的属性过滤