【问题标题】:Laravel 4.1 Eager Loading working, but queries still executed in viewLaravel 4.1 Eager Loading 工作,但查询仍在视图中执行
【发布时间】:2014-06-26 22:02:47
【问题描述】:

我有一个简单的“项目”模型。一个项目可以有很多任务,每个任务都属于一个员工。 我有一个页面显示 +- 100 个项目,以及所有相关的(独特的)员工。这导致执行了 500 多个查询,因此页面加载时间飙升至不可接受的水平。所以我尝试了急切的加载:

$projects->with(array('tasks' => function($query)
{
    $query->select('project_id', 'employee_id')->distinct()->get();
}));

据我通过调试可以看出,这给了我想要的结果。对于每个项目,任务都被正确检索,没有重复的员工。

但是。当我尝试在视图中加载任务时,Laravel 仍在执行 400 多个查询来检索每个项目的任务。

@foreach ($projects as $project)
   @foreach($project->tasks as $task)
       @if($task->employee_id != 0 )
           <img src="{{UserHelper::getGravatarUrl($task->employee->id)}}" alt="{{{ $task->employee->name }}}"
        @endif
    @endforeach
@endforeach 

我曾尝试使用 tasks()tasks()-&gt;get() 代替,但无济于事。如何访问急切加载的属性? 我不喜欢使用脏的原始查询,因为 Eloquent 应该能够胜任这项任务。

【问题讨论】:

    标签: php mysql laravel-4 eager-loading


    【解决方案1】:

    我将在 WereWolf 的回答中添加一点内容:

    1) 获取对象的正确方法是:

    $projects->with(array('tasks' => function($query)
    {
        $query->select('project_id', 'employee_id')->distinct();
    }))->get();
    

    get 是 OUTSIDE,而不是 Eager Loading 的内部。

    2) 您的函数任务(在项目中)应该具有一对多的关系,该关系指向具有 WereWolf 提到的外键的任务模型

    3) Eager Loading 使您能够提前为一对一加载 N+1,例如,如果您需要在项目中引用员工,以便查看哪些任务交给了哪个员工,而不是运行一个

    SELECT * FROM employee WHERE id = ?
    

    在您为从关系中获取数据而运行的每个 $task-&gt;employee()-&gt;name

    所以急切加载将首先SELECT * FROM employee - 然后在任务中急切加载 WHERE IN (?,?,?)(如果我错了,请纠正我)

    4) 让我想到第四点是急切加载员工,这将减少查询。建立一对多内部任务的函数关系。

    【讨论】:

    • 事实证明,即使在匿名函数中使用 get(),热切加载也按预期工作。多次检索用户的是 UserHelper。不过,您的解释非常好,急切加载就是这样工作的。
    • @Maarten00 来自一个非急切的加载框架和纯 SQL 连接等 - 我知道它是什么感觉 :)
    【解决方案2】:

    据此,您的关系应该使用projects 表的id 字段和tasks 表的project_id 字段建立。还要确保你在最后调用了get() 方法。

    您可以在预加载中使用选择,但请确保您已选择关系构建器键,例如,如果您的 Project 有许多 Task 并且您的 tasks 表包含 project_id 作为 foreign key那么您需要在select 中选择该外键。所以请确保project_idtasks 表中作为foreign key 可用,以建立与projects 表的关系。例如,检查这个:

    $r = Role::with(['users' => function($q) {
        $q->select('role_id', 'username');
    }])->get();
    

    我有users 表,其中包含role_id 作为外键来建立rolesusers 表之间的关系,并且在Role 模型中声明了one-to-many 关系,因此我可以使用select()急切加载相关的User 模型(只有role_id 和'用户名' 将加载到相关的User 模型中)但没有select 中的role_id 我无法加载相关的User 模型.

    【讨论】:

      【解决方案3】:

      您不能依赖 select() 来进行预加载,因为 Eloquent 可能需要模型的所有属性才能正确预加载。要么去掉 select,要么尝试在 select 语句中添加更多列。

      【讨论】:

        【解决方案4】:

        除了上述答案之外,我还将添加自己的查询,该查询可以完美地加载每个项目的所有任务,以及员工和员工的个人资料。

        $projects->with(array('tasks' => function($query)
        { 
           $query->groupBy('project_id', 'employee_id')->with(array('employee.profiles'));
        }));
        

        在我看来,我正在像这样访问这些:

        @foreach($projects as $project)    
            @foreach($project->tasks as $task)
                @if($task->employee_id != 0 )
                    <img src="{{UserHelper::getGravatarUrl($task->employee)}}" alt="{{{ $task->employee->name }}}">
                @endif
            @endforeach
        @endforeach
        

        这很好用,并且大大减少了我的查询量。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-09-17
          • 2021-10-17
          • 1970-01-01
          • 1970-01-01
          • 2018-01-25
          • 2018-11-16
          • 1970-01-01
          相关资源
          最近更新 更多