首页 > 解决方案 > 获取在所选行之前创建的行

问题描述

我想知道获得在选定行之前创建的所有行的计数的最佳方法。现在我已经定义了一个看起来像这样的访问器:

// In the model
public function getPositionAttribute() {
    return self::where([
        // Some other condition
        ['created_at', '<', $this->created_at->toDateTimeString()]
    ])->count();
}

// In the code
$model->position

它工作正常,但我担心两件事:

  1. 调用self模型是一种不好的做法吗?在我看来不知何故。
  2. 当在 a 中调用时,foreach这显然会为每个远非最佳的元素生成一个查询。有什么方法可以重构它,以便可以在单个查询中预先加载它?

奖励:我完全放弃了使用某种索引来保留列的想法,因为最初听起来无法维护,例如。当一条记录被删除时,所有其他记录都应该以某种方式转移位置。我应该重新考虑吗?有没有更好的办法?

标签: phplaraveleloquent

解决方案


很确定在这里使用 self 是“最佳实践”,因为这就是该关键字的设计方式。

关于重构,我个人无法考虑按原样优化查询,但您可以创建一个函数来预加载所有位置,然后正常使用它。假设您的模型具有唯一键“id”并且您正在传递模型集合,那么您可以尝试以下操作:

public static function populateOrderPositions($modelCollection){
  // Optimize this query to include your "other condition"
  $idCollection = Model::orderBy('created_at') // this will make it in the order of creation
      ->pluck('id'); // this will only retrieve the id field

  // This array will contain an array with the model object ids as key and a numeric position (starts at 0)
  $positionArr = $idCollection->flip()->all();

  // Then just load all the position into the object and return it.
  return $modelCollection->map(function($modelObj) use ($positionArr){
      return $modelObj->position = $positionArr[$modelObj->id] + 1; // +1 because array key starts at 0
    };
}

您还需要调整属性代码以使用加载的属性,而不是像这样忽略加载的属性:

public function getPositionAttribute() {
    return $this->attributes['position'] ?? self::where([
        // Some other condition
        ['created_at', '<', $this->created_at->toDateTimeString()]
    ])->count();
}

通过这些更改,您可以预先填充该位置,然后使用它,而无需查询数据库。

这些代码未经测试,因为我不知道您的模型和查询将如何构建,并且只是一个示例。您还需要将性能与原始代码进行比较。


推荐阅读