Лучший способ запросить вложенные отношения в Laravel / Eloquent - PullRequest
0 голосов
/ 26 февраля 2019

Я создал приложение с Laravel, в котором у меня довольно глубокие отношения, которые мне иногда нужно запрашивать.База данных - MySQL.

Например, я хочу получить всех пользователей , которым разрешено читать книгу .Мои данные структурированы следующим образом:

  • A Пользователь принадлежит 0-n UserGroups через UserMembership
  • A UserGroup имеет 0-n Права
  • A Право относится к 1 Книге и описывает, какие действия могут бытьВыполнено

После просмотра и просмотра я обнаружил, что некоторые люди рекомендуют следующий способ для решения вложенных отношений:

// class Book extends Model
public function readers() {
    $bookId= $this->id;
    return User::whereHas('memberships', function($m) use($bookId) {
        $m->whereHas('group', function($g) use($bookId) {
            $g->whereHas('rights', function($r) use($bookId) {
                $r->where('resource_id', $bookId)->where('action', 'read');
            });
        });
    });
}

Мне нравится, что код имеет большой смысл,но производительность ужасна .. Время выполнения составляет 430 мс в среднем для Book::find(967)->readers()->get()

Я переписал функцию следующим образом:

public function readersNew() {
    $bookId= $this->id;
    $g = Right::where('resource_id', $bookId)->where('action', 'read')->pluck('group_id');
    $uIds = UserMembership::whereIn('group_id', $g)->pluck('user_id');
    return User::whereIn('id', $uIds);
}

С этим кодомЯ достигаю среднего времени исполнения 4 мс , что, очевидно, намного лучше.Но это также выглядит гораздо менее «методично» с точки зрения написания вложенных запросов.

Мне бы очень хотелось понять:

  • почему reader () -> get () намного медленнее, чем readerNew () -> get ()
  • что лучше всего написать такие запросы

1 Ответ

0 голосов
/ 26 февраля 2019

Прежде всего, хорошая работа по улучшению производительности, даже не зная, почему это происходит:)

Q1: почему reader () -> get () намного медленнее, чем читателиNew () -> get()

Ваша readers()->get() функция перемещается по иерархии вверх-вниз, поэтому она имеет больше смысла, но медленнее.Это то же самое, что запуск 3 вложенных циклов foreach, сначала он возвращает всех пользователей, которые имеют членство, а затем выполняет итерацию для каждого пользователя и находит все группы, к которым он принадлежит, а затем выполняет итерацию каждой группы и находит права для каждого, а затем выполняет итерацию каждого права и получаетжелаемую запись путем сопоставления resource_id и action.

, тогда как ваш readersNew()->get() перемещается по иерархии вниз, поэтому он быстрее.Сначала он извлекает целевую группу на основе сопоставленного права, а затем извлекает членство, пользователя, связанного с этой группой, следовательно, быстрее.

Q2, что лучший способ написать такие запросы

ПодходreadersNew()->get() лучший, вы могли бы просто изменить свои соглашения о написании, чтобы иметь больше смысла, если хотите:

public function readersNew() {
    $bookId= $this->id;
    $targetGroup = Right::where(['resource_id' => $bookId, 'action' => 'read'])->pluck('group_id');
    $associatedUserIds = UserMembership::whereIn('group_id', $g)->pluck('user_id');
    return User::whereIn('id', $associatedUserIds);
}

Я надеюсь, что это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...