'字段名', 'direction' => 'asc/desc']. */ protected array|bool|string $sortable = 'sort'; // 默认按sort字段升序 public function __construct(array $attributes = []) { parent::__construct($attributes); $this->bootBaseModel(); } /** * 获取排序配置. * @return null|array [field, direction] 或 null(禁用排序) */ public function getSortConfig(): ?array { if ($this->sortable === false) { return null; } // 处理字符串配置(如 'sort' 或 'create_time') if (is_string($this->sortable)) { return [ 'field' => $this->sortable, 'direction' => 'asc', ]; } return null; } /** * 快捷创建. */ public static function createOne(array $data): AetherModel|Builder|HyperfModel { return static::query()->create($data); } /** * 快捷更新. */ public static function updateById(int $id, array $data): bool { return static::query()->where('id', $id)->update($data) > 0; } /** * 快捷删除指定ID的记录. * * @param int $id 要删除的记录ID * @return bool 成功删除返回true * @throws ModelNotFoundException 当记录不存在时抛出 * @throws Exception 当删除操作发生其他错误时抛出 */ public static function deleteById(int $id): bool { if (! static::query()->where('id', $id)->exists()) { throw new ModelNotFoundException(sprintf( '找不到ID为 %d 的 %s 记录', $id, static::class )); } return static::query()->where('id', $id)->delete() > 0; } /** * 快捷查找. * @return Builder|Builder[]|Collection|HyperfModel * @throws Exception 当删除操作发生其他错误时抛出 * @throws ModelNotFoundException */ public static function findById(int $id): array|Builder|Collection|HyperfModel { $record = static::query()->find($id); if (is_null($record)) { throw new ModelNotFoundException(sprintf('找不到 ID 为 %d 的记录', $id)); } return $record; } /** * 快捷查找或失败. * @param int $id 要查找的记录ID * @return AetherModel 根据ID查找记录,不存在则抛出异常 * 根据ID查找记录,不存在则抛出异常 * @throws Exception 当删除操作发生其他错误时抛出 * @throws ModelNotFoundException 当记录不存在时抛出 */ public static function findOrFailById(int $id): static { $record = static::query()->find($id); if (is_null($record)) { throw new ModelNotFoundException(sprintf( '找不到ID为 %d 的 %s 记录', $id, static::class )); } return $record; // static::query()->findOrFail($id); } /** * 获取列表. */ public static function getList( array $conditions = [], array $columns = ['*'], array $orders = [] ): Collection { $query = static::buildQuery($conditions, $orders); return $query->get($columns); } /** * 分页查询列表. */ public static function getPageList( array $conditions = [], int $page = 1, int $pageSize = 10, array $columns = ['*'], array $orderBy = [] ): LengthAwarePaginatorInterface { // 直接通过静态方法链构建查询 $query = static::query(); // 应用条件 foreach ($conditions as $field => $value) { $query->where($field, $value); } // 应用排序 foreach ($orderBy as $field => $direction) { $query->orderBy($field, $direction); } // 执行分页 return $query->paginate($pageSize, $columns, 'page', $page); } /** * 获取器处理. */ public function getAttribute(string $key): mixed { $value = parent::getAttribute($key); // 检查是否有自定义获取器 $getterMethod = 'get' . ucfirst($key) . 'Attr'; if (method_exists($this, $getterMethod)) { return $this->{$getterMethod}($value); } // 应用获取器规则 if (isset($this->append[$key])) { return $this->applyGetterRule($value, $this->append[$key]); } return $value; } /** * 批量更新. */ public static function batchUpdate(array $conditions, array $data): int { return static::query()->where($conditions)->update($data); } /** * 事务处理. * @throws Throwable */ public static function transaction(Closure $closure): mixed { return Db::transaction($closure); } /** * 初始化模型. */ protected function bootBaseModel(): void { // 自动注册搜索器和获取器 $this->registerSearchers(); $this->registerGetters(); } /** * 注册搜索器. */ protected function registerSearchers(): void { // 为模型查询添加全局作用域,自动应用搜索规则 static::addGlobalScope('auto_search', function (Builder $query) { $searchConditions = $this->getSearchConditions(); if (empty($searchConditions)) { return; } // 应用搜索条件到查询 $this->applySearch($query, $searchConditions); }); } /** * 获取搜索条件. * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ protected function getSearchConditions(): array { $request = ApplicationContext::getContainer()->get(RequestInterface::class); $allParams = $request->all(); if (is_array($allParams) && count($allParams) === 1 && is_array($allParams[0])) { $allParams = $allParams[0]; } $allowedFields = array_keys($this->search); return array_intersect_key($allParams, array_flip($allowedFields)); } protected function registerGetters(): void { foreach ($this->append as $field => $rule) { // 为字段注册获取器回调 $this->registerGetter($field, $rule); } } /** * 为单个字段注册获取器. * @param string $field 字段名 * @param array|Closure|string $rule 规则(类型/带参数的类型/闭包) */ protected function registerGetter(string $field, array|Closure|string $rule): void { // 生成获取器方法名(遵循 Hyperf 模型获取器规范:get{Field}Attr) $getterMethod = 'get' . ucfirst($field) . 'Attr'; // 若已存在自定义获取器方法,则不覆盖 if (method_exists($this, $getterMethod)) { return; } // 动态定义获取器方法 $this->{$getterMethod} = function ($value) use ($rule) { // 闭包规则直接执行 if ($rule instanceof Closure) { return $rule($value, $this); // 传入当前模型实例方便关联字段处理 } // 解析规则类型和参数 if (is_array($rule)) { $type = $rule[0]; $params = $rule[1] ?? []; } else { $type = $rule; $params = []; } // 应用规则(复用/扩展 applyGetterRule 方法) return $this->applyGetterRule($value, $type, $params); }; } /** * 应用获取器规则(支持参数). * @param mixed $value 字段原始值 * @param string $type 规则类型 * @param array $params 规则参数 */ protected function applyGetterRule(mixed $value, string $type, array $params = []): mixed { return match ($type) { // 基础规则 'date' => $value instanceof DateTime ? $value->format('Y-m-d') : $value, 'datetime' => $value instanceof DateTime ? $value->format('Y-m-d H:i:s') : $value, 'timestamp' => $value instanceof DateTime ? $value->getTimestamp() : $value, 'boolean' => (bool) $value, 'json' => is_string($value) ? json_decode($value, true) : $value, // 参数化规则 'number' => $this->formatNumber($value, $params), // 数字格式化 'truncate' => $this->truncateString($value, $params), // 字符串截断 'enum' => $this->mapEnum($value, $params), // 枚举映射 default => $value, }; } // 数字格式化(示例参数化实现) protected function formatNumber($value, array $params): string { $precision = $params['precision'] ?? 2; // 默认保留2位小数 return number_format((float) $value, $precision); } // 字符串截断(示例参数化实现) protected function truncateString($value, array $params): string { $length = $params['length'] ?? 20; // 默认截断到20字符 $suffix = $params['suffix'] ?? '...'; // 省略符 if (! is_string($value)) { $value = (string) $value; } return mb_strlen($value) > $length ? mb_substr($value, 0, $length) . $suffix : $value; } // 枚举映射(示例参数化实现) protected function mapEnum($value, array $params): mixed { $map = $params['map'] ?? []; // 枚举映射表,如 [1 => '男', 2 => '女'] return $map[$value] ?? $value; } // 修正 buildQuery 方法,避免使用 new static() protected static function buildQuery(array $conditions = [], array $orderBy = []): Builder { // 使用静态 query() 方法获取查询构建器 $query = static::query(); // 处理搜索条件 foreach ($conditions as $field => $value) { $query->where($field, $value); } // 处理排序 foreach ($orderBy as $field => $direction) { $query->orderBy($field, $direction); } return $query; } /** * 应用搜索条件. */ protected function applySearch(Builder $query, array $conditions): void { foreach ($conditions as $field => $value) { // 跳过非字符串的字段名(防止索引数组键导致的类型错误) if (! is_string($field)) { continue; } // 处理嵌套关系查询(如:user.name) if (str_contains($field, '.')) { [$relation, $relationField] = explode('.', $field, 2); $query->whereHas($relation, function ($q) use ($relationField, $value) { $q->where($relationField, $value); }); continue; } // 检查是否有自定义搜索器方法 $searchMethod = 'search' . ucfirst($field); if (method_exists($this, $searchMethod)) { $this->{$searchMethod}($query, $value); continue; } // 应用搜索规则配置 if (isset($this->search[$field])) { $this->applySearchRule($query, $field, $value, $this->search[$field]); continue; } // 默认精确匹配(仅对$search中允许的字段生效,因已通过白名单过滤) $query->where($field, $value); } } /** * 应用搜索规则. */ protected function applySearchRule(Builder $query, string $field, mixed $value, array|string $rule): void { if (is_array($rule)) { $type = $rule[0]; $params = $rule[1] ?? []; } else { $type = $rule; $params = []; } switch ($type) { case 'like': $query->where($field, 'like', "%{$value}%"); break; case 'like_left': $query->where($field, 'like', "%{$value}"); break; case 'like_right': $query->where($field, 'like', "{$value}%"); break; case 'in': $query->whereIn($field, (array) $value); break; case 'not_in': $query->whereNotIn($field, (array) $value); break; case 'gt': $query->where($field, '>', $value); break; case 'lt': $query->where($field, '<', $value); break; case 'gte': $query->where($field, '>=', $value); break; case 'lte': $query->where($field, '<=', $value); break; case 'between': $query->whereBetween($field, (array) $value); break; case 'null': $query->whereNull($field); break; case 'not_null': $query->whereNotNull($field); break; default: $query->where($field, $type, $value); } } }