首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >laravel生成无限级分类

laravel生成无限级分类

作者头像
章鱼喵
发布于 2019-05-06 09:38:30
发布于 2019-05-06 09:38:30
3.2K0
举报
文章被收录于专栏:codingcoding

无限级分类是很常见的功能,算法的好坏对于获取分类树的性能起到决定性的作用。尤其当分类数据和层级多时,一个糟糕的算法将使服务器不堪重负

以下用laravel实现无限级分类功能,包括:

  • 数据表设计
  • 填充模拟数据
  • 生成分类树
  • 分类树的后台维护

数据表设计

字段名

描述

id

主键id

name

类目名称

parent_id

父类目 ID

is_directory

是否拥有子类目

level

当前类目层级

path

该类目所有父类目 id

为什么要用level与path

无限级分类中,我们经常需要获取一个分类的所有祖先类目或者后代类目,以及判断两个类目是否存在层级关系。倘若都使用递归查询,会产生极多的sql查询。level和path这两个冗余字段便应运而生

以下面的模拟数据为例:

代码语言:javascript
AI代码解释
复制
[
    [
        "id" => 1,
        "name" => "手机配件",
        "parent_id" => null,
        "level" => 0,
        "path" => "-"
    ],
    [
        "id" => 2,
        "name" => "耳机",
        "parent_id" => 1,
        "level" => 1,
        "path" => "-1-"
    ],
    [
        "id" => 3,
        "name" => "蓝牙耳机",
        "parent_id" => 2,
        "level" => 2,
        "path" => "-1-2-"
    ],
    [
        "id" => 4,
        "name" => "移动电源",
        "parent_id" => 1,
        "level" => 1,
        "path" => "-1-"
    ],
];

目录树

  • 场景1:查询蓝牙耳机的所有祖先类目

根据path字段的值获取其祖先id为[1, 2],用 Category::whereIn('id', [1, 2])->orderBy('level')->get() 即可获取结果

  • 场景2:查询手机配件的所有后代类目

id字段追加到path字段,得到-1-, 用Category::where('path', 'like', '-1-%')->get() 即可获取结果

  • 场景3:判断移动电源蓝牙耳机是否有层级关系
代码语言:javascript
AI代码解释
复制
$highLevelPath = '-1-2'; // 蓝牙耳机的path
$lowLevelPath = '-1-' . '4-'; // 移动电源的path值拼接id值
if (strpos($highLevelPath, $lowLevelPath) === 0) { // 判断蓝牙耳机的path值是否以移动电源的path值为开头
    echo '存在层级关系';
} else {
    echo '并无层级关系';
}

创建数据表

代码语言:javascript
AI代码解释
复制
$ php artisan make:model Models/Category -m

编写迁移文件:database/migrations/2019_04_21_140243_create_categories_table.php

代码语言:javascript
AI代码解释
复制
    public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name')->comment('分类名称');
            $table->unsignedInteger('parent_id')->nullable()->comment('父类id');
            $table->foreign('parent_id')->references('id')->on('categories')->onDelete('cascade');
            $table->boolean('is_directory')->comment('是否有子类目');
            $table->unsignedInteger('level')->comment('当前类目层级');
            $table->string('path')->comment('该分类的所有父类id, 用 - 连接');
            $table->timestamps();
        });
    }

执行迁移创建数据表

代码语言:javascript
AI代码解释
复制
$ php artisan migrate

填充模拟数据

调整模型类app/Models/Category.php代码:

代码语言:javascript
AI代码解释
复制
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    protected $fillable = ['name', 'is_directory', 'level', 'path'];
    protected $casts = [
        'is_directory' => 'boolean',
    ];
    protected static function boot()
    {
        parent::boot();
        // 当创建Category时,自动初始化 path 和 level
        static::creating(function (Category $category) {
            if (is_null($category->parent_id)) { // 创建的是根目录
                $category->level = 0; // 将层级设为0
                $category->path = '-'; // 将 path 设为 -
            } else { // 创建的并非根目录
                $category->level = $category->parent->level + 1; // 将层级设为父类层级+1
                $category->path = $category->parent->path . $category->parent_id . '-'; // 将path值设为父类path+父类id
            }
        });
    }

    public function parent()
    {
        return $this->belongsTo(Category::class);
    }

    public function children()
    {
        return $this->hasMany(Category::class, 'parent_id');
    }

    /**
     * 获取所有祖先分类id
     * @date 2019-04-21
     */
    public function getPathIdsAttribute()
    {
        $path = trim($this->path, '-'); // 过滤两端的 -
        $path = explode('-', $path); // 以 - 为分隔符切割为数组
        $path = array_filter($path); // 过滤空值元素
        return $path;
    }

    /**
     * 获取所有祖先分类且按层级正序排列
     * @date 2019-04-21
     */
    public function getAncestorsAttribute()
    {
        return Category::query()
            ->whereIn('id', $this->path_ids) // 调用 getPathIdsAttribute 获取祖先类目id
            ->orderBy('level') // 按层级排列
            ->get();
    }

    /**
     * 获取所有祖先类目名称以及当前类目的名称
     * @date 2019-04-21
     */
    public function getFullNameAttribute()
    {
        return $this->ancestors // 调用 getAncestorsAttribute 获取祖先类目
            ->pluck('name') // 将所有祖先类目的 name 字段作为一个数组
            ->push($this->name) // 追加当前类目的name字段到数组末尾
            ->implode(' - '); // 用 - 符号将数组的值组装成一个字符串
    }

}

