首页 > 解决方案 > 每次从数据库中检索模型集合后对其进行排序

问题描述

我有一个Product和一个Tag模型。它们以多对多关系连接。
当我得到一个集合时Tag,我希望它随时被排序。因为排序算法是在 PHP 中实现的,所以我不能在数据库级别执行此操作。

我的第一次尝试是简单地覆盖newCollection()方法Model

// Tag.php
public function newCollection(array $models = []): Collection
{
    return parent::newCollection($models)->sortBy(fn(Tag $tag) => $tag->present()->name)->values();
}

这样可行。不幸的是,从数据库中检索集合newCollection()时总是调用两次: 第一次:Builder::hydrate() 第二次:Builder::get()Tag

这与高效相去甚远。于是我又做了一次尝试。newCollection()我没有覆盖,而是覆盖了以下的newEloquentBuilder()方法Model

// Tag.php
public function newEloquentBuilder($query): Builder
{
    return new class($query) extends Builder {
        public function get($columns = ['*'])
        {
            return parent::get($columns)->sortBy(fn(Tag $tag) => $tag->present()->name)->values();
        }
    };
}

似乎工作!但不幸的是,不是每次。$product->tags不会用Builder::get()。它使用BelongsToMany::get()

所以我的问题是:从数据库中检索到特定模型的集合后,是否有一种有效的方法来修改它?

标签: phplaravel

解决方案


像这样修改默认行为通常是个坏主意,而且您经常会发现自己在某个地方需要原始默认行为。我认为更简洁的方法是向模型添加第二种方法,该方法检索集合然后应用排序方法。

public function sortedTags(): Collection {
 return $this->tags->sortBy('tag.present.name');
}

您可能需要急切地加载关系以优化事物。

尽管如此,从性能的角度来看,这是一个坏主意。它加载所有数据,然后循环通过它进行排序。如果您可以将排序/排序推送到 sql 中,从长远来看,您会做得更好,特别是如果 Tags 表中的数据集预计会随着时间增长。它肯定会导致查询不太干净,因为您可能需要添加联接才能使事情正常工作,并且您可能希望根据复杂性切换到查询构建器。


推荐阅读