方法一:使用迭代方式(推荐)

1、先修改模型

在原有方法的基础上,先添加当前分类的名称,然后再递归或迭代查找上级分类。

<?php
namespace app\model;use think\Model;class LaCate extends Model
{protected $table = 'la_cate';/*** 获取当前分类及其所有上级分类名称,用 "-" 分隔** @param int $cateId 当前分类的ID* @return string 分类名称,用 "-" 分隔(包含当前分类)*/public function getParentNames($cateId){$names = [];$currentId = $cateId;// 1. 先查询当前分类$currentCate = self::find($currentId);if (!$currentCate) {// 如果找不到当前分类,返回空字符串return '';}// 2. 先把当前分类名称加入数组$names[] = $currentCate->name;// 3. 获取父级 ID$parentId = $currentCate->pid;// 4. 开始迭代查找上级分类while ($parentId) {// 查询父级分类$parentCate = self::find($parentId);if (!$parentCate) {// 如果找不到父级分类,退出循环break;}// 将父级分类名称添加到数组$names[] = $parentCate->name;// 更新 parentId 为父级分类的 pid$parentId = $parentCate->pid;}// 5. 使用 "-" 拼接所有分类名称(顺序已经是当前分类 + 上级分类)return implode('-', $names);}
}

关键点:

