Eloquent(Laravel 5) - 如何在关系查询中包含SoftDeleted记录(whereHas)?

时间:2015-04-17 11:19:16

标签: laravel eloquent laravel-5 soft-delete

我使用Eloquent构建了一个存储库层。我在表之间有很多复杂的关系,并且能够使用雄辩的方式轻松构建所有查询,我非常依赖WhereHas查询来根据关系条件进行查询。

完成所有查询并完成工作后,最后要做的事情是在我的一些查询中添加一个选项以包含softDeleted记录(在特定日期之后删除) - 这突出了一些问题。

因此,例如,我可能有一个查询,它通过急切加载必要的数据开始如下:

public function query()
{
    $this->query = AssetInstance::with('asset.type', 'zoneInstance.type', 'zoneInstance.zone');
)

我可能会有一个函数可选择优化查询,如下所示:

function filterByZoneInstance($zone_instance_id)
{
    $this->query->whereZoneInstanceId($zone_instance_id);
    return $this;
}

我可能还有另一个功能来进一步细化查询:

public function filterByZoneType($type)
{
    $this->query->whereHas('zone_instance', function($q) use($type){
        return $q->whereHas('type', function($q2){
            return $q2->whereName($type);
        });
    });
}

public function get()
{
     return $this->query->get();
}

所以这一切都很好,我可以这样做: $this->query()->filterByZoneType('typex')->get();

现在,让我们说我想要包含softDeleteResults,我可以这样做:

public function includeTrashed()
{
     $this->query->withTashed();
     return $this;
}

但是这并没有贯彻到关系,所以是的所有assetInstances(包括软删除都会被拉入)但不是所有的zoneInstances都会导致filterByZoneType失败,如果关系(例如zone_instance被软删除)

所以我认为没问题 - 我可以用trashed加载关系:

public function query()
    {
        $this->query = AssetInstance::with(['asset.type', 'ZoneInstance' => function ($q){
                $q->withTrashed();
            }, 'zoneInstance.type', 'zoneInstance.zone']);
    )

这种工作直到你应用whereHas查询,此时eagerloading withTrashed被wherehas查询覆盖,执行它自己的预先加载(没有删除);

我尝试在whereHas闭包中应用约束,并且它可以正常工作:

public function filterByZoneType($type)
{
    $this->query->whereHas('zone_instance', function($q) use($type){
        return $q->whereHas('type', function($q2){
            return $q2->whereName($type)->withTrashed();
        })->withTrashed();
    });
}

我决定在这一点上使用原始查询,不幸的是这有类似的效果,所以例如,如果我做这样的事情:

$this->query->whereRaw("asset_instances.`deleted_at` <= '2014-03-11 00:00:00' and (select count(*) from `variable_data_package_instances` where `variable_data_package_instances`.`asset_instance_id` = `asset_instances`.`id` and `variable_data_package_instances`.`deleted_at` <= '2014-03-11 00:00:00')");

我现在看到的是,已删除的zoneInstances不再需要加载(来自之前调用的query()函数)。

有没有人有幸使用雄辩的关系查询来引入破败的结果?

3 个答案:

答案 0 :(得分:2)

这个怎么样:

public function query()
    {
        $this->query = AssetInstance::with(['asset.type', 'ZoneInstance' => function ($q){
                $q->withTrashed()->with('type','zone');
            }]);
    )->get();
}

答案 1 :(得分:1)

我注意到以下一段代码存在同样的问题:

class Reward extends Model 
{
    /* ... */

    public function membership()
    {
        return $this->belongsTo(Membership::class);
    }

    /* ... */
}

class Membership extends Model 
{
    use SoftDeletes ;

    /* ... */

    public function rewards()
    {
        return $this->hasMany(Reward::class);
    }

    /* ... */
}

当我想将Rewards与已删除的成员资格相关联时,我使用此查询:

Reward::whereHas( ['membership' => function($query){
    $query->withTrashed() ;
}] )->get();

但是,它并不起作用,因为&#34; withTrashed&#34; method不是作用域(添加约束),而是创建一个新查询:

public static function withTrashed()
{
    return (new static)->newQueryWithoutScope(new SoftDeletingScope);
}

因此,我目前找到的唯一解决方案是使用其他关系:

class Reward extends Model 
{
    /* ... */

    public function membershipWithTrashed()
    {
        return $this->belongsTo(Membership::class)->withTrashed();
    }

    /* ... */
}

然后:

Reward::has( 'membershipWithTrashed' )->get();

作品。

在我看来,这是一个非常难看的解决方案,需要修复SoftDelete Trait。

答案 2 :(得分:0)

Laravel 5.2解决了这个问题。不再需要一个丑陋的黑客。

请参阅:https://github.com/laravel/framework/issues/11860