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技术栈
本帖由系统于 3个月前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 15

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

/**
     * 一维数据数组生成数据树
     * @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;
    }
4个月前 评论
miaotiao 4个月前
PHPer技术栈 (楼主) 4个月前
miaotiao 4个月前
NiZerin 4个月前
leifuping 3个月前
aa24615 2个月前
TonyF

:+1:

4个月前 评论

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

/**
     * 一维数据数组生成数据树
     * @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;
    }
4个月前 评论
miaotiao 4个月前
PHPer技术栈 (楼主) 4个月前
miaotiao 4个月前
NiZerin 4个月前
leifuping 3个月前
aa24615 2个月前

这样会多次查库吧

4个月前 评论
PHPer技术栈 (楼主) 4个月前

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

4个月前 评论
WmKong

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

4个月前 评论
PHPer技术栈 (楼主) 4个月前
WmKong (作者) 4个月前

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

4个月前 评论
Code_Er

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

4个月前 评论
PHPer技术栈 (楼主) 4个月前
Code_Er (作者) 4个月前

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

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

3个月前 评论

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

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

3个月前 评论
dongzhiyu 3个月前

通过保存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();
        });
    }

3个月前 评论

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

3个月前 评论

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

2个月前 评论

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