微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Eloquent 模型:防止将瞬态或“虚拟”属性保存到数据库

如何解决Eloquent 模型:防止将瞬态或“虚拟”属性保存到数据库

A 有两个 Eloquent 模型 AlbumPhoto。相册可以包含更多子相册或照片。因此,所有专辑的集合是一个有向树。照片具有 taken_at 属性,用于存储照片拍摄时的时间戳。

对于每个相册,我希望有两个“瞬态”或“虚拟”属性 max_taken_atmin_taken_at,它们为相册中的所有照片保留 taken_at 的最小值和最大值及其子专辑递归。所以它不像 Album:query()->withMin('photos','taken_at')->withMax('photos','taken_at') 那样容易,因为那只会考虑相册的直接子级照片,但我也想考虑间接子级。

还有两个要求:

  • 我希望 Album::query() 始终认包含这些属性,而无需记住任何复杂的构造。
  • 出于效率原因,max_taken_atmin_taken_at 的计算应直接在数据库后端进行。我不希望 Eloquent 首先从数据库获取所有(直接和间接)子照片,然后在 PHP 中计算最小值/最大值。这对于拥有超过 5 万张照片的大型装置来说是令人望而却步的。

我已经走了多远:

我利用 Eloquent 的作用域来修改专辑的查询,并将所需的属性包含到查询中,如下所示:

class Album extends Model {
  protected static function booted() {
    parent::booted();
    // normally "scopes" are used to restrict the result of the query
    // to a particular subset through adding additional WHERE-clauses
    // to the default query.
    // However,"scopes" can be used to manipulate the query in any way.
    // Here we add to additional "virtual" columns to the query.
    static::addGlobalScope('calc_minmax_taken_at',function (Builder $builder) {
      $builder->addSelect([
        'max_taken_at' => Photo::query()
          ->leftJoin(...)
          // left out complicated sql query here to recursively include photos from all sub-albums
          ->orderBy('taken_at','desc')
          ->limit(1),'min_taken_at' => Photo::query()
          ->leftJoin('albums as a','a.id','=','album_id')
          // left out complicated sql query here to recursively include photos from all sub-albums
          ->orderBy('taken_at','asc')
          ->limit(1),]);
    });
  }
}

基本上,它类似于可以通过 CREATE VIEW 创建然后查询视图而不是表的 sql 视图。结果正如预期的那样,速度很快(即使对于大型安装也小于 10 毫秒)。通过这种方式,我在 attributes 对象的数组 Album 中获得了另外两个属性。为了避免意外修改值,我添加了以下修改

protected function setMaxTakenAtAttribute($value): void {
  throw new \BadMethodCall('"max_taken_at" must not be set,because this attribute is virtual');
}

protected function setMinTakenAtAttribute($value): void {
  throw new \BadMethodCall('"min_taken_at" must not be set,because this attribute is virtual');
}

不幸的是,还有一个问题。有时 Eloquent 还是想将属性 min_taken_atmax_taken_at 保存到数据库中。当然,这会因 sql 异常而失败,因为专辑关系中不存在这些列。

我该如何解决最后一个问题?有没有办法告诉 Laraval 在 INSERT/UPDATE 期间忽略某些属性

当然,我知道我滥用了 attributes 数组。在数组中根本没有属性 min_taken_atmax_taken_at 可能会更清晰,因为它们不是 Album 模型的真实属性,而是计算值。也许有一种完全不同的方法解决我的“虚拟”属性问题。但是,请注意,不能简单地定义两个访问器 getMinTakenAtgetMaxTakenAt 并在 PHP 中迭代所有子相册及其照片来计算这些值。这对于内存消耗和计算时间来说是令人望而却步的。 DB 可以在

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。