Resposta curta
A terceira opção: 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();
Resposta longa
Por um lado, (quase) tudo o que você pode fazer no código é melhor em termos de desempenho do que nas consultas.
Por outro lado, obter mais dados do banco de dados do que o necessário já seria um excesso de dados (uso de RAM e assim por diante).
Na minha perspectiva, você precisa de algo intermediário, e só você saberá onde estaria o equilíbrio, dependendo dos números.
Sugiro executar várias consultas, a última opção que você propôs ( Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
):
- Consultar todos os identificadores, para todas as permissões (5 consultas)
- Mesclar todos os resultados de formulários na memória e obter valores exclusivos
array_unique($ids)
- Consulte o modelo de formulário, usando os identificadores em uma instrução IN ().
Você pode tentar as três opções propostas e monitorar o desempenho, usando alguma ferramenta para executar a consulta várias vezes, mas tenho 99% de certeza de que a última fornecerá o melhor desempenho.
Isso também pode mudar bastante, dependendo de qual banco de dados você estiver usando, mas se estamos falando sobre MySQL, por exemplo; Em Uma consulta muito grande usaria mais recursos do banco de dados, que não apenas gastariam mais tempo do que consultas simples, mas também bloqueariam a tabela de gravações, e isso pode produzir erros de conflito (a menos que você use um servidor escravo).
Por outro lado, se o número de IDs de formulários for muito grande, você poderá ter erros para muitos espaços reservados, portanto, você pode agrupar as consultas em grupos de, digamos, 500 IDs (isso depende muito, pois o limite tem tamanho, não número de ligações) e mescla os resultados na memória. Mesmo se você não receber um erro no banco de dados, também poderá ver uma grande diferença no desempenho (ainda estou falando do MySQL).
Implementação
Vou assumir que este é o esquema do banco de dados:
users
- id
- team_id
forms
- id
- user_id
- team_id
- group_id
permissible
- user_id
- permissible_id
- permissible_type
Tão admissível seria uma relação polimórfica já configurada .
Portanto, as relações seriam:
- Possui o formulário:
users.id <-> form.user_id
- A equipe possui o formulário:
users.team_id <-> form.team_id
- Tem permissões para um grupo que possui um formulário:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
- Tem permissões para uma equipe que possui um formulário:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
- Tem permissão para um formulário:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\From'
Simplifique a versão:
$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();
Versão detalhada:
// 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:
Desempenho do banco de dados:
- Consultas ao banco de dados (excluindo o usuário): 2 ; um para obter o permitido e outro para obter os formulários.
- Sem junções !!
- As ORs mínimas possíveis (
user_id = ? OR id IN (?..) OR team_id IN (?...) OR group_id IN (?...)
.
PHP, na memória, desempenho:
- foreach looping o permissível com um interruptor dentro.
array_values(array_unique())
para evitar repetir os IDs.
- Na memória, 3 matrizes de ids (
$teamIds
, $groupIds
,$formIds
)
- Na memória, coleta eloquente de permissões relevantes (isso pode ser otimizado, se necessário).
Prós e contras
PROS:
- Tempo : a soma dos tempos de consultas únicas é menor que o tempo de uma grande consulta com junções e OR.
- Recursos de banco de dados : Os recursos MySQL usados por uma consulta com instruções join e ou são maiores que os usados pela soma de suas consultas separadas.
- Dinheiro : Menos recursos de banco de dados (processador, RAM, leitura de disco etc.), que são mais caros que os recursos PHP.
- Bloqueios : Caso você não esteja consultando um servidor escravo somente leitura, suas consultas criarão menos bloqueios de leitura de linhas (o bloqueio de leitura é compartilhado no MySQL, para que não bloqueie outra leitura, mas bloqueará qualquer gravação).
- Escalonável : essa abordagem permite fazer mais otimizações de desempenho, como agrupar as consultas.
CONTRAS:
- Recursos de código : fazer cálculos no código, e não no banco de dados, obviamente consumirá mais recursos na instância do código, mas principalmente na RAM, armazenando as informações do meio. No nosso caso, isso seria apenas uma matriz de IDs, o que não deveria ser realmente um problema.
- Manutenção : Se você usar as propriedades e os métodos do Laravel e fizer alguma alteração no banco de dados, será mais fácil atualizar o código do que se você fizer consultas e processamento mais explícitos.
- Overkilling? : Em alguns casos, se os dados não forem tão grandes, otimizar o desempenho pode ser um exagero.
Como medir o desempenho
Algumas dicas sobre como medir o desempenho?
- Logs de consulta lentos
- ANALISAR A TABELA
- MOSTRAR O STATUS DA MESA COMO
- EXPLIQUE ; Formato de saída EXPLAIN estendido ; usando explicar ; explicar saída
- MOSTRAR AVISOS
Algumas ferramentas interessantes de criação de perfil: