Krótka odpowiedź
Trzecia opcja: 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();
Długa odpowiedź
Z jednej strony (prawie) wszystko, co można zrobić w kodzie, jest lepsze pod względem wydajności niż w przypadku zapytań.
Z drugiej strony, pobieranie większej ilości danych z bazy danych, niż to konieczne, byłoby już zbyt dużą ilością danych (użycie pamięci RAM i tak dalej).
Z mojej perspektywy potrzebujesz czegoś pomiędzy, a tylko będziesz wiedział, gdzie będzie równowaga, w zależności od liczb.
Sugerowałbym uruchomienie kilku zapytań, ostatnia zaproponowana opcja ( Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
):
- Zapytaj o wszystkie identyfikatory dla wszystkich uprawnień (5 zapytań)
- Scal wszystkie wyniki formularzy w pamięci i uzyskaj unikalne wartości
array_unique($ids)
- Zapytaj o model Form, używając identyfikatorów w instrukcji IN ().
Możesz wypróbować trzy zaproponowane opcje i monitorować wydajność, używając narzędzia do wielokrotnego uruchamiania zapytania, ale jestem 99% pewien, że ostatnia zapewni najlepszą wydajność.
To również może się wiele zmienić, w zależności od używanej bazy danych, ale jeśli mówimy na przykład o MySQL; W bardzo dużym zapytaniu zużyłoby więcej zasobów bazy danych, co nie tylko poświęci więcej czasu niż proste zapytania, ale także zablokuje tabelę przed zapisem, co może powodować błędy zakleszczenia (chyba że użyjesz serwera podrzędnego).
Z drugiej strony, jeśli liczba identyfikatorów formularzy jest bardzo duża, możesz mieć błędy dla zbyt wielu symboli zastępczych, więc możesz chcieć podzielić zapytania na grupy, powiedzmy, 500 identyfikatorów (zależy to bardzo od limitu ma rozmiar, a nie liczbę powiązań) i scal wyniki w pamięci. Nawet jeśli nie dostaniesz błędu bazy danych, możesz również zauważyć dużą różnicę w wydajności (wciąż mówię o MySQL).
Realizacja
Zakładam, że jest to schemat bazy danych:
users
- id
- team_id
forms
- id
- user_id
- team_id
- group_id
permissible
- user_id
- permissible_id
- permissible_type
Tak więc dopuszczalna byłaby już skonfigurowana relacja polimorficzna .
Dlatego relacje byłyby następujące:
- Forma właściciela:
users.id <-> form.user_id
- Zespół jest właścicielem formularza:
users.team_id <-> form.team_id
- Ma uprawnienia do grupy, która jest właścicielem formularza:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
- Ma uprawnienia do zespołu, który jest właścicielem formularza:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
- Ma pozwolenie na formularz:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\From'
Uprość wersję:
$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();
Wersja szczegółowa:
// 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();
Wykorzystane zasoby:
Wydajność bazy danych:
- Zapytania do bazy danych (z wyłączeniem użytkownika): 2 ; jeden, aby uzyskać dopuszczalny, a drugi, aby uzyskać formularze.
- Brak dołączeń !!
- Możliwe minimalne OR (
user_id = ? OR id IN (?..) OR team_id IN (?...) OR group_id IN (?...)
.
PHP, w pamięci, wydajność:
- przewidzieć pętlę dopuszczalną z przełącznikiem w środku.
array_values(array_unique())
aby uniknąć powtarzania identyfikatorów.
- W pamięci, 3 tablice identyfikatorów (
$teamIds
, $groupIds
, $formIds
)
- W pamięci odpowiednie elokwentne zbieranie uprawnień (w razie potrzeby można to zoptymalizować).
Plusy i minusy
Plusy:
- Czas : Suma czasów pojedynczych zapytań jest krótsza niż czas dużego zapytania z połączeniami i OR.
- Zasoby DB : zasoby MySQL używane przez zapytanie z łączeniem i / lub instrukcjami są większe niż zużyte przez sumę jego oddzielnych zapytań.
- Pieniądze : mniej zasobów bazy danych (procesor, pamięć RAM, odczyt dysku itp.), Które są droższe niż zasoby PHP.
- Blokady : W przypadku, gdy nie wysyłasz zapytania do serwera podrzędnego tylko do odczytu, twoje zapytania spowodują mniej blokad odczytu blokad (blokada odczytu jest wspólna w MySQL, więc nie zablokuje kolejnego odczytu, ale zablokuje jakikolwiek zapis).
- Skalowalne : to podejście pozwala na większą optymalizację wydajności, na przykład na częściowe zapytania.
CONS:
- Zasoby kodu : Wykonywanie obliczeń w kodzie, a nie w bazie danych, z pewnością zużyje więcej zasobów w instancji kodu, ale szczególnie w pamięci RAM, przechowując środkową informację. W naszym przypadku byłby to tylko szereg identyfikatorów, co nie powinno stanowić problemu.
- Konserwacja : Jeśli użyjesz właściwości i metod Laravela i dokonasz jakichkolwiek zmian w bazie danych, łatwiej będzie zaktualizować kod niż w przypadku bardziej wyraźnych zapytań i przetwarzania.
- Overkilling? : W niektórych przypadkach, jeśli dane nie są tak duże, optymalizacja wydajności może być nadmierna.
Jak mierzyć wydajność
Kilka wskazówek na temat pomiaru wydajności?
- Wolne dzienniki zapytań
- TABELA ANALIZOWA
- POKAŻ STATUS TABELI JAK
- WYJAŚNIĆ ; Rozszerzony format wyjściowy EXPLAIN ; używając wyjaśnienia ; wyjaśnić wyjście
- POKAŻ OSTRZEŻENIA
Kilka interesujących narzędzi profilujących: