134 lines
4.5 KiB
PHP
134 lines
4.5 KiB
PHP
<?php
|
||
|
||
namespace App\Models\Nasa;
|
||
|
||
use Illuminate\Database\Eloquent\Builder;
|
||
use Illuminate\Database\Eloquent\Model;
|
||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||
use Illuminate\Support\Facades\Route;
|
||
|
||
class Navigation extends Model
|
||
{
|
||
protected $table = 'nasa_navigation';
|
||
protected $primaryKey = 'id';
|
||
|
||
const CREATED_AT = 'dtCreatedAt';
|
||
const UPDATED_AT = 'dtUpdatedAt';
|
||
|
||
const TYPE_TOP_BAR = 'TOP_BAR';
|
||
const TYPE_SIDEBAR_MENU = 'SIDEBAR_MENU';
|
||
const TYPE_INNER_PAGE_TAB = 'INNER_PAGE_TAB';
|
||
|
||
const STATUS_OPEN = 1;
|
||
const STATUS_CLOSE = 0;
|
||
|
||
private static array $aComponentLabels = [
|
||
self::TYPE_TOP_BAR => '顶部导航条',
|
||
self::TYPE_SIDEBAR_MENU => '侧边栏菜单',
|
||
self::TYPE_INNER_PAGE_TAB => '页面内标签页',
|
||
];
|
||
|
||
private static array $aStatusLabels = [
|
||
self::STATUS_OPEN => '开启',
|
||
self::STATUS_CLOSE => '关闭',
|
||
];
|
||
|
||
public function getMapAll()
|
||
{
|
||
$aMap = [
|
||
'sComponentType' => self::$aComponentLabels,
|
||
'iStatus' => self::$aStatusLabels
|
||
];
|
||
|
||
return $aMap;
|
||
}
|
||
|
||
protected function casts(): array
|
||
{
|
||
return [
|
||
'dtUpdatedAt' => 'datetime:Y-m-d H:i:s', // 自动转换为 Carbon 对象
|
||
'dtCreatedAt' => 'datetime:Y-m-d H:i:s', // 写入时自动 password_hash 加密
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 递归获取当前菜单的所有上级祖先节点(包含自身)
|
||
* 结果按从根节点到当前节点的顺序排列(即:[一级, 二级, 三级, 当前])
|
||
*
|
||
* @return \Illuminate\Support\Collection
|
||
*/
|
||
public function cGetAncestorsAndSelf(): \Illuminate\Support\Collection
|
||
{
|
||
$cPath = collect([$this]);
|
||
$oCurrent = $this;
|
||
|
||
// 使用循环代替递归进行反向溯源,性能更佳,防止极端情况下的堆栈溢出
|
||
while ($oCurrent->iParentId != 0) {
|
||
// 高标准注意:这里如果能做内存缓存或走预加载更佳,但由于是单条数据向上,走主键查询性能损耗可控
|
||
$oParent = self::where('id', $oCurrent->iParentId)->available()->first();
|
||
|
||
if (!$oParent) {
|
||
break; // 防御性编程:万一数据链断了,及时跳出
|
||
}
|
||
|
||
$cPath->prepend($oParent); // 往前插入,保证顺序是 [父, 子, 孙]
|
||
$oCurrent = $oParent;
|
||
}
|
||
|
||
return $cPath;
|
||
}
|
||
|
||
public function children()
|
||
{
|
||
// 这里的 iParentId 是外键,id 是当前表的主键
|
||
return $this->hasMany(self::class, 'iParentId', 'id');
|
||
}
|
||
|
||
/**
|
||
* 你的本地作用域(Scope),确保 scopeAvailable 存在
|
||
* 顺便提一句,高标准设计中,状态字段建议内聚在 scope 里
|
||
*/
|
||
public function scopeAvailable($query)
|
||
{
|
||
// 假设你的可用状态字段是 iStatus 且 1 为可用,请根据实际字段调整
|
||
return $query->where('iStatus', 1);
|
||
}
|
||
|
||
/**
|
||
* 高标准设计:递归预加载所有下级(无限级核心)
|
||
* 只要这一条 Eager Loading,Laravel 会自动用最少的 SQL 把整棵树一次性读入内存
|
||
*/
|
||
public function childrenRecursive()
|
||
{
|
||
return $this->children()
|
||
->available()
|
||
->orderBy('iSort', 'asc')
|
||
->with('childrenRecursive'); // 完美的自我套娃
|
||
}
|
||
|
||
/**
|
||
* 业务逻辑内聚:深度优先穿透获取最终 URL
|
||
* 职责:如果有下级,永远优先往下找第一个合法的下级;如果没有下级或下级全挂了,退回自身。
|
||
*/
|
||
public function sGetFinalUrl(): string
|
||
{
|
||
// 1. 如果有下级集合(已经过 available 过滤),尝试向下穿透
|
||
if ($this->relationLoaded('childrenRecursive') && $this->childrenRecursive->isNotEmpty()) {
|
||
foreach ($this->childrenRecursive as $oChild) {
|
||
// 递归向下寻找子节点的最终 URL
|
||
$sChildUrl = $oChild->sGetFinalUrl();
|
||
|
||
// 只要子节点或者孙子节点能返回一个有效 URL,立刻中断并返回它(最左原则)
|
||
if (!empty($sChildUrl)) {
|
||
return $sChildUrl;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 2. 兜底逻辑:没有下级,或者所有下级分支都没配置有效路由,返回自身的 URL
|
||
// return $this->sPermissionSlug;
|
||
return Route::has($this->sPermissionSlug) ? route($this->sPermissionSlug) : '';
|
||
}
|
||
|
||
}
|