Laravel: Eloquent упорядочивание по hasMany отношениям
Сценарий: у вас есть User -> hasMany Posts -> hasMany Comments, и вы хотите загрузить пользователей с их сообщениями и комментариями, но комментарии упорядочены по наибольшему количеству голосов, поля comments.likes_count БД.
Структура модели:
app/Models/User.php:
class User extends Authenticatable
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}app/Models/Post.php:
class Post extends Model
{
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}app/Http/Controllers/Api/UserController.php:
class UserController extends Controller
{
    public function index()
    {
        return User::with('posts.comments')->get();
    }
}Давайте заполним базу данных одним пользователем, одним сообщением и двумя комментариями:
Результат в формате JSON:
[
    {
        "id": 1,
        "name": "Prof. Tito Macejkovic MD",
        "email": "charlene.olson@example.org",
        "posts": [
            {
                "title": "Post 1",
                "comments": [
                    {
                        "id": 1,
                        "comment_text": "First comment",
                        "likes_count": 1,
                        "created_at": "2023-01-11T19:11:45.000000Z",
                    },
                    {
                        "id": 2,
                        "comment_text": "Second comment",
                        "likes_count": 3,
                        "created_at": "2023-01-11T19:11:45.000000Z",
                    }
                ]
            }
        ]
    }
]Теперь, как упорядочить по comments.likes_count вместо упорядочивания по умолчанию по comments.id?
Три способа.
Вариант 1. Функция обратного вызова с orderBy
При загрузке отношения — неважно, один или два уровня — вы можете поместить его в массив ключ-значение, где ключ — имя отношения, а значение — функция обратного вызова с дополнительными условиями, такими как orderBy или какие вы захотите.
class UserController extends Controller
{
    public function index()
    {
        return User::with(['posts.comments' => function($query) {
            $query->orderBy('comments.likes_count', 'desc');
        }])->get();
    }
}Результат:
[
    {
        "id": 1,
        "name": "Prof. Tito Macejkovic MD",
        "email": "charlene.olson@example.org",
        "posts": [
            {
                "title": "Post 1",
                "comments": [
                    {
                        "id": 2,
                        "comment_text": "Second comment",
                        "likes_count": 3,
                        "created_at": "2023-01-11T19:11:45.000000Z",
                    },
                    {
                        "id": 1,
                        "comment_text": "First comment",
                        "likes_count": 1,
                        "created_at": "2023-01-11T19:11:45.000000Z",
                    }
                ]
            }
        ]
    }
]Вариант 2. Модель: ВСЕГДА упорядочивайте по полю X
Может быть, вы хотите, чтобы эти отношения всегда упорядочивались по этому полю?
Вы можете сделать так:
app/Models/Post.php:
class Post extends Model
{
    public function comments()
    {
        return $this->hasMany(Comment::class)->orderBy('likes_count', 'desc');
    }
}Затем, в контроллере вы просто оставляете всё как есть, User::with('posts.comments')->get(), и результаты в любом случае будут упорядочены правильно.
Но если вы хотите сделать исключение и в некоторых случаях упорядочить его по другому, вы можете сделать в Контроллере так:
public function index()
{
    return User::with(['posts.comments' => function($query) {
        $query->reorder('comments.id', 'desc');
    }])->get();
}Вариант 3. Отделяйте упорядоченные отношения
Если вы захотите использовать это упорядочивание часто, но не всегда, другой вариант — создать отдельную функцию отношений:
app/Models/Post.php:
class Post extends Model
{
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
    public function commentsByMostLikes()
    {
        return $this->hasMany(Comment::class)->orderBy('likes_count', 'desc');
        // Или как вариант, даже...
        return $this->comments()->orderBy('likes_count', 'desc');
    }
}app/Http/Controllers/Api/UserController.php:
class UserController extends Controller
{
    public function index()
    {
        return User::with('posts.commentsByMostLikes')->get();
    }
}