Respuesta corta
La tercera opción: 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();
Respuesta larga
Por un lado, (casi) todo lo que puede hacer en código, es mejor en términos de rendimiento, que hacerlo en consultas.
Por otro lado, obtener más datos de la base de datos de los necesarios ya sería demasiados datos (uso de RAM, etc.).
Desde mi punto de vista, necesitas algo intermedio, y solo tú sabrás dónde estaría el saldo, dependiendo de los números.
Sugeriría ejecutar varias consultas, la última opción que propuso ( Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
):
- Consulta todos los identificadores, para todos los permisos (5 consultas)
- Combina todos los resultados de los formularios en la memoria y obtén valores únicos
array_unique($ids)
- Consulte el modelo de formulario, utilizando los identificadores en una declaración IN ().
Puede probar las tres opciones que propuso y controlar el rendimiento, utilizando alguna herramienta para ejecutar la consulta varias veces, pero estoy 99% seguro de que la última le dará el mejor rendimiento.
Esto también puede cambiar mucho, dependiendo de qué base de datos esté usando, pero si estamos hablando de MySQL, por ejemplo; En una consulta muy grande, se usarían más recursos de la base de datos, lo que no solo pasará más tiempo que las consultas simples, sino que también bloqueará la tabla de las escrituras, y esto puede producir errores de punto muerto (a menos que use un servidor esclavo).
Por otro lado, si el número de identificadores de formularios es muy grande, puede tener errores para demasiados marcadores de posición, por lo que es posible que desee agrupar las consultas en grupos de, digamos, 500 identificadores (esto depende mucho, ya que el límite está en tamaño, no en número de enlaces), y combina los resultados en la memoria. Incluso si no recibe un error en la base de datos, también puede ver una gran diferencia en el rendimiento (todavía estoy hablando de MySQL).
Implementación
Asumiré que este es el esquema de la base de datos:
users
- id
- team_id
forms
- id
- user_id
- team_id
- group_id
permissible
- user_id
- permissible_id
- permissible_type
Tan permisible sería una relación polimórfica ya configurada .
Por lo tanto, las relaciones serían:
- Posee el formulario:
users.id <-> form.user_id
- El equipo posee el formulario:
users.team_id <-> form.team_id
- Tiene permisos para un grupo que posee un formulario:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
- Tiene permisos para un equipo que posee un formulario:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
- Tiene permiso para un formulario:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\From'
Versión simplificada:
$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();
Versión detallada:
// 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();
Recursos utilizados:
Rendimiento de la base de datos:
- Consultas a la base de datos (excluyendo al usuario): 2 ; uno para obtener lo permitido y otro para obtener los formularios.
- No se une!
- Los OR mínimos posibles (
user_id = ? OR id IN (?..) OR team_id IN (?...) OR group_id IN (?...)
.
PHP, en memoria, rendimiento:
- foreach girando lo permitido con un interruptor dentro.
array_values(array_unique())
para evitar repetir los identificadores.
- En la memoria, 3 matrices de ids (
$teamIds
, $groupIds
, $formIds
)
- En memoria, permisos relevantes colección elocuente (esto se puede optimizar, si es necesario).
Pros y contras
PROS:
- Tiempo : la suma de los tiempos de consultas individuales es menor que el tiempo de una consulta grande con combinaciones y OR.
- Recursos de base de datos : los recursos de MySQL utilizados por una consulta con uniones o declaraciones, son mayores que los utilizados por la suma de sus consultas separadas.
- Dinero : Menos recursos de base de datos (procesador, RAM, lectura de disco, etc.), que son más caros que los recursos PHP.
- Bloqueos : en caso de que no esté consultando un servidor esclavo de solo lectura, sus consultas harán menos filas de bloqueos de lectura (el bloqueo de lectura se comparte en MySQL, por lo que no bloqueará otra lectura, pero bloqueará cualquier escritura).
- Escalable : este enfoque le permite realizar más optimizaciones de rendimiento, como fragmentar las consultas.
CONTRAS:
- Recursos de código : hacer cálculos en código, en lugar de en la base de datos, obviamente consumirá más recursos en la instancia de código, pero especialmente en la RAM, almacenando la información intermedia. En nuestro caso, esto sería solo una serie de identificadores, que en realidad no debería ser un problema.
- Mantenimiento : si utiliza las propiedades y métodos de Laravel, y realiza algún cambio en la base de datos, será más fácil actualizar el código que si realiza consultas y procesos más explícitos.
- ¿Matanza excesiva? : En algunos casos, si los datos no son tan grandes, optimizar el rendimiento puede ser exagerado.
Cómo medir el desempeño
¿Algunas pistas sobre cómo medir el rendimiento?
- Registros de consulta lentos
- TABLA DE ANÁLISIS
- MOSTRAR ESTADO DE LA MESA COMO
- EXPLICAR ; Formato de salida EXPLAIN extendido ; usando explicar ; explicar salida
- MOSTRAR ADVERTENCIAS
Algunas herramientas de perfilado interesantes: