为了账号安全,请及时绑定邮箱和手机立即绑定

高效加载或缓存基于许多嵌套关系的视图

高效加载或缓存基于许多嵌套关系的视图

PHP
缥缈止盈 2023-08-19 16:42:32
我有无限的类别、子类别、子子类别等等。结构:main--main1-----main2 ( child of main1 )-----main3 ( child of main1 )--main4test我的代码:在Category.php modelpublic function categories(){    return $this->hasMany(Category::class);}public function childrenCategories(){    return $this->hasMany(Category::class);}在index.blade.php:<select>    @foreach ($categories as $category)        <option value="{{ $category->id }}">{{ $category->category_name }}</option>        @foreach ($cat->childrenCategories as $childCategory)            @include('partials._child_categroy', ['child_category' => $childCategory])        @endforeach    @endforeach</select>在 _child_categroy.blade.php 中<option value="{{ $child_category->id }}">--- {{ $child_category->category_name }}</option>@if ($child_category->categories)    @foreach ($child_category->categories as $childCategory)        @include('partials._child_categroy', ['child_category' => $childCategory])    @endforeach@endif到目前为止一切都很好,但是当我打开调试器时,每个类别级别都有三个查询,存在循环!这是我的问题,如果我的链变得越来越大,那么 foreach 将会增加,这对性能不利!请问有什么建议吗?
查看完整描述

1 回答

?
MM们

TA贡献1886条经验 获得超2个赞

这个问题及其潜在的问题引起了我的兴趣,所以我想了解更多关于整个问题的信息。我自己创建了一个测试场景。


优化

首先对刀片模板的代码进行一些优化:


// index.blade.php

<select>

    @include('categories.options', ['categories' => $categories, 'level' => 0])

</select>


// options.blade.php

@foreach ($categories as $category)

    <option value="{{ $category->id }}">{{ str_repeat("--", $level) }} {{ $category->name }}</option>

    @include('categories.options', ['categories' => $category->categories, 'level' => $level+1])

@endforeach

然后,我生成了一个包含大约 5000 个嵌套类别、8 层深度的数据库,以测试加载时间。我的假设是,如果您向类别模型添加急切加载,则可以优化加载时间:


// Category.php


// this eager loads ALL nested relations using levels + 1 queries

protected $with = ['categories'];

// this is not needed and doesn't make any difference

protected $with = ['categories.categories'];

结果如下:


                       Time  Queries  Memory

--------------------------------------------------

  No eager loading   12,81 s     5101   112MB

with eager loading    1,49 s        9    31MB

 2 x eager loading    1,54 s        9    31MB


Caching               0,08 s        0     4MB


(stats recorded mainly with debugbar)

正如您所看到的,急切加载绝对是有意义的。在模型中放入一个单一的关系也足够了$with = ['categories'],laravel 将立即加载所有嵌套的关系 - 整洁!


缓存

因此,为了让网站尽可能快地加载(我能想到的),唯一真正的解决方案是缓存。


// create new job in console

php artisan make:job RenderCategoryView


// RenderCategoryView.php

public function handle() {

    // get all top level categories

    $categories = \App\Category::where('category_id', null)->get();

    $html = \View::make('categories.options', ['categories' => $categories, 'level' => 0])->render();

    file_put_contents(resource_path('views/categories/options_cache.blade.php'), $html);

    return true;

}

@include现在您可以像这样替换刀片模板:


// index.blade.php

@include('categories.options_cache')

要测试 options_cache 文件的生成,您可以执行以下操作:


php artisan tinker

\App\Jobs\RenderCategoryView::dispatchNow();

我还在返回索引视图之前删除了现在不必要的数据库查询,新的加载时间为83 ms。这并不奇怪,因为现在所有内容都被缓存了。


要在创建、编辑或删除类别后自动生成新的视图缓存,您应该将其包含在相应的控制器中:


\App\Jobs\RenderCategoryView::dispatch();


查看完整回答
反对 回复 2023-08-19
  • 1 回答
  • 0 关注
  • 95 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信