创建填充文件:

代码语言:javascript
AI代码解释
复制
$ php artisan make:seeder CategoriesSeeder

调整database/seeds/CategoriesSeeder.php代码:

代码语言:javascript
AI代码解释
复制
<?php

use Illuminate\Database\Seeder;
use App\Models\Category;

class CategoriesSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $categories = [
            [
                'name'     => '手机配件',
                'children' => [
                    ['name' => '手机壳'],
                    ['name' => '贴膜'],
                    ['name' => '存储卡'],
                    ['name' => '数据线'],
                    ['name' => '充电器'],
                    [
                        'name'     => '耳机',
                        'children' => [
                            ['name' => '有线耳机'],
                            ['name' => '蓝牙耳机'],
                        ],
                    ],
                ],
            ],
            [
                'name'     => '电脑配件',
                'children' => [
                    ['name' => '显示器'],
                    ['name' => '显卡'],
                    ['name' => '内存'],
                    ['name' => 'CPU'],
                    ['name' => '主板'],
                    ['name' => '硬盘'],
                ],
            ],
            [
                'name'     => '电脑整机',
                'children' => [
                    ['name' => '笔记本'],
                    ['name' => '台式机'],
                    ['name' => '平板电脑'],
                    ['name' => '一体机'],
                    ['name' => '服务器'],
                    ['name' => '工作站'],
                ],
            ],
            [
                'name'     => '手机通讯',
                'children' => [
                    ['name' => '智能机'],
                    ['name' => '老人机'],
                    ['name' => '对讲机'],
                ],
            ],
        ];
        foreach ($categories as $data) {
            $this->createCategory($data);
        }
    }

    protected function createCategory($data, $parent = null)
    {
        $category = new Category(['name' => $data['name']]);
        $category->is_directory = isset($data['children']);
        if (!is_null($parent)) { // 如果存在parent,代表有父类目
            $category->parent()->associate($parent);
        }
        $category->save(); // 数据入库
        if (isset($data['children']) && is_array($data['children'])) {
            foreach ($data['children'] as $child) {
                // 递归调用 createCategory 方法
                $this->createCategory($child, $category); // $category 为刚创建的类目,作为子类目的父级类目参数
            }
        }
    }

}

执行数据填充:

代码语言:javascript
AI代码解释
复制
$ php artisan db:seed --class=CategoriesSeeder

查看结果:

数据填充结果

生成分类树