  • 先查询当前分类,并把它的名称加入 $names 数组。
  • 再迭代查找上级分类,把父级分类名称依次加入数组。
  • 最后用 implode(‘-’,$names) 拼接,顺序已经是 当前分类 - 父级分类 - 祖父级分类…。

方法二:使用递归方式

如果你更喜欢递归,也可以这样实现:

方法二:使用递归方式
<?php
namespace app\model;use think\Model;class LaCate extends Model
{protected $table = 'la_cate';/*** 获取当前分类及其所有上级分类名称,用 "-" 分隔** @param int $cateId 当前分类的ID* @return string 分类名称,用 "-" 分隔(包含当前分类)*/public function getParentNames($cateId){$names = [];$this->getCategoryNamesRecursive($cateId, $names);// 递归是先子后父,但我们在递归前已经添加了当前分类,所以顺序正确return implode('-', $names);}/*** 递归获取上级分类名称(不包含当前分类)** @param int $cateId 当前分类的ID* @param array &$names 用于存储名称的数组(引用传递)*/protected function getCategoryNamesRecursive($cateId, &$names){// 获取当前分类$currentCate = self::find($cateId);if (!$currentCate) {// 如果找不到当前分类,返回return;}$parentId = $currentCate->pid;if ($parentId == 0 || $parentId == null) {// 已经到达根分类,返回return;}// 查询父级分类$parentCate = self::find($parentId);if ($parentCate) {// 将父级分类名称添加到数组$names[] = $parentCate->name;// 递归查找父级的上级$this->getCategoryNamesRecursive($parentId, $names);}}
}

但这种方法需要调整,因为递归是先子后父,而我们希望先当前分类再上级分类。
所以更推荐迭代方式,因为它更直观且性能更好。

2、控制器调用

<?php
namespace app\controller;use app\model\LaCate;
use think\facade\Request;class LaCateController
{/*** 获取当前分类及其所有上级分类名称** @return \think\Response*/public function getParentNames(){// 获取请求参数中的 cateId$cateId = Request::param('cateId/d', 0);if ($cateId <= 0) {return json(['error' => '无效的分类ID'], 400);}// 实例化 LaCate 模型$laCate = new LaCate();// 调用方法获取分类名称(包含当前分类)$categoryNames = $laCate->getParentNames($cateId);// 返回结果return json(['category_names' => $categoryNames]);}
}

3、前端调用实现

假设 la_cate 表数据如下:

id

pid

name

1

0

电子产品

2

1

手机

3

2

智能手机

4

1

电脑

5

4

笔记本

  • 测试 1:id=5(笔记本)
  • 当前分类:笔记本
  • 父级分类:电脑 → 电子产品
  • 返回结果:笔记本-电脑-电子产品

访问 URL:

http://yourdomain.com/la-cate/parent-names?cateId=5

返回 JSON:

json
{"category_names": "笔记本-电脑-电子产品"
}

测试 2:id=3(智能手机)

  • 当前分类:智能手机
  • 父级分类:手机 → 电子产品
  • 返回结果:智能手机-手机-电子产品

访问 URL:

http://yourdomain.com/la-cate/parent-names?cateId=3

返回 JSON:

json
{"category_names": "智能手机-手机-电子产品"
}

tips
如果希望包含当前分类,只需在查询上级分类之前,先获取当前分类的名称,再迭代查找上级分类。
推荐使用迭代方式,因为它更高效且避免了递归可能导致的栈溢出问题。
最终返回的字符串格式:当前分类 - 父级分类 - 祖父级分类…。

在处理大量数据时,PHP的迭代和递归哪种更高效?

在处理大量数据(尤其是无限分类或层级较深的数据结构)时,PHP 的迭代(Iteration)通常比递归(Recursion)更高效,主要原因如下:

  1. 性能对比

thinkphp8无限分类实现方法大比拼,哪种方法更高效?_#php

对比项迭代(Iteration)递归(Recursion)内存占用较低(仅维护循环变量)较高(每次调用都会占用栈帧)执行速度更快(无函数调用开销)较慢(频繁函数调用)最大深度限制无(仅受内存限制)有(PHP 默认递归深度约 100-1000 层)代码复杂度稍复杂(需手动管理状态)更简单(逻辑直观)

  1. 为什么迭代更高效?
    (1) 无函数调用开销

递归:每次调用自身都会产生新的栈帧(Stack Frame),存储局部变量、返回地址等,消耗更多内存和 CPU。
迭代:仅用 while/for 循环,无额外函数调用,性能更高。

(2) 无递归深度限制

PHP 默认递归深度限制(xdebug.max_nesting_level 或 max_execution_time 可能触发):

默认约 100-1000 层(取决于 PHP 版本和配置)。
超过限制会抛出 Fatal error: Maximum function nesting level reached。

迭代:仅受内存限制,可处理更深的层级(如百万级数据)。

(3) 更适合大数据量

递归在数据量大时可能导致:

栈溢出(Stack Overflow)(PHP 进程崩溃)。
性能急剧下降(函数调用开销累积)。

迭代则稳定可控,适合生产环境。

  1. 实际测试对比
  2. 假设查询一个 1000 层 的无限分类结构:
  3. thinkphp8无限分类实现方法大比拼,哪种方法更高效?_#大数据_02


  4. 方式执行时间(示例)内存占用(示例)递归500ms(可能超时)50MB(栈帧累积)迭代50ms5MB
  5. (具体数值取决于服务器配置,但迭代通常快 10 倍以上)
  6. 如何选择?

thinkphp8无限分类实现方法大比拼,哪种方法更高效?_#大数据_03


场景推荐方式层级较浅(<100 层)递归(代码更简洁)层级深或数据量大迭代(性能更稳定)需要严格避免栈溢出迭代(100% 安全)

  1. 优化递归的方法(如必须用递归)
    如果因业务逻辑必须用递归,可通过以下方式优化:
    (1) 尾递归优化(PHP 不支持,但可模拟)

PHP 默认不支持尾递归优化(TCO),但可通过改写逻辑减少栈帧占用。
例如:用循环+栈数组模拟递归(类似迭代)。

(2) 增加递归深度限制检查
if ($depth > 1000) {
throw new Exception(“递归深度过大!”);
}

(3) 改用迭代

终极解决方案:直接改用 while 循环,彻底避免递归问题。

  1. 最终结论

优先使用迭代:处理无限分类、树形结构等大数据量场景时,迭代是更安全、高效的选择。
递归仅用于简单逻辑:如目录遍历(层级已知且较浅)等小规模数据。

附:迭代 vs 递归代码示例(无限分类查询)
迭代方式(推荐)

public function getParentNames($cateId) {$names = [];$currentId = $cateId;// 1. 先查当前分类$currentCate = self::find($currentId);if (!$currentCate) return '';$names[] = $currentCate->name;// 2. 迭代查父级while ($parentId = $currentCate->pid) {$parentCate = self::find($parentId);if (!$parentCate) break;$names[] = $parentCate->name;$currentCate = $parentCate;}return implode('-', $names);
}

递归方式(不推荐大数据量)

public function getParentNames($cateId) {$currentCate = self::find($cateId);if (!$currentCate) return '';$names = [];$this->collectParentNames($currentCate, $names);array_unshift($names, $currentCate->name); // 添加当前分类return implode('-', $names);
}protected function collectParentNames($cate, &$names) {if ($cate->pid == 0) return;$parent = self::find($cate->pid);if ($parent) {$names[] = $parent->name;$this->collectParentNames($parent, $names);}
}

显然,迭代方式更简洁高效! 🚀

thinkphp8无限分类实现方法大比拼,哪种方法更高效?_#php_04