猿问

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

我有无限的类别、子类别、子子类别等等。


结构:


main

--main1

-----main2 ( child of main1 )

-----main3 ( child of main1 )

--main4

test

我的代码:


在Category.php model


public 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 将会增加,这对性能不利!


请问有什么建议吗?


缥缈止盈
浏览 96回答 1
1回答

MM们

这个问题及其潜在的问题引起了我的兴趣,所以我想了解更多关于整个问题的信息。我自己创建了一个测试场景。优化首先对刀片模板的代码进行一些优化:// 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 queriesprotected $with = ['categories'];// this is not needed and doesn't make any differenceprotected $with = ['categories.categories'];结果如下:                       Time  Queries  Memory--------------------------------------------------  No eager loading   12,81 s     5101   112MBwith eager loading    1,49 s        9    31MB 2 x eager loading    1,54 s        9    31MBCaching               0,08 s        0     4MB(stats recorded mainly with debugbar)正如您所看到的,急切加载绝对是有意义的。在模型中放入一个单一的关系也足够了$with = ['categories'],laravel 将立即加载所有嵌套的关系 - 整洁!缓存因此,为了让网站尽可能快地加载(我能想到的),唯一真正的解决方案是缓存。// create new job in consolephp artisan make:job RenderCategoryView// RenderCategoryView.phppublic 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();
随时随地看视频慕课网APP
我要回答