0

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?

Lovelock
  • 7,689
  • 19
  • 86
  • 186
  • What does `User::currentQueueLength()` do exactly? – haakym Jul 25 '17 at 10:14
  • 1
    @haakym Runs a horrible loop to calculate the total time of a users services. The user is a barber and can have many appointments with services assigned. There are various model relationships but the method returns an integer of minutes. Normally called e.g. $user->currentQueueLength() – Lovelock Jul 25 '17 at 10:18
  • Gotcha. My first thought is that you could use the `$appends` attribute and an accessor method to preload the `currentQueueLength()` as an attribute on the `User` model, then order by this attribute on your `Team:: users()` call. The issue with this approach is that it sounds like `currentQueueLength()` is a costly operation (based on your last comment, is it?) so ideally, it would be something you could do conditionally. Perhaps refactoring your `currentQueueLength()` may be the first step? – haakym Jul 25 '17 at 10:30

2 Answers2

1

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.

haakym
  • 12,050
  • 12
  • 70
  • 98
0

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 :)

Maraboc
  • 10,550
  • 3
  • 37
  • 48
  • I think @lovelock wants to implement the ordering *within* the `Team::users()` call? `currentQueueLength` is also not currently a property on `User`, however it could become one with `$appends` + accessor as I mentioned in my comment on the question, but it may be a costly operation and unwanted besides this instance so perhaps not ideal? – haakym Jul 25 '17 at 10:34