Laravel 框架实现无限极分类

最近开发商品功能,在尝试递归和引用方式后,蓦然回首,突然发现laravel框架有更简单高效的实现方式,无限极分类最佳实践,open code与大家共享!感兴趣的Mark一下,谢谢~

表结构如下:

CREATE TABLE `goods_category` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(500) DEFAULT '' COMMENT '分类名称',
  `pid` int(5) unsigned DEFAULT '0' COMMENT '父级id',
  `level` tinyint(3) unsigned DEFAULT '1' COMMENT '分类等级',
  `status` tinyint(3) unsigned DEFAULT '0' COMMENT '分类状态:0-禁用,1-正常',
  `created_at` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `updated_at` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `status` (`status`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8mb4 COMMENT='商品分类表';

数据存储格式:

Laravel框架无限极分类简单实现

业务代码:

    // 模型文件
    public function children() {
        return $this->hasMany(get_class($this), 'pid' ,'id');
    }

    public function allChildren() {
        return $this->children()->with( 'allChildren' );
    }
// 控制器
$list = GoodsCategory::with('allChildren')->first();
dd($list);

处理后数据:

Laravel框架无限极分类简单实现

至此,laravel框架无限极分类实现完毕,相比递归和引用实现无限极分类的两种方式,是不是简单高效很多呢,关于更多laravel特性,欢迎评论区留言探讨。
参考文档:laravel5.4实现无限级分类

本作品采用《CC 协议》,转载必须注明作者和本文链接
PHPer技术栈
本帖由系统于 1年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 18

试试这两个方法,不用多次查询数据库

/**
     * 一维数据数组生成数据树
     * @param array $list 数据列表
     * @param string $id 父ID Key
     * @param string $pid ID Key
     * @param string $son 定义子数据Key
     * @return Collection
     */
    public static function arr2tree($list, $id = 'id', $pid = 'pid', $son = 'sub')
    {
        list($tree, $map) = [[], []];
        foreach ($list as $item) {
            $map[$item[$id]] = $item;
        }

        foreach ($list as $item) {
            if (isset($item[$pid]) && isset($map[$item[$pid]])) {
                $map[$item[$pid]][$son][] = &$map[$item[$id]];
            } else {
                $tree[] = &$map[$item[$id]];
            }
        }
        unset($map);
        return $tree;
    }

    /**
     * 一维数据数组生成数据树
     * @param array $list 数据列表
     * @param string $id ID Key
     * @param string $pid 父ID Key
     * @param string $path
     * @param string $ppath
     * @return array
     */
    public static function arr2table(array $list, $id = 'id', $pid = 'pid', $path = 'path', $ppath = '')
    {
        $tree = [];
        foreach (self::arr2tree($list, $id, $pid) as $attr) {
            $attr[$path] = "{$ppath}-{$attr[$id]}";
            $attr['sub'] = isset($attr['sub']) ? $attr['sub'] : [];
            $attr['spt'] = substr_count($ppath, '-');
            $attr['spl'] = str_repeat(" ├ ", $attr['spt']);
            $sub         = $attr['sub'];
            unset($attr['sub']);
            $tree[] = $attr;
            if (!empty($sub)) {
                $tree = array_merge($tree, self::arr2table($sub, $id, $pid, $path, $attr[$path]));
            }
        }
        return $tree;
    }
1年前 评论
miaotiao 1年前
PHPer技术栈 (楼主) 1年前
miaotiao 1年前
NiZerin 1年前
leifuping 1年前
aa24615 1年前
wangu7 11个月前

试试这两个方法,不用多次查询数据库

/**
     * 一维数据数组生成数据树
     * @param array $list 数据列表
     * @param string $id 父ID Key
     * @param string $pid ID Key
     * @param string $son 定义子数据Key
     * @return Collection
     */
    public static function arr2tree($list, $id = 'id', $pid = 'pid', $son = 'sub')
    {
        list($tree, $map) = [[], []];
        foreach ($list as $item) {
            $map[$item[$id]] = $item;
        }

        foreach ($list as $item) {
            if (isset($item[$pid]) && isset($map[$item[$pid]])) {
                $map[$item[$pid]][$son][] = &$map[$item[$id]];
            } else {
                $tree[] = &$map[$item[$id]];
            }
        }
        unset($map);
        return $tree;
    }

    /**
     * 一维数据数组生成数据树
     * @param array $list 数据列表
     * @param string $id ID Key
     * @param string $pid 父ID Key
     * @param string $path
     * @param string $ppath
     * @return array
     */
    public static function arr2table(array $list, $id = 'id', $pid = 'pid', $path = 'path', $ppath = '')
    {
        $tree = [];
        foreach (self::arr2tree($list, $id, $pid) as $attr) {
            $attr[$path] = "{$ppath}-{$attr[$id]}";
            $attr['sub'] = isset($attr['sub']) ? $attr['sub'] : [];
            $attr['spt'] = substr_count($ppath, '-');
            $attr['spl'] = str_repeat(" ├ ", $attr['spt']);
            $sub         = $attr['sub'];
            unset($attr['sub']);
            $tree[] = $attr;
            if (!empty($sub)) {
                $tree = array_merge($tree, self::arr2table($sub, $id, $pid, $path, $attr[$path]));
            }
        }
        return $tree;
    }
1年前 评论
miaotiao 1年前
PHPer技术栈 (楼主) 1年前
miaotiao 1年前
NiZerin 1年前
leifuping 1年前
aa24615 1年前
wangu7 11个月前

这样会多次查库吧

1年前 评论
PHPer技术栈 (楼主) 1年前

这样的话子查询了,建议还是用php处理比较好

1年前 评论

试过这个方法,在有 3000 条数据的情况下,处理时间太长,不可取。

1年前 评论
PHPer技术栈 (楼主) 1年前
WmKong (作者) 1年前

这样设计无限分类都是唠嗑有问题的,使用二叉树来设计,效果高,查询快,左右节点 my.oschina.net/corwien/blog/687664

1年前 评论
Code_Er

我感觉性能上用文件静态化来解决

1年前 评论
PHPer技术栈 (楼主) 1年前
Code_Er (作者) 1年前

用 ClosureTable 可以缩减到两次查询获取到所有子集/父级数据

www.yuque.com/docs/share/9852f2aa-...

1年前 评论

zhuanlan.zhihu.com/p/33279392 laravel-nestedset:多级无限分类正确姿势

用过这个也挺好用的,也是基于左右两个节点,效率高速度快,提供丰富的API查询和插入。

1年前 评论
dongzhiyu 1年前
liuyong 7个月前

通过保存path去完成高性能树的建立,具体可参考:lumina.xbhub.com/docs/zh/guide/trai...

<?php

namespace Modules\Core\Traits;

use Exception;
use Illuminate\Support\Facades\DB;
use Modules\Core\Utils\Tree;

trait HasPathTree
{
    /**
     * Create the event listeners for the saving and saved events
     * This lets us save revisions whenever a save is made, no matter the
     * http method.
     *
     */
    public static function bootHasPathTree()
    {
        static::created(function($model){
            $tablename = $model->getTable();
            $model->level = 1;
            $model->path = '/0/'.$model->id.'/';
            if($model->parentid > 0){
                $parent = DB::table($tablename)->find($model->parentid);
                if($parent){
                    $model->path = $parent->path.$model->id.'/';
                    $model->level = $parent->level + 1;
                }
            }
            $model->save();
        });

        static::updated(function($model) {
            $tablename = $model->getTable();
            $original = $model->getOriginal();

            if(isset($original['parentid']) && $model->parentid != $original['parentid']) {
                // 树结构发生变更
                $parent = DB::table($tablename)->find($model->parentid);
                $old_parent = DB::table($tablename)->find($original['parentid']);

                if($model->parentid==0 || !$old_parent) {
                    $model->path = '/0/';
                    $model->level = 1;
                }else{
                    $level_delta = $old_parent->level - $parent->level;
                    $categories = DB::table($tablename)->where('path', 'like', '%/'.$model->id.'/%')->get();
                    foreach ($categories as $_category){
                        DB::table($tablename)->where('id', $_category->id)->update([
                            'path' =>  str_replace($old_parent->path, $parent->path, $_category->path),
                            'level' => $_category->level - $level_delta
                        ]);
                    }
                }
            }
        });

        static::deleted(function ($model) {
            $tablename = $model->getTable();
            DB::table($tablename)->where('path', 'like', '%/'.$model->id.'/%')->delete();
        });
    }

1年前 评论

用sql做递归第二天就被开了,在实现业务的同时更要注重性能

1年前 评论

github.com/ydtg1993/dendrogram 我这个是用mysql自定义函数查询 一条sql搞定不重复查库 支持adjacency list和nested sets两种数据格式

1年前 评论
public function children() {
    return $this->hasMany(get_class($this), 'pid' ,'id')->with('children');
}

不需要额外定义一个 allchildren

8个月前 评论

www.phpbloger.com/article/50.html#...
这篇文章博主分析了四种方法优缺点,根据自己业务需求选择。

7个月前 评论

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