分类树是一个通用的功能,适合将其封装为一个服务,创建CategoryService

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019.04.21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
laravel-nestedset:多级无限分类正确姿势
laravel-nestedset是一个关系型数据库遍历树的larvel4-5的插件包
猿哥
2019/07/25
4.3K0
PHP 无限级分类数据库设计及实现
♖背景 最近复习算法,在此对无限级分类的实现方法稍作整理,当然也是参考了道友的经验,目测适合实际的项目应用,当然,也有不少公司的笔试题还会涉及到呢,有何问题,欢迎各位道友指摘 … 操作环境:Win10 使用语言:PHP 使用框架:ThinkPHP 3.2.3 ♘前期准备 ①. 首先,实现无限级分类的方式有: 以父ID设计,运用递归实现的方式 以全路径实现的无限分类方式 ②. 其次,数据表设计思路 对应于上述的两种实现方式,那么在数据表设计时也可以有两种方式,参考所给的数据表截图 如
泥豆芽儿 MT
2018/10/15
2.4K0
吊打面试官03:如何设计一个内存文件系统(多级目录)
树状结构或层次结构的数据在企业应用里非常常见, 例如公司的组织架构、文档库的目录结构、 仓库的库位组织以及物件的分类等等
早起的鸟儿有虫吃
2025/08/02
2040
吊打面试官03:如何设计一个内存文件系统(多级目录)
tp6中无限极分类里面的获取多级分类数据
就是很多很多的数据,按照无限极分类结构排序。每一个数组的所有数据都是顶级分类及其其下数据
贵哥的编程之路
2022/06/05
1.2K0
tp6中无限极分类里面的获取多级分类数据
Laravel5.2之Seeder填充数据小技巧
说明:本文主要聊一聊Laravel测试数据填充器Seeder的小技巧,同时介绍下Laravel开发插件三件套,这三个插件挺好用哦。同时,会将开发过程中的一些截图和代码黏上去,提高阅读效率。
botkenni
2022/01/10
4.4K0
Laravel5.2之Seeder填充数据小技巧
PHP实现无限级分类 -- Path标识
在实际项目中经常要用到无限级分类,如多级分类、导航表等。PHP 实现无限级分类通常有两种实现方式,一种是利用path字段(pid+id)标识当前层级;另一种是利用递归循环pid的方式。此处介绍前种方式。
程序小工
2018/09/12
2.5K0
Laravel 框架实现无限极分类
近开发商品功能,在尝试递归和引用方式后,蓦然回首,突然发现 laravel 框架有更简单高效的实现方式,无限极分类最佳实践,open code 与大家共享!感兴趣的 Mark 一下,谢谢~
ITer.996
2020/07/22
1.9K0
laravel-admin 自定义页面需要注意的点
接下来我们修改迁移文件,迁移文件在database/migrations/xxxxxx.create)categories_table.php
友儿
2022/09/11
6340
wordpress根据父分类ID获取子分类的描述、链接和文章数
在WordPress中,要根据父分类ID获取子分类的名称、别名、描述、链接和文章数,可以通过get_categories()函数实现。该函数能查询分类信息,结合参数筛选出指定父分类的子分类,再提取所需字段。
WordPress爱好者
2025/07/28
1990
wordpress根据父分类ID获取子分类的描述、链接和文章数
tp6删除某个分类,考虑种种情况
第一种情况:这个要删除的分类其下有子分类,要删除先删除子分类先. 举个例子:顶级分类服装 其下有衣服 其下下还有耐克 如果要删除顶级分类服装咋整。 第一步:判断它是否为空。如果为空,代表什么都没做。不理会。 第一步:获取要删除的分类的id。 第三步:先找出无限极分类所有的分类,然后通过算法找到这个服装分类其下有没有子分类。有的话,返回服装所有的子分类回来。 第四步:转换成数组格式遍历下。遍历的作用就是删除服装其下的所有子类。 第五步:遍历删除完服装其下所有子类,在删除服装这个分类。
贵哥的编程之路
2022/05/30
3690
tp6删除某个分类,考虑种种情况
PHP无限级分类函数封装与应用
下面的讲解我用的是ThinkPHP框架,与PHP用法是一样的。 Step 1、数据表中主要字段是id、parent_id Step 2、SQL语句: SELECT * FROM `t_class` W
Sindsun
2018/04/28
1.9K0
PHP无限级分类函数封装与应用
对无限级分类数据进行重新排序(非树形结构)
无限级分类查询有很多方式。本文记录的方式是先将所有数据查出来,再使用递归对数据进行排序,并附加层级字段(level)。此方式仅仅对无限级的数据进行排序,并没有将子级内容放入父级。 1. 先看效果图
很酷的站长
2023/02/17
2K0
对无限级分类数据进行重新排序(非树形结构)
WordPress调用分类目录 及输出当前分类下的二级目录 和分类文章数量显示
效果如下:一级二级分类目录:图片分类计数:图片教程代码第一步先在主题的function.php丢进去下边的代码:function get_category_root_id($cat){$this_category = get_category($cat); // 取得当前分类while($this_category->category_parent) // 若当前分类有上级分类时,循环{$this_category = get_category($this_category->category_parent
AlexTao
2020/09/03
3.2K0
写Laravel测试代码(1)
写laravel程序时,除了写生产代码,还需要写测试代码。其中,写数据库测试比较麻烦,因为需要针对每一个test case需要建立好数据集,该次test case污染的数据表还需要恢复现场,避免影响下一个test case运行,同时还得保证性能问题,否则随着程序不断膨胀,测试数量也越多,那每一次测试运行需要花费大量时间。
botkenni
2019/09/02
8910
laravel-admin自动生成模块,及相关基础配置方法
$ php artisan make:model Brand -m  //创建模型并生成迁移文件
用户8832503
2021/07/13
1.5K0
商城项目-实现商品分类查询
商城的核心自然是商品,而商品多了以后,肯定要进行分类,并且不同的商品会有不同的品牌信息,其关系如图所示:
cwl_java
2020/02/11
2.1K0
推荐阅读
相关推荐
laravel-nestedset:多级无限分类正确姿势
更多 >
交个朋友
加入云开发企业交流群
企业云开发实战交流 探讨技术架构优化
加入[数据] 腾讯云技术交流站
获取数据实战干货 共享技术经验心得
加入数据技术工作实战群
获取实战干货 交流技术经验
换一批
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
首页
学习
活动
专区
圈层
工具
MCP广场
首页
学习
活动
专区
圈层
工具
MCP广场