Laravel渴望加载问题。如何解决收到的N + 1警告?

时间:2019-11-15 23:49:39

标签: php laravel eager-loading

我有一个在循环中使用的视图中使用的函数。因此它被多次使用。我认为正因为如此,我遇到了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问题。 我不确定如何解决此问题。

enter image description here

使用@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) }}

1 个答案:

答案 0 :(得分:2)

您的视图最初是 获得了一个$newUserAccount并急切加载了applications。因此,该循环中的$newUserAccount集合将加载applications

但是,在该刀片页面上循环时,您将id的{​​{1}}传递回控制器,在那里您要加载全新的newUserAccount模型在该循环中的时间

此外,此行:

newUserAccount

部分是您的$newUser = $newUserAccount->with('applications')->where('id', $newUserId)->first(); 问题的来源。您的n+1对象是从路由模型绑定中传入的(我想,尽管您没有类型提示,但是除非该对象进入,否则它将断开),但是您渴望加载{{1 }}在刀片的每个循环中的新实例上。

因此它正在发送$newUserAccount,创建applications对象,然后一遍又一遍地加载应用程序。

该行还有另一个问题:id已经是来自方法参数的单个对象-它已经是基于newUserAccount的第一个对象,但是您是运行上面的行,就好像它是一个集合。我认为这可能是多余的,但是我不确定,因为我真的不知道该对象在哪里。

要解决重复数据库调用的问题,请在发送到刀片文件之前,使用$newUserAccount方法 进行所有准备逻辑,而不是让刀片文件继续回调给控制器在id的每个循环中。换句话说,在第一次进行刀片操作之前,请先获取appDefaultPassword(),并提前获取newUserAccount->tasks的列表,您可以从刀片循环的逻辑中进行选择。