Laravel 预加载问题。如何修复收到的 N+1 警告?
Laravel eager loading problem. How do I fix the N+1 warning that I'm getting?
我有一个函数,我在一个循环中使用的视图中使用它。所以它被多次使用。我认为因此我有 N+1 问题。在我的函数中,我急切地加载模型,但我仍然收到 N+1 警告。
我在 TaskController 上的函数
public function appDefaultPassword($newUserAccount, $newUserId)
{
// Check if application default_password is set
if($this->application->default_password) {
return $this->application->decryptDefaultPass();
// Check if application use_network_password is true
} elseif($this->application->use_network_password) {
// Get newUserAccount
$newUser = $newUserAccount->with('applications')->where('id', $newUserId)->first();
$netapp = $newUser->applications->where('app_type', 'LDAP')->first();
$tasks = $this->with('actions')->where('model_id', $newUserAccount->id)->where('application_id', $netapp->id)->first();
$taskAction = $tasks->actions->where('password', !NULL)->last();
return decrypt($taskAction->password);
}
}
在我的 NewUserAccountController 中,我也很想加载应用程序和 task.actions
public function show($newUserAccount)
{
$newUserAccount = NewUserAccount::with('applications', 'task.application.admins', 'task.actions', 'approvalActions')->find($newUserAccount);
}
在我看来:
@foreach($newUserAccount->task as $task)
...
{{ $task->appDefaultPassword($newUserAccount->id) }}
...
@endforeach
我正在使用 Laravel N+1 查询检测器来检测这个 N+1 问题。
我不确定如何修复我的功能以满足此要求。
使用@Watercayman 建议...
更新代码:
appDefaultPassword 方法
$netapp = $newUserAccount->applications->where('app_type', 'LDAP')->first();
$tasks = $newUserAccount->task->where('model_id', $newUserAccount->id)->where('application_id', $netapp->id)->first();
foreach($tasks->actions as $taskAction);
$taskAction = $tasks->actions->where('password', !NULL)->last();
return decrypt($taskAction->password);
查看:
{{ $task->appDefaultPassword($newUserAccount) }}
您的观点是 最初 得到一个 $newUserAccount
并且急切加载 applications
。因此,该循环中的 $newUserAccount
collection 将加载 applications
。
但是,在那个 blade 页面上循环时,您正在将 newUserAccount
的 id
传递回控制器,您正在加载一个全新的 newUserAccount
模型 每次在该循环中 。
此外,这一行:
$newUser = $newUserAccount->with('applications')->where('id', $newUserId)->first();
部分是您的 n+1
问题的来源。您的 $newUserAccount
object 已从路由模型绑定进入(我假设,尽管您没有类型提示 - 但除非 object 进入,否则它会中断),但是您渴望在 blade 的每个循环中将 applications
加载到新实例上。
因此它正在发送 id
,生成 newUserAccount
object,然后一遍又一遍地加载应用程序。
该行还有另一个问题:$newUserAccount
已经是来自方法参数的单个 object - 它已经是第一个 object 基于id
,但你是 运行 上面那一行就好像是 collection。我认为这可能是无关紧要的,但我不确定,因为我真的不知道参数中的 object 来自哪里。
要修复重复的数据库调用,请在方法中执行所有准备逻辑,然后发送到您的blade文件,而不是让blade 文件在 newUserAccount->tasks
的每个循环中不断回调控制器。换句话说,在第一次去 blade 之前,有 newUserAccounts
,并提前得到一个 $taskAction->passwords
的列表,你可以从 [=49] 的逻辑中选择=]循环。
我有一个函数,我在一个循环中使用的视图中使用它。所以它被多次使用。我认为因此我有 N+1 问题。在我的函数中,我急切地加载模型,但我仍然收到 N+1 警告。
我在 TaskController 上的函数
public function appDefaultPassword($newUserAccount, $newUserId)
{
// Check if application default_password is set
if($this->application->default_password) {
return $this->application->decryptDefaultPass();
// Check if application use_network_password is true
} elseif($this->application->use_network_password) {
// Get newUserAccount
$newUser = $newUserAccount->with('applications')->where('id', $newUserId)->first();
$netapp = $newUser->applications->where('app_type', 'LDAP')->first();
$tasks = $this->with('actions')->where('model_id', $newUserAccount->id)->where('application_id', $netapp->id)->first();
$taskAction = $tasks->actions->where('password', !NULL)->last();
return decrypt($taskAction->password);
}
}
在我的 NewUserAccountController 中,我也很想加载应用程序和 task.actions
public function show($newUserAccount)
{
$newUserAccount = NewUserAccount::with('applications', 'task.application.admins', 'task.actions', 'approvalActions')->find($newUserAccount);
}
在我看来:
@foreach($newUserAccount->task as $task)
...
{{ $task->appDefaultPassword($newUserAccount->id) }}
...
@endforeach
我正在使用 Laravel N+1 查询检测器来检测这个 N+1 问题。 我不确定如何修复我的功能以满足此要求。
使用@Watercayman 建议...
更新代码:
appDefaultPassword 方法
$netapp = $newUserAccount->applications->where('app_type', 'LDAP')->first();
$tasks = $newUserAccount->task->where('model_id', $newUserAccount->id)->where('application_id', $netapp->id)->first();
foreach($tasks->actions as $taskAction);
$taskAction = $tasks->actions->where('password', !NULL)->last();
return decrypt($taskAction->password);
查看:
{{ $task->appDefaultPassword($newUserAccount) }}
您的观点是 最初 得到一个 $newUserAccount
并且急切加载 applications
。因此,该循环中的 $newUserAccount
collection 将加载 applications
。
但是,在那个 blade 页面上循环时,您正在将 newUserAccount
的 id
传递回控制器,您正在加载一个全新的 newUserAccount
模型 每次在该循环中 。
此外,这一行:
$newUser = $newUserAccount->with('applications')->where('id', $newUserId)->first();
部分是您的 n+1
问题的来源。您的 $newUserAccount
object 已从路由模型绑定进入(我假设,尽管您没有类型提示 - 但除非 object 进入,否则它会中断),但是您渴望在 blade 的每个循环中将 applications
加载到新实例上。
因此它正在发送 id
,生成 newUserAccount
object,然后一遍又一遍地加载应用程序。
该行还有另一个问题:$newUserAccount
已经是来自方法参数的单个 object - 它已经是第一个 object 基于id
,但你是 运行 上面那一行就好像是 collection。我认为这可能是无关紧要的,但我不确定,因为我真的不知道参数中的 object 来自哪里。
要修复重复的数据库调用,请在方法中执行所有准备逻辑,然后发送到您的blade文件,而不是让blade 文件在 newUserAccount->tasks
的每个循环中不断回调控制器。换句话说,在第一次去 blade 之前,有 newUserAccounts
,并提前得到一个 $taskAction->passwords
的列表,你可以从 [=49] 的逻辑中选择=]循环。