Eloquent 按关系过滤(仅最后一条记录)

Posted

技术标签:

【中文标题】Eloquent 按关系过滤(仅最后一条记录)【英文标题】:Eloquent Filter by relationship (ONLY last record) 【发布时间】:2020-01-25 19:24:02 【问题描述】:

我将我的问题编辑得更明确

我需要建立一个过滤 hasMany 关系的范围。问题是,我想将该查询应用于 lastONLY,我的意思是,我希望我的模型由 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过滤和选择每个月的最后一条记录的方法?

使用 Eloquent 选择具有值的最后一条记录

Lumen/Laravel Eloquent - 按数据透视表中的属性过滤

Laravel/Eloquent ORM - 仅检索引用的记录

Laravel Eloquent/MySQL 如何从每个父记录的最后一个条目中获取去年的记录?