Laravel Eloquent - 通过用户模型上的方法对用户关系进行排序

Posted

技术标签:

【中文标题】Laravel Eloquent - 通过用户模型上的方法对用户关系进行排序【英文标题】:Laravel Eloquent - Order users relationship by a method on the user model 【发布时间】:2017-12-31 04:35:43 【问题描述】:

我正在使用 Laravel Spark 开发 API 端点。

此端点返回给定团队及其用户。

// in App\Team

public function users()

   return $this->belongsToMany(
       'App\User', 'team_users', 'team_id', 'user_id'
   )->withPivot('role');

但是,我希望通过用户模型上的方法对这些用户进行排序。

在我的 App\User 模型上,我有一个方法:

public function currentQueueLength()

    returns an integer based upon the users current appointments,

有什么方法可以返回用户关系,但通过该方法的结果对用户进行排序?

【问题讨论】:

User::currentQueueLength() 到底是做什么的? @haakym 运行一个可怕的循环来计算用户服务的总时间。用户是理发师,并且可以与分配的服务进行许多约会。有各种模型关系,但该方法返回一个整数分钟。通常称为例如$user->currentQueueLength() 明白了。我的第一个想法是您可以使用$appends 属性和访问器方法将currentQueueLength() 作为User 模型上的属性预加载,然后在Team:: users() 调用中按此属性排序。这种方法的问题在于,听起来currentQueueLength() 是一项代价高昂的操作(根据您的最后评论,是吗?)所以理想情况下,您可以有条件地执行此操作。也许重构你的currentQueueLength() 可能是第一步? 【参考方案1】:

如果将current_queue_length 作为属性添加到User 模型,则可以按此属性进行排序。

您可以通过将属性添加到$appends 数组并创建访问器来添加属性:

class User extends Model 

    protected $appends = ['currentQueueLength'];

    public function getCurrentQueueLengthAttribute()
    
        return $this->currentQueueLength();  
    

感谢这个问题:Add a custom attribute to a Laravel / Eloquent model on load?

然后在Team 中你可以像这样添加方法:

class Team extends Model 

    public function users()
    
       return $this->belongsToMany(
           'App\User', 'team_users', 'team_id', 'user_id'
       )->withPivot('role');
    

    public function usersByCurrentQueueLength()
    
        return $this->users->orderBy('current_queue_length');
    


正如我在评论中提到的,这种方法的问题在于,听起来currentQueueLength() 是一项代价高昂的操作(基于您的评论),因此理想情况下,您可以有条件地执行此操作,但是,我不确定怎么做!您可能需要重新考虑实现 currentQueueLength() 的方法,这可能会为您构建此查询的方式提供更多选择。

【讨论】:

【参考方案2】:

您可以通过像这样对用户进行排序来实现这一点:

Team::with('users')
     ->all();

$team->users->sort(
    function ($user1, $user2) 
        return $user1->currentQueueLength() - $user2->currentQueueLength();
    
);

关于 sort 的更多信息:要按升序排序,当第一项小于第二项时返回 -1。所以你可以使用:

return $user1->currentQueueLength() < $user2->currentQueueLength() ? -1 : 1;

并按降序排序,当第一项小于第二项时返回+1。

return $user1->currentQueueLength() < $user2->currentQueueLength() ? 1 : -1;

如果它是用户模型中的一个字段,您可以这样做:

$teams = Team::with(['users' => function ($q) 
  $q->orderBy('Field', 'asc'); // or desc
])->all();

对于财产的情况:

// asc
$team->users->sortBy('userProperty');
// desc
$team->users->sortByDesc('userProperty');

希望有帮助:)

【讨论】:

我认为@lovelock 想要在Team::users() 调用中实现排序withincurrentQueueLength 目前也不是 User 上的属性,但它可以成为 $appends + 访问器的属性,正如我在我对问题的评论中提到的那样,但它可能是一项昂贵的操作,并且除了这个实例之外是不需要的,所以也许不是理想?

以上是关于Laravel Eloquent - 通过用户模型上的方法对用户关系进行排序的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5.0 用户 Eloquent 模型嘲讽

Laravel 5 Eloquent - 将计算值作为列添加到集合

Laravel 混合获取 Eloquent 急切加载嵌套多个模型

Laravel count Eloquent belongsToMany 相关模型

一个模型和多个模型之间的 Laravel Eloquent 关系作为 MorphOne?

Laravel 5:工作时间的 Eloquent & form 模型