'integer', 'sort' => 'integer', 'status' => 'integer', 'created_at' => 'datetime:Y-m-d H:i:s', 'updated_at' => 'datetime:Y-m-d H:i:s', ]; /** * 父分类 */ public function parent(): BelongsTo { return $this->belongsTo(self::class, 'parent_id', 'id'); } /** * 子分类 */ public function children(): HasMany { return $this->hasMany(self::class, 'parent_id', 'id')->orderBy('sort'); } /** * 分类下的商品 */ public function products(): HasMany { return $this->hasMany(ProductModel::class, 'category_id', 'id'); } /** * 获取分类树(树表展示 / 级联下拉选项复用) * * @param bool $onlyEnabled 是否仅返回启用分类 * @return array */ public static function getTreeData($columns = ['*'], bool $onlyEnabled = false): array { $query = static::query()->orderBy('sort')->orderBy('id'); if ($onlyEnabled) { $query->where('status', self::STATUS_NORMAL); } return static::buildTree($query->get($columns)->toArray()); } /** * 按树展示顺序(sort/id 深度优先)获取分类 ID 列表,供商品列表等按分类顺序排序 * * @param bool $onlyEnabled 是否仅包含启用分类 * @return array */ public static function getTreeOrderedIds(bool $onlyEnabled = false): array { return static::flattenTreeIds(static::getTreeData(['id', 'parent_id'], $onlyEnabled)); } /** * 深度优先展开分类树为有序 ID 列表 * * @param array> $tree * @return array */ private static function flattenTreeIds(array $tree): array { $ids = []; foreach ($tree as $node) { $ids[] = (int) $node['id']; if (!empty($node['children'])) { $ids = [...$ids, ...static::flattenTreeIds($node['children'])]; } } return $ids; } /** * 将扁平分类列表组装为树 * * @param array> $items * @param int $parentId * @return array> */ public static function buildTree(array $items, int $parentId = 0): array { $tree = []; foreach ($items as $item) { if ((int) $item['parent_id'] !== $parentId) { continue; } $children = static::buildTree($items, (int) $item['id']); if ($children !== []) { $item['children'] = $children; } $tree[] = $item; } return $tree; } }