简短答案
第三种选择: 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();
长答案
一方面,(几乎)您可以在代码中执行的所有操作在性能方面都比在查询中更好。
另一方面,从数据库中获取比必要数量更多的数据将已经是过多的数据(RAM使用情况等等)。
从我的角度来看,您需要介于两者之间的东西,只有您才会知道余额在哪里,具体取决于数字。
我建议运行几个查询,这是您建议的最后一个选项(Query all identifiers for all permissions (5), then query the Form model using the identifiers in an IN() statement
):
- 查询所有标识符的所有权限(5个查询)
- 合并所有表单结果到内存中,并获取唯一值
array_unique($ids)
- 使用IN()语句中的标识符查询Form模型。
您可以尝试使用建议的三个选项并使用某种工具多次运行查询来监视性能,但我99%确信最后一个选项将为您提供最佳性能。
这可能也会发生很大变化,具体取决于您使用的是哪个数据库,但是例如,如果我们谈论的是MySQL,则可能会有所不同。在一个非常大的查询中,将使用更多的数据库资源,这不仅比简单查询花费更多的时间,而且还会锁定表以防止写入,并且这可能产生死锁错误(除非您使用从属服务器)。
另一方面,如果表单ID的数量很大,则占位符过多会出错,因此您可能希望将查询分块,例如500个ID(这在很大程度上取决于限制)大小,而不是绑定数),然后将结果合并到内存中。即使没有出现数据库错误,您也可能会看到性能上的巨大差异(我仍在谈论MySQL)。
实作
我将假定这是数据库方案:
users
- id
- team_id
forms
- id
- user_id
- team_id
- group_id
permissible
- user_id
- permissible_id
- permissible_type
所以可以允许的是已经配置好的多态关系。
因此,关系为:
- 拥有表格:
users.id <-> form.user_id
- 团队拥有表格:
users.team_id <-> form.team_id
- 对拥有表单的组具有权限:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Team'
- 拥有拥有表单的团队的权限:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\Group'
- 有权使用表格:
permissible.user_id <-> users.id && permissible.permissible_type = 'App\From'
简化版本:
$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();
详细版本:
// 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();
使用的资源:
数据库性能:
- 查询数据库(不包括用户):2 ; 一个获得许可,另一个获得表格。
- 没有加入!
- 可能的最小OR(
user_id = ? OR id IN (?..) OR team_id IN (?...) OR group_id IN (?...)
。
PHP,在内存中,性能:
- foreach在内部允许开关循环。
array_values(array_unique())
避免重复ID。
- 在存储器中,IDS的3门阵列(
$teamIds
,$groupIds
,$formIds
)
- 在内存中,对相关权限进行雄辩的收集(如果需要,可以对其进行优化)。
利弊
优点:
- 时间:单个查询的时间总和小于具有联接和OR的大型查询的时间。
- 数据库资源:带有join和or语句的查询所使用的MySQL资源大于其单独查询的总和所使用的MySQL资源。
- 资金:更少的数据库资源(处理器,RAM,磁盘读取等),比PHP资源昂贵。
- 锁:如果您不查询只读从属服务器,则查询将使更少的行具有读取锁(该读取锁在MySQL中是共享的,因此它不会锁定其他读取,但会阻止任何写入)。
- 可扩展:此方法使您可以进行更多性能优化,例如对查询进行分块。
缺点:
- 代码资源:用代码而不是数据库进行计算显然会在代码实例中消耗更多的资源,但是特别是在RAM中,存储中间信息。在我们的例子中,这只是一个id数组,这实际上不是问题。
- 维护:如果您使用Laravel的属性和方法,并且对数据库进行了任何更改,则与进行更明确的查询和处理相比,更新代码将更加容易。
- 杀人过度?:在某些情况下,如果数据不是那么大,那么优化性能可能会导致过大的损失。
如何衡量绩效
有关如何衡量性能的一些线索?
- 慢查询日志
- 分析表
- 显示表格状态
- 说明 ; 扩展的EXPLAIN输出格式 ; 使用说明 ; 解释输出
- 显示警告
一些有趣的分析工具: