Короткий ответ
Третий вариант: 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)
- Запросите модель Form, используя идентификаторы в выражении 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
)
- В памяти соответствующих разрешений красноречивый сбор (это можно оптимизировать при необходимости).
Плюсы и минусы
ПЛЮСЫ:
- Время : сумма раз одиночных запросов меньше времени большого запроса с объединениями и ИЛИ.
- Ресурсы БД. Ресурсы MySQL, используемые запросом с операторами join или or, больше, чем сумма, используемая отдельными запросами.
- Деньги : меньше ресурсов базы данных (процессор, оперативная память, чтение дисков и т. Д.), Которые стоят дороже ресурсов PHP.
- Блокировки . Если вы не запрашиваете подчиненный сервер, доступный только для чтения, ваши запросы будут делать меньше блокировок чтения строк (блокировка чтения используется в MySQL, поэтому она не блокирует другое чтение, но блокирует любую запись).
- Масштабируемость . Этот подход позволяет оптимизировать производительность, например, разделять запросы на части.
МИНУСЫ:
- Ресурсы кода : выполнение вычислений в коде, а не в базе данных, очевидно, потребляет больше ресурсов в экземпляре кода, но особенно в оперативной памяти, хранящей промежуточную информацию. В нашем случае это будет просто массив идентификаторов, что не должно быть проблемой на самом деле.
- Обслуживание : если вы используете свойства и методы Laravel и вносите какие-либо изменения в базу данных, обновление кода будет проще, чем если вы будете делать более явные запросы и обработку.
- Overkilling? В некоторых случаях, если данные не так велики, оптимизация производительности может оказаться излишней.
Как измерить производительность
Некоторые подсказки о том, как измерить производительность?
- Медленные журналы запросов
- АНАЛИЗ ТАБЛИЦА
- ПОКАЗАТЬ СТАТУС, КАК
- ОБЪЯСНИТЬ ; Расширенный EXPLAIN Формат вывода ; используя объяснение ; объяснить вывод
- ПОКАЗАТЬ ПРЕДУПРЕЖДЕНИЯ
Некоторые интересные инструменты профилирования: