Я уверен, что использование 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();
}
С этими изменениями вы можете предварительно заполнить позицию и затем использовать ее без необходимости запрашивать базу данных.
Эти коды непроверенный, поскольку я не знаю, как ваша модель и запрос будут структурированы, и это скорее пример. Также вам нужно сравнить производительность с исходным кодом.