Короткий ответ
Третий вариант: Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
$teamMorphType = Relation::getMorphedModel('team');
$groupMorphType = Relation::getMorphedModel('group');
$formMorphType = Relation::getMorphedModel('form');
$permissible = [
$teamMorphType => [$user->team_id],
$groupMorphType => [],
$formMorphType => [],
];
foreach ($user->permissible as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
case $groupMorphType:
case $formMorphType:
$permissible[$permissible->permissible_type][] = $permissible->permissible_id;
break;
}
}
$forms = Form::query()
->where('user_id', '=', $user->id)
->orWhereIn('id', $permissible[$fromMorphType])
->orWhereIn('team_id', $permissible[$teamMorphType])
->orWhereIn('group_id', $permissible[$groupMorphType])
->get();
Длинный ответ
С одной стороны, (почти) все, что вы можете сделать в коде,лучше с точки зрения производительности, чем в запросах.
С другой стороны, получение большего количества данных из базы данных, чем необходимо, уже слишком много данных (использование ОЗУ и т. д.).
С моей точки зрения, вам нужно что-то промежуточное, и только вы будете знать, где будет баланс, в зависимости от чисел.
Я бы предложил выполнить несколько запросов, последний предложенный вами вариант (Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
):
- Запрос всех идентификаторов для всех разрешений (5 запросов)
- Объединение всех результатов форм в памяти и получение уникальных значений
array_unique($ids)
- ЗапросМодель формы с использованием идентификаторов в операторе IN ().
Вы можете попробовать три предложенных вами варианта и отслеживать производительность, используя какой-либо инструмент для многократного выполнения запроса, но я на 99%уверен, что последний даст вам тЛучшая производительность.
Это также может сильно измениться, в зависимости от того, какую базу данных вы используете, но если мы говорим, например, о MySQL;В очень большом запросе будет использоваться больше ресурсов базы данных, который не только будет тратить больше времени, чем простые запросы, но также будет блокировать таблицу от записи, и это может привести к ошибкам взаимоблокировки (если вы не используете подчиненный сервер).
С другой стороны, если число идентификаторов форм очень велико, вы можете иметь ошибки для слишком большого количества заполнителей, поэтому вы можете разделить запросы на группы, скажем, 500 идентификаторов (это зависит отограничение по размеру, а не по количеству привязок), и объединить результаты в памяти. Даже если вы не получите ошибку базы данных, вы также можете увидеть большую разницу в производительности (я все еще говорю о MySQL).
Реализация
Я предполагаю, чтоэто схема базы данных:
users
- id
- team_id
forms
- id
- user_id
- team_id
- group_id
permissible
- user_id
- permissible_id
- permissible_type
Таким образом, допустимым будет уже настроенное полиморфное отношение .
Следовательно, отношения будут:
- Форма собственности:
users.id <-> form.user_id
- Команда владеет формой:
users.team_id <-> form.team_id
- Имеет разрешения для группы, владеющей формой:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
- Имеет разрешения дляКоманда, владеющая формой:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
- Имеет разрешение на форму:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\From'
Упрощенная версия:
$teamMorphType = Relation::getMorphedModel('team');
$groupMorphType = Relation::getMorphedModel('group');
$formMorphType = Relation::getMorphedModel('form');
$permissible = [
$teamMorphType => [$user->team_id],
$groupMorphType => [],
$formMorphType => [],
];
foreach ($user->permissible as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
case $groupMorphType:
case $formMorphType:
$permissible[$permissible->permissible_type][] = $permissible->permissible_id;
break;
}
}
$forms = Form::query()
->where('user_id', '=', $user->id)
->orWhereIn('id', $permissible[$fromMorphType])
->orWhereIn('team_id', $permissible[$teamMorphType])
->orWhereIn('group_id', $permissible[$groupMorphType])
->get();
Подробная версия:
// Owns Form
// users.id <-> forms.user_id
$userId = $user->id;
// Team owns Form
// users.team_id <-> forms.team_id
// Initialise the array with a first value.
// The permissions polymorphic relationship will have other teams ids to look at
$teamIds = [$user->team_id];
// Groups owns Form was not mention, so I assume there is not such a relation in user.
// Just initialise the array without a first value.
$groupIds = [];
// Also initialise forms for permissions:
$formIds = [];
// Has permissions to a group that owns a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
$teamMorphType = Relation::getMorphedModel('team');
// Has permissions to a team that owns a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
$groupMorphType = Relation::getMorphedModel('group');
// Has permission to a Form
// permissible.user_id <-> users.id && permissible.permissible_type = 'App\Form'
$formMorphType = Relation::getMorphedModel('form');
// Get permissions
$permissibles = $user->permissible()->whereIn(
'permissible_type',
[$teamMorphType, $groupMorphType, $formMorphType]
)->get();
// If you don't have more permissible types other than those, then you can just:
// $permissibles = $user->permissible;
// Group the ids per type
foreach ($permissibles as $permissible) {
switch ($permissible->permissible_type) {
case $teamMorphType:
$teamIds[] = $permissible->permissible_id;
break;
case $groupMorphType:
$groupIds[] = $permissible->permissible_id;
break;
case $formMorphType:
$formIds[] = $permissible->permissible_id;
break;
}
}
// In case the user and the team ids are repeated:
$teamIds = array_values(array_unique($teamIds));
// We assume that the rest of the values will not be repeated.
$forms = Form::query()
->where('user_id', '=', $userId)
->orWhereIn('id', $formIds)
->orWhereIn('team_id', $teamIds)
->orWhereIn('group_id', $groupIds)
->get();
Используемые ресурсы:
Производительность базы данных:
- Запросы к базе данных (исключая пользователя): 2 ;один для получения разрешенного, а другой для получения форм.
- Нет присоединений !!
- Минимальное возможное ИЛИ (
user_id = ? OR id IN (?..) OR team_id IN (?...) OR group_id IN (?...)
.
PHP, в памяти, производительность:
- foreach зацикливание допустимого с переключателем внутри.
array_values(array_unique())
доизбегайте повторения идентификаторов. - В памяти 3 массива идентификаторов (
$teamIds
, $groupIds
, $formIds
) - В памяти соответствующие разрешения красноречивого сбора (это можно оптимизировать,при необходимости).
Плюсы и минусы
ПРОФИ:
- Время : сумма раз одиночногозапросов меньше, чем время большого запроса с объединениями и OR.
- Ресурсы БД : ресурсы MySQL, используемые запросом с операторами объединения или или, больше, чем используемыесумма отдельных запросов.
- Деньги : меньше ресурсов базы данных (процессор, ОЗУ, чтение с диска и т. д.), которые стоят дороже ресурсов PHP.
- Locks : Если вы не запрашиваете подчиненный сервер, доступный только для чтения, ваши запросы сделают меньше строк блокировками на чтение (блокировка на чтение является общей в MySQL, поэтому она не блокирует другое чтение, но блокируетлюбая запись).
- Масштабируемый : этот подход позволяет оптимизировать производительность, например разбивать запросы на части.
CONS:
- Ресурсы кода : Выполнение вычислений в коде, а не в базе данных, очевидно, потребляет больше ресурсов в экземпляре кода, но особенно в ОЗУ, хранящем промежуточную информацию. В нашем случае это будет просто массив идентификаторов, что не должно быть проблемой на самом деле.
- Обслуживание : Если вы используете свойства и методы Laravel, и вы вносите какие-либо изменения вбазу данных будет проще обновлять в коде, чем при более явных запросах и обработке.
- Избыточность? : в некоторых случаях, если данные не так велики, оптимизацияпроизводительность может быть чрезмерной.
Как измерить производительность
Некоторые подсказки о том, как измерить производительность?
- Медленный запросжурналы
- ТАБЛИЦА АНАЛИЗА
- ПОКАЗАТЬ СОСТОЯНИЕ ТАБЛИЦЫ, КАК
- ОБЪЯСНИТЬ ; Расширенный формат EXPLAIN Output ; с использованием объяснения ; объяснение вывода
- ПОКАЗАТЬ ПРЕДУПРЕЖДЕНИЯ
Некоторые интересные инструменты профилирования: