Risposta breve
La terza opzione: 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();
Risposta lunga
Da un lato, (quasi) tutto ciò che puoi fare nel codice, è meglio dal punto di vista delle prestazioni, che farlo nelle query.
D'altra parte, ottenere più dati dal database del necessario sarebbe già troppi dati (utilizzo della RAM e così via).
Dal mio punto di vista, hai bisogno di qualcosa nel mezzo e solo tu saprai dove sarebbe l'equilibrio, a seconda dei numeri.
Suggerirei di eseguire diverse query, l'ultima opzione che hai proposto ( Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
):
- Interroga tutti gli identificatori, per tutte le autorizzazioni (5 query)
- Unisci tutti i risultati dei moduli in memoria e ottieni valori univoci
array_unique($ids)
- Eseguire una query sul modello di modulo, utilizzando gli identificativi in un'istruzione IN ().
Puoi provare le tre opzioni che hai proposto e monitorare le prestazioni, usando alcuni strumenti per eseguire la query più volte, ma sono sicuro al 99% che l'ultimo ti darà le migliori prestazioni.
Questo può anche cambiare molto, a seconda del database che stai usando, ma se stiamo parlando di MySQL, per esempio; In Una query molto grande utilizzerebbe più risorse di database, che non solo impiegheranno più tempo delle semplici query, ma bloccheranno anche la tabella dalle scritture e questo può produrre errori di deadlock (a meno che non si usi un server slave).
D'altra parte, se il numero di id di moduli è molto grande, puoi avere errori per troppi segnaposto, quindi potresti voler dividere le query in gruppi di, diciamo, 500 id (questo dipende molto, come limite è di dimensioni, non in numero di associazioni) e unisce i risultati in memoria. Anche se non ricevi un errore del database, potresti notare anche una grande differenza nelle prestazioni (sto ancora parlando di MySQL).
Implementazione
Presumo che questo sia lo schema del database:
users
- id
- team_id
forms
- id
- user_id
- team_id
- group_id
permissible
- user_id
- permissible_id
- permissible_type
Così ammissibile sarebbe una relazione polimorfica già configurata .
Pertanto, le relazioni sarebbero:
- Modulo proprietario:
users.id <-> form.user_id
- Il team possiede un modulo:
users.team_id <-> form.team_id
- Ha le autorizzazioni per un gruppo che possiede un modulo:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
- Ha le autorizzazioni per un team che possiede un modulo:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
- Ha l'autorizzazione per un modulo:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\From'
Semplifica la versione:
$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();
Versione dettagliata:
// 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();
Risorse utilizzate:
Prestazioni del database:
- Domande al database (escluso l'utente): 2 ; uno per ottenere il permesso e un altro per ottenere i moduli.
- Nessuna partecipazione !!
- Gli OR minimi possibili (
user_id = ? OR id IN (?..) OR team_id IN (?...) OR group_id IN (?...)
.
PHP, in memoria, prestazioni:
- foreach ad anello il ammissibile con un interruttore all'interno.
array_values(array_unique())
per evitare di ripetere gli ID.
- In memoria, 3 matrici di ids (
$teamIds
, $groupIds
, $formIds
)
- In memoria, raccolta eloquente delle autorizzazioni pertinenti (questo può essere ottimizzato, se necessario).
Pro e contro
PROFESSIONISTI:
- Tempo : la somma dei tempi delle singole query è inferiore al tempo di una query di grandi dimensioni con join e OR.
- Risorse DB : le risorse MySQL utilizzate da una query con join e o istruzioni sono maggiori di quelle utilizzate dalla somma delle sue query separate.
- Denaro : meno risorse del database (processore, RAM, lettura del disco, ecc.), Che sono più costose delle risorse PHP.
- Blocchi : nel caso in cui non si stia eseguendo una query su un server slave di sola lettura, le query eseguiranno un numero inferiore di blocchi di lettura delle righe (il blocco di lettura è condiviso in MySQL, quindi non bloccherà un'altra lettura, ma bloccherà qualsiasi scrittura).
- Scalabile : questo approccio consente di apportare ulteriori ottimizzazioni delle prestazioni, ad esempio bloccare le query.
CONS:
- Risorse di codice : fare calcoli nel codice, piuttosto che nel database, consumerà ovviamente più risorse nell'istanza del codice, ma soprattutto nella RAM, memorizzando le informazioni di mezzo. Nel nostro caso, questo sarebbe solo un array di ID, che non dovrebbe essere un problema.
- Manutenzione : se si utilizzano le proprietà e i metodi di Laravel e si apportano modifiche al database, sarà più semplice aggiornarlo nel codice che se si effettuano query ed elaborazioni più esplicite.
- Overkilling? : In alcuni casi, se i dati non sono così grandi, l'ottimizzazione delle prestazioni potrebbe essere eccessiva.
Come misurare le prestazioni
Alcuni indizi su come misurare le prestazioni?
- Log delle query lenti
- TABELLA ANALISI
- MOSTRA STATO TABELLA COME
- SPIEGARE ; Formato di output EXPLAIN esteso ; usando spiega ; spiegare l'output
- MOSTRA AVVERTENZE
Alcuni strumenti di profilazione interessanti: