【问题标题】:Eloquent (Laravel 5) - How do I include SoftDeleted records in relationship queries (whereHas)?Eloquent (Laravel 5) - 如何在关系查询 (whereHas) 中包含 SoftDeleted 记录?
【发布时间】:2015-06-24 05:16:03
【问题描述】:

我已经使用 Eloquent 构建了一个存储库层。我在表之间有很多复杂的关系,并且能够使用 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,如果关系(例如zone_instance已被软删除),这反过来会导致filterByZoneType失败.

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

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

这种方法一直有效,直到您应用 whereHas 查询,此时带有 Trashed 的 eagerloading 被 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() 函数)。

有没有人幸运地使用雄辩的关系查询来带来垃圾结果?

【问题讨论】:

    标签: laravel eloquent laravel-5 soft-delete


    【解决方案1】:

    这个呢:

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

    【讨论】:

      【解决方案2】:

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

      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);
          }
      
          /* ... */
      }
      

      当我想获得与已删除会员资格相关联的奖励时,我使用此查询:

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

      但是,它不起作用,因为“withTrashed”方法不是范围(添加约束)而是创建一个新查询:

      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。

      【讨论】:

        【解决方案3】:

        Laravel 5.2 已经修复了这个问题。不再需要丑陋的 hack。

        见:https://github.com/laravel/framework/issues/11860

        【讨论】:

        • 我并没有真正使用 Laravel/PHP,所以没有花时间正确地确认这个问题已经修复。但是,问题似乎确实相似,所以我相信你是正确的。由于当时没有其他答案有帮助,因此我将其标记为已接受,因为对于任何登陆这里的人来说,这似乎都是最有帮助的答案。如果有人对此有争议,请评论纠正。
        • 感谢您的回答。在过去的几天里,我自己也遇到了这个问题,很难发现 Laravel 已经修复了它。希望这个答案可以帮助其他人节省时间。
        猜你喜欢
        • 2021-05-25
        • 2017-03-23
        • 2021-09-05
        • 2022-08-19
        • 1970-01-01
        • 1970-01-01
        • 2020-12-23
        • 2018-06-19
        • 2016-03-25
        相关资源
        最近更新 更多