Lovelock
Lovelock

Reputation: 8085

Laravel Eloquent - Order users relationship by a method on the user model

I'm working on an API endpoint with Laravel Spark.

This endpoint returns the given Team along with its users.

// in App\Team

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

However, I wish to order those users by a method that is on the user model.

On my App\User model I have a method:

public function currentQueueLength()
{
    returns an integer based upon the users current appointments,
}

Is there any way I can return the users relationship but order the users by the result of that method?

Upvotes: 0

Views: 698

Answers (2)

haakym
haakym

Reputation: 12358

If you add current_queue_length as an attribute to the User model, you can then order by this attribute.

You can add the attribute by adding it to the $appends array and creating an accessor:

class User extends Model {

    protected $appends = ['currentQueueLength'];

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

Credit to this question: Add a custom attribute to a Laravel / Eloquent model on load?

Then in Team you can add the method like so:

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');
    }

}

As mentioned in my comment, the issue with this approach is that it sounds like currentQueueLength() is a costly operation (based on your comment) so ideally, it would be something you could do conditionally, however, I'm unsure how to do that! You may want to reconsider your approach to implementing currentQueueLength() which may open up more options to the way you structure this query.

Upvotes: 1

Maraboc
Maraboc

Reputation: 11083

You can acheave this by sorting the users like this :

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

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

More information about sort : To sort in ascending order, return -1 when the first item is less than the second item. So you can use :

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

And to sort in descending order, return +1 when the first item is less than the second item.

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

And if it's a field in the users model you can do it like this :

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

For the case of property :

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

Hope that helps :)

Upvotes: 0

Related Questions