Коротка відповідь
Третій варіант: 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, в пам'яті, продуктивність:
- передбачити петлю допустимого з вимикачем всередині.
array_values(array_unique())
щоб не повторювати ідентифікатори.
- У пам'яті, 3 масиви ідентифікаторів (
$teamIds
, $groupIds
, $formIds
)
- У пам'яті відповідні дозволи красномовно колекціонуються (це можна оптимізувати, якщо потрібно).
Плюси і мінуси
ПРО:
- Час : сума разів одиничних запитів менша за час великого запиту з об'єднаннями та АБО.
- Ресурси БД : Ресурси MySQL, які використовуються в запиті з приєднанням або операторами, більше, ніж використані сумою окремих запитів.
- Гроші : менше ресурсів бази даних (процесор, оперативна пам’ять, читання дисків тощо), які дорожчі, ніж ресурси PHP.
- Блокування : Якщо ви не запитуєте підлеглий сервер, який працює лише для читання, ваші запити зроблять менше рядків блоку читання блоків (блокування читання поділяється в MySQL, тому він не блокує іншого читання, але блокує будь-яке записування).
- Масштабованість : Цей підхід дозволяє зробити більше оптимізацій продуктивності, таких як фрагменти запитів.
Мінуси:
- Кодові ресурси : Здійснюючи обчислення в коді, а не в базі даних, очевидно, буде витрачено більше ресурсів в екземплярі коду, але особливо в оперативній пам'яті, зберігаючи середню інформацію. У нашому випадку це був би лише масив ідентифікаторів, що насправді не повинно бути проблемою.
- Технічне обслуговування : Якщо ви використовуєте властивості та методи Laravel і вносите будь-які зміни в базу даних, оновлення коду буде простіше, ніж якщо ви робите більш чіткі запити та обробку.
- Завищення? : У деяких випадках, якщо дані не такі великі, оптимізація продуктивності може бути надмірною.
Як виміряти продуктивність
Деякі підказки про те, як виміряти продуктивність?
- Повільні журнали запитів
- ТАБЛИКА АНАЛІЗУ
- ПОКАЗУЙТЕ ТАБЛИЧНИЙ СТАТУС, ЩО СПІЛЬНО
- ПОЯСНІТЬ ; Розширений EXPLAIN вихідний формат ; використовуючи пояснення ; пояснити вихід
- ПОКАЗУЙТЕ ПОПЕРЕДЖЕННЯ
Деякі цікаві інструменти профілювання: