Laravel Eloquent: 使用 hasMany 来处理『无限级分类』

在网上商城上,我们经常可以看到多级分类、子分类、甚至无限级分类。本文将向你展示如何优雅的通过 Laravel Eloquent 将其实现。

我们会创建一个微型项目来展示儿童商店的分类,总共有 5 级,如下:


数据库迁移

简单的数据表结构:

Schema::create('categories', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->string('name');
    $table->unsignedBigInteger('category_id')->nullable();
    $table->foreign('category_id')->references('id')->on('categories');
    $table->timestamps();
});

只有一个 name 字段, 关联到其自身。所以,大部分父级分类 category_id = NULL,每一个子分类都有一个 parent_id

数据表数据如下:


Eloquent 模型和关联关系

首先,在 app/Category.php 创建一个简单的 hasMany() 方法, 分类可能拥有其子分类:

class Category extends Model
{

    public function categories()
    {
        return $this->hasMany(Category::class);
    }

}

好戏开场 本文最妙 “计策”。你知道可以向这样描述 递归 关系吗? 如下:

public function childrenCategories()
{
    return $this->hasMany(Category::class)->with('categories');
}

因此,如果调用 Category::with(‘categories’),将得到下级“子分类”,但是通过 Category::with(‘childrenCategories’) 将能帮你实现无限级。


路由和控制器方法

现在,让我们尝试显示所有类别和子类别,如上例所示。

routes/web.php,我们添加以下内容:

Route::get('categories', 'CategoryController@index');

app/Http/CategoryController.php 如下所示:

public function index()
{
    $categories = Category::whereNull('category_id')
        ->with('childrenCategories')
        ->get();
    return view('categories', compact('categories'));
}

我们仅加载父类别,将子类别作为关系。简单吧?


视图和递归子视图

最后,渲染到页面。 在 resources/views/categories.blade.php 文件:

<ul>
    @foreach ($categories as $category)
        <li>{{ $category->name }}</li>
        <ul>
        @foreach ($category->childrenCategories as $childCategory)
            @include('child_category', ['child_category' => $childCategory])
        @endforeach
        </ul>
    @endforeach
</ul>

我们先遍历了最顶级的父类别,然后遍历出父类的子类别,然后使用 @include 加载子类别的子类别……

最好的部分是 resources/views/admin/child_category.blade.php 将使用递归加载自身。看代码:

<li>{{ $child_category->name }}</li>
@if ($child_category->categories)
    <ul>
        @foreach ($child_category->categories as $childCategory)
            @include('child_category', ['child_category' => $childCategory])
        @endforeach
    </ul>
@endif

child_category.blade.php 内部,我们包含了 @include(‘child_category’),因此只要当前子类别中有类别,模板就会递归地加载子类别。


就是这样!我们拥有无限级别的子类别-无论是在数据库还是关联关系或是视图中

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://laraveldaily.com/eloquent-recurs...

译文地址:https://learnku.com/laravel/t/38977

本帖已被设为精华帖!
本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 8

我都是这样实现的

file

4年前 评论

我都是这样实现的

file

4年前 评论

原文的一个地方写错了,译文也错了 with('childrenCategories'),而非with('categories');
public function childrenCategories()
{
return $this->hasMany(Category::class)->with('childrenCategories');
}

4年前 评论
Itliusha (作者) 4年前
丶缪尔 3年前

考虑过这样写会有多少次sql查询吗?

4年前 评论

这个会出现N+1的问题吧?

4年前 评论

so elegantly with laravel eloquent!

4年前 评论
//Category模型
public function children() {
    return $this->hasMany(get_class($this), 'pid' );
}

public function allChildren() {
    return $this->children()->with( 'allChildren' );
}
//CategorieController
$cat = Category::find(6);
$count = $cat->allChildren()->count();
//这样只能获取第一级的,所有的条数怎么获取呢?

同一个模型,这样的无限极分类的写法,如何获取其中一条数据下的数据合计

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!