
本教程详细阐述了在Lar*el中处理具有递归关系的模型时,如何有效地排除特定节点及其所有子孙节点。通过自定义模型作用域和辅助函数,我们展示了一种从自引用表中查询数据并过滤掉指定层级分支的方法,涉及递归加载、数据扁平化及`whereNotIn`条件的应用,确保查询结果不包含目标节点及其所有后代。
在许多应用场景中,数据实体可能存在自引用关系,形成树状或图状结构。例如,一个“爱好”分类可能包含子爱好,子爱好又可以有自己的子爱好。为了在Lar*el中表示这种关系,我们通常会在数据表中包含一个parent_id字段,并在模型中定义递归关系。
假设我们有一个hobbies表,结构如下:
- id - name - parent_id
对应的Hobbies模型需要定义以下关系来支持递归操作:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Hobbies extends Model
{
/**
* 获取当前爱好的所有子爱好。
*/
public function sub_hobbies()
{
return $this->hasMany(Hobbies::class, 'parent_id');
}
/**
* 获取当前爱好的父爱好。
*/
public function parent_hobbies()
{
return $this->belongsTo(Hobbies::class, 'parent_id');
}
/**
* 递归获取当前爱好的所有子孙爱好。
*/
public function allsub()
{
return $this->sub_hobbies()->with('allsub');
}
/**
* 递归获取当前爱好的所有祖先爱好。
*/
public function allparent()
{
return $this->parent_hobbies()->with('allparent');
}
}这些关系定义允许我们方便地查询一个爱好的直接子爱好、父爱好,以及通过with('allsub')和with('allparent')递归加载其所有后代或祖先。
我们的目标是查询所有爱好,但排除某个特定爱好及其所有子孙爱好。例如,如果我们有一个爱好树:
- 爱好 1
- 爱好 11
- 爱好 12
- 爱好 121
- 爱好 122
- 爱好 13
- 爱好 2
- 爱好 21
- 爱好 22
- 爱好 221
- 爱好 222
- 爱好 23
- 爱好 3
- 爱好 31
- 爱好 32
- 爱好 321
- 爱好 322
- 爱好 33如果给定“爱好 1”的ID,我们希望查询结果不包含“爱好 1”、“爱好 11”、“爱好 12”、“爱好 121”、“爱好 122”和“爱好 13”。直接使用whereNotIn需要一个包含所有这些ID的列表,而这个列表是动态且递归生成的。
为了解决这个问题,我们可以结合使用模型作用域(Scope)和自定义的递归扁平化函数。
核心思路:
ChatGPT Writer
免费 Chrome 扩展程序,使用 ChatGPT AI 生成电子邮件和消息。
106
查看详情
由于Eloquent的with()方法会返回嵌套的对象结构,我们需要一个函数来遍历这些嵌套数据并提取所有非数组字段(特别是id),从而生成一个扁平的数组。
将以下flatten方法添加到Hobbies模型中:
// Hobbies.php
// ...
class Hobbies extends Model
{
// ... (现有关系定义)
/**
* 递归地将嵌套数组结果扁平化,只保留非数组字段。
* 适用于将Eloquent的with()结果转换为可操作的扁平数组。
*
* @param array $array 待扁平化的嵌套数组
* @return array 扁平化后的结果数组
*/
private function flatten(array $array): array
{
$result = [];
foreach ($array as $item) {
if (is_array($item)) {
// 提取当前层级的非数组属性
$result[] = array_filter($item, function ($value) {
return !is_array($value);
});
// 递归处理子项
$result = array_merge($result, $this->flatten($item));
}
}
// 过滤掉空数组,确保结果只包含有数据的项
return array_filter($result);
}
}这个flatten函数会遍历传入的数组,如果遇到子数组,它会递归地处理,并将其非数组元素提取出来。
接下来,在Hobbies模型中定义一个局部作用域scopeIsNotLine。这个作用域将封装排除特定分支的所有逻辑。
// Hobbies.php
// ...
class Hobbies extends Model
{
// ... (现有关系定义和flatten方法)
/**
* 作用域:查询所有爱好,但排除指定ID及其所有子孙爱好。
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param int $id 要排除的爱好及其子孙的根ID
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeIsNotLine($query, int $id)
{
// 1. 获取要排除的爱好及其所有子孙爱好
// toArray() 将模型集合转换为数组,便于后续的扁平化处理
$hobbiesToExclude = Hobbies::with('allsub')->where('id', $id)->get()->toArray();
// 2. 将嵌套结果扁平化,提取所有相关爱好的ID
$flattenedItems = collect($this->flatten($hobbiesToExclude));
// 3. 从扁平化结果中提取所有ID
$excludeIds = $flattenedItems->map(function ($item) {
return collect($item)->only(['id'])->all();
})->flatten()->all();
// 4. 使用 whereNotIn 排除这些ID
// 额外条件:whereDoesntH*e('is_archive') 示例,可根据实际需求调整或移除
return $query->whereNotIn('id', $excludeIds);
}
}作用域scopeIsNotLine的详细步骤:
现在,你可以在任何地方通过链式调用这个作用域来查询数据:
use App\Models\Hobbies; // 假设要排除ID为1的爱好及其所有子孙 $filteredHobbies = Hobbies::isNotLine(1)->get(); // $filteredHobbies 将包含所有爱好,但不包括 ID 为 1 的爱好及其所有后代。
通过在Lar*el模型中定义递归关系、一个自定义的扁平化辅助函数以及一个模型作用域,我们成功实现了一个灵活且可重用的解决方案,用于在查询中排除特定节点及其所有子孙节点。这种方法在许多需要处理层级数据的应用中都非常实用,为复杂的递归数据过滤提供了一种清晰且易于理解的实现方式。在实际部署时,应根据具体的数据规模和性能要求,权衡PHP层面处理与数据库层面处理的优劣。
以上就是Lar*el递归关系中排除子孙节点的策略的详细内容,更多请关注php中文网其它相关文章!
# 这是
# 顺德营销推广哪家好
# 惠州网站推广单位有哪些
# 萍乡抖音seo排名
# 太平网站seo优化
# seo 关键词优化技巧
# 红谷滩网站优化
# 做淘宝店怎么营销推广
# 绍兴整合营销推广方案
# seo排名标题
# 成人的SEO综合查询
# 查询结果
# 过滤掉
# mysql
# 遍历
# 转换为
# 链式
# 加载
# 自定义
# 扁平化
# 递归
# red
# 作用域
# app
# laravel
# php
相关文章:
c++如何使用std::memory_order控制原子操作顺序_c++ C++11内存模型详解
HTML5原生日期选择器与jQuery UI:实现日期选择器的联动与程序化控制
C++ explicit关键字防止隐式转换_C++构造函数安全规范
163邮箱网页版入口导航平台 163邮箱网页版登录入口官网导航
html网页设计源代码怎么运行_运行html网页设计源代码步骤【指南】
学习通在线学习平台 学习通网页版直接进入课程中心
Win10快速启动功能利弊分析 Win10开启或关闭快速启动教程【技巧】
深入理解J*aScript中的B样条曲线与节点向量生成
解决 Vaadin 8 中大文件音频播放与定位时出现的 IOException
微信网页版扫码登录入口 微信网页版二维码登录入口
Spyder启动失败:字体文件权限拒绝错误解决方案
Animex动漫社网入口地址 Animex动漫社网正版在线入口
windows10怎么查看本机ip_windows10命令提示符ipconfig使用
双系统安装时,如何设置默认启动系统? msconfig命令了解一下!
怎样在Excel中做仪表盘_Excel仪表盘设计与关键指标展示方法
为什么我的微信朋友圈看不到别人的更新_微信朋友圈更新显示异常解决方法
PyTorch模型训练效果不佳?深入剖析常见错误与调试技巧
J*aScript中localStorage数据的获取、清洗与格式化教程
php源码怎么在电脑上测试_电脑测试php源码方法步骤【教程】
如何在Promise链中优雅地中断后续then执行
随机参数递归函数的基准调用次数与时间复杂度探究
钉钉视频会议声音异常如何处理 钉钉会议音频修复技巧
J*aScript中针对特定容器内图片动画的实现教程
如何将HTML表格多行数据保存到Google Sheets
蛙漫漫画免费阅读入口_蛙漫官方正版无广告纯净版
漫蛙MANWA漫画主页官方入口 漫蛙漫画最新在线阅读地址
excel如何生成目录 excel一键生成工作表目录超链接
网易大神怎么保存别人动态的图片_网易大神动态图片保存方法
excel怎么提取文本中数字 excel函数提取技巧
Go语言:非阻塞式判断标准输入(os.Stdin)是否有数据
浏览器打开即用 美图秀秀网页版入口
必由学官方网站入口 必由学学生教师共用登录通道
学习通网页版官方登录 超星学习通电脑端入口指南
PHP:根据嵌套关联数组项值动态添加新键值对
Walmart退货API集成指南:PHP cURL实现与常见问题解析
win11开机启动修复循环怎么办 Win11无法进入系统高级启动解决方法【修复】
C++如何实现线程池_C++11手动实现一个简单的固定大小线程池
composer的"require-dev"部分是用来做什么的?
如何为你的Composer包编写自动化测试_集成PHPUnit到Composer的scripts工作流
整合Supabase认证与Django模型:跨模式迁移的解决方案
c++如何实现一个简单的软件渲染器_c++从零开始的3D图形学
蛙漫2日版入口 WAMAN2(日版)无删减漫画官网链接
微信聊天记录怎么加密_微信聊天记录加密方法
C++如何打印当前代码行号与文件名_C++预定义宏FILE与LINE的使用
Tabulator表格日期时间排序问题及自定义解决方案
2026春节假期票务安排_2026春节放假购票指南
抖音商城签到领现金是真的吗_抖音商城签到奖励与提现说明
Selenium Python中处理点击后新窗口加载冻结问题的策略与实践
qq游戏跨平台入口_qq游戏多设备同步登录
铃兰之剑为这和平的世界希里技能组及加点推荐
*请认真填写需求信息,我们会在24小时内与您取得联系。