Réponse courte
La troisième option: 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();
Longue réponse
D'une part, (presque) tout ce que vous pouvez faire dans le code est meilleur en termes de performances que dans les requêtes.
D'un autre côté, obtenir plus de données de la base de données que nécessaire serait déjà trop de données (utilisation de la RAM, etc.).
De mon point de vue, vous avez besoin de quelque chose entre les deux, et vous seul saurez où serait l'équilibre, selon les chiffres.
Je suggérerais d'exécuter plusieurs requêtes, la dernière option que vous avez proposée ( Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
):
- Recherchez tous les identifiants, pour toutes les autorisations (5 requêtes)
- Fusionner tous les résultats des formulaires en mémoire et obtenir des valeurs uniques
array_unique($ids)
- Recherchez le modèle de formulaire à l'aide des identificateurs dans une instruction IN ().
Vous pouvez essayer les trois options que vous avez proposées et surveiller les performances, en utilisant un outil pour exécuter la requête plusieurs fois, mais je suis sûr à 99% que la dernière vous donnera les meilleures performances.
Cela peut également changer beaucoup, selon la base de données que vous utilisez, mais si nous parlons de MySQL, par exemple; Dans Une très grande requête utiliserait plus de ressources de base de données, ce qui non seulement passera plus de temps que les requêtes simples, mais verrouillera également la table des écritures, ce qui peut produire des erreurs de blocage (sauf si vous utilisez un serveur esclave).
D'un autre côté, si le nombre d'identifiants de formulaires est très grand, vous pouvez avoir des erreurs pour trop d'espaces réservés, vous pouvez donc vouloir découper les requêtes en groupes de, disons, 500 identifiants (cela dépend beaucoup, car la limite est en taille, pas en nombre de liaisons) et fusionner les résultats en mémoire. Même si vous n'obtenez pas d'erreur de base de données, vous pouvez également voir une grande différence de performances (je parle toujours de MySQL).
la mise en oeuvre
Je suppose que c'est le schéma de base de données:
users
- id
- team_id
forms
- id
- user_id
- team_id
- group_id
permissible
- user_id
- permissible_id
- permissible_type
Une relation polymorphe déjà configurée serait donc admissible .
Par conséquent, les relations seraient:
- Forme propriétaire:
users.id <-> form.user_id
- L'équipe possède le formulaire:
users.team_id <-> form.team_id
- A des autorisations sur un groupe propriétaire d'un formulaire:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
- A des autorisations pour une équipe qui possède un formulaire:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
- A l'autorisation d'un formulaire:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\From'
Version simplifiée:
$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();
Version détaillée:
// 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();
Ressources utilisées:
Performances de la base de données:
- Requêtes à la base de données (à l'exclusion de l'utilisateur): 2 ; un pour obtenir le permis et un autre pour obtenir les formulaires.
- Pas de jointures !!
- Les OR minimums possibles (
user_id = ? OR id IN (?..) OR team_id IN (?...) OR group_id IN (?...)
.
PHP, en mémoire, performances:
- foreach boucle le admissible avec un interrupteur à l' intérieur.
array_values(array_unique())
pour éviter de répéter les identifiants.
- Dans la mémoire, 3 rangées de ids (
$teamIds
, $groupIds
, $formIds
)
- En mémoire, les autorisations pertinentes collection éloquente (cela peut être optimisé, si nécessaire).
Avantages et inconvénients
AVANTAGES:
- Heure : la somme des heures des requêtes uniques est inférieure à l'heure d'une grande requête avec jointures et OU.
- Ressources DB : Les ressources MySQL utilisées par une requête avec jointure et ou instructions sont plus grandes que celles utilisées par la somme de ses requêtes distinctes.
- Argent : moins de ressources de base de données (processeur, RAM, lecture de disque, etc.), plus chères que les ressources PHP.
- Verrous : Au cas où vous n'interrogez pas un serveur esclave en lecture seule, vos requêtes feront moins de lignes verrous de lecture (le verrou de lecture est partagé dans MySQL, donc il ne verrouillera pas une autre lecture, mais il bloquera toute écriture).
- Évolutif : cette approche vous permet de faire plus d'optimisations de performances telles que la segmentation des requêtes.
LES INCONVÉNIENTS:
- Ressources de code : effectuer des calculs dans le code, plutôt que dans la base de données, consommera évidemment plus de ressources dans l'instance de code, mais surtout dans la RAM, stockant les informations intermédiaires. Dans notre cas, ce ne serait qu'un tableau d'identifiants, ce qui ne devrait pas vraiment être un problème.
- Maintenance : si vous utilisez les propriétés et les méthodes de Laravel et que vous modifiez la base de données, il sera plus facile de mettre à jour le code que si vous effectuez des requêtes et un traitement plus explicites.
- Trop de travail? : Dans certains cas, si les données ne sont pas si grandes, l'optimisation des performances peut être exagérée.
Comment mesurer les performances
Quelques indices sur la façon de mesurer la performance?
- Journaux de requête lents
- TABLE D'ANALYSE
- AFFICHER L'ÉTAT DE LA TABLE COMME
- EXPLIQUER ; Format de sortie EXPLAIN étendu ; utiliser expliquer ; expliquer la sortie
- AFFICHER LES AVERTISSEMENTS
Quelques outils de profilage intéressants: