'字段名', 'direction' => 'asc/desc']. */ protected array|bool|string $sortable = false; // '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 function list(array $params = []): array // { // $query = $this->newQuery(); // // // 通过模型配置自动应用所有搜索条件 // $this->applySearch($query, $params); // // // 动态应用排序 // $sortConfig = $this->getSortConfig(); // if ($sortConfig) { // $query->orderBy($sortConfig['field'], $sortConfig['direction']); // } // // $withDeleted = filter_var($params['with_deleted'] ?? false, FILTER_VALIDATE_BOOLEAN); // if ($withDeleted) { // $query->withTrashed(); // } // // // 存在分页参数(page或size)则进行分页查询 // if (isset($params['page']) || isset($params['size'])) { // $page = (int) ($params['page'] ?? 1); // $size = (int) ($params['size'] ?? 10); // $page = max(1, $page); // $size = max(1, min(100, $size)); // $result = $query->paginate($size, ['*'], 'page', $page); // return [ // 'total' => $result->total(), // 'list' => $result->items(), // ]; // } // // // 无分页参数时返回完整数据集合 // $items = $query->get()->toArray(); // // // 若模型支持树形结构则构建树形,否则返回普通数组 // if ($this instanceof TreeableInterface) { // return $this::buildTree($items, (int) ($params['parent_id'] ?? 0)); // } // // return $items; // } /** * 列表查询. */ // public function list(array $params = []): array // { // $query = $this->buildQueryFromParams($params); // return $this->listResult($query, $params); // } public function list(array $params = []): array { $query = static::query(); // 通过模型配置自动应用所有搜索条件 $this->applySearch($query, $params); // 动态应用排序 if ($this->sortable) { $sortConfig = $this->getSortConfig(); $query->orderBy($sortConfig['field'], $sortConfig['direction']); } $withDeleted = filter_var($params['with_deleted'] ?? false, FILTER_VALIDATE_BOOLEAN); if ($withDeleted) { $query->withTrashed(); } // 存在分页参数(page或size)则进行分页查询 if (isset($params['page']) || isset($params['size'])) { $page = (int) ($params['page'] ?? 1); $size = (int) ($params['size'] ?? 10); // 确保分页参数合法性 $page = max(1, $page); $size = max(1, min(100, $size)); // 限制最大页大小为100 $result = $query->paginate($size, ['*'], 'page', $page); return [ 'list' => $result->items(), 'total' => $result->total(), ]; } // 无分页参数时返回完整数据集合 $items = $query->get()->toArray(); // 若模型支持树形结构则构建树形,否则返回普通数组 if ($this instanceof TreeableInterface) { return $this::buildTree($items, (int) ($params['parent_id'] ?? 0)); } return $items; } /** * 根据模型的$search配置,自动应用搜索条件到查询构建器. */ // public function applySearch(Builder $query, array $params): void // { // foreach ($this->search as $field => $rule) { // // 跳过未传递的参数 // if (!isset($params[$field])) { // continue; // } // // $value = $params[$field]; // $this->applySearchRule($query, $field, $value, $rule); // } // } // // /** // * 应用单个搜索规则 // */ // protected function applySearchRule(Builder $query, string $field, $value, $rule): void // { // // 处理规则格式(支持字符串简写或数组配置) // $config = is_array($rule) ? $rule : ['type' => $rule]; // $type = $config['type']; // // switch ($type) { // case '=': // 精确匹配 // $query->where($field, $value); // break; // case 'like': // 模糊匹配 // $query->where($field, 'like', "%{$value}%"); // break; // case 'between': // 范围查询(支持数组或两个参数) // $values = is_array($value) ? $value : [$value, $params[$field . '_end'] ?? $value]; // $query->whereBetween($field, $values); // break; // case 'callback': // 自定义回调 // if (isset($config['handler']) && is_callable($config['handler'])) { // call_user_func($config['handler'], $query, $value); // } // break; // // 可扩展其他类型:in、>、< 等 // } // } /** * 快捷创建. */ public static function createOne(array $data): AetherModel|Builder|HyperfModel { return static::query()->create($data); } /** * 快捷更新. */ public static function updateById(int $id, array $data): int { return static::query()->where('id', $id)->update($data); } /** * 快捷删除指定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 的记录', $id, )); } 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 的记录', $id )); } 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 applySorting(Builder $query, array $params = []): void { // 优先使用传入的排序参数 if (isset($params['sort_field'], $params['sort_direction'])) { $query->orderBy($params['sort_field'], $params['sort_direction']); return; } // 使用模型配置的排序 $sortConfig = $this->getSortConfig(); if (! empty($sortConfig)) { $query->orderBy($sortConfig['field'], $sortConfig['direction']); } } /** * 根据分页参数获取结果. */ protected function listResult(Builder $query, array $params = []): array { // 分页处理 if (isset($params['page'], $params['size'])) { $page = max(1, (int) $params['page']); $size = max(1, min(100, (int) $params['size'])); $result = $query->paginate($size, ['*'], 'page', $page); return [ 'list' => $result->items(), 'total' => $result->total(), ]; } // 获取所有数据 $result = $query->get()->toArray(); // 如果实现了树结构接口,构建树 if ($this instanceof TreeableInterface) { return $this->buildTree($result, $params['parent_id'] ?? 0); } return $result; } /** * 初始化模型. */ 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) || ! isset($value)) { continue; } // 核心限制:只处理$search数组中定义的字段 if (! isset($this->search[$field])) { continue; } // 处理嵌套关系查询(如:user.name,需在$search中配置完整键名) if (str_contains($field, '.')) { [$relation, $relationField] = explode('.', $field, 2); $query->whereHas($relation, function ($q) use ($relationField, $value) { // 嵌套查询默认使用精确匹配,如需特殊规则可在$search中自定义处理 $q->where($relationField, $value); }); continue; } // 优先使用自定义搜索器方法(仅对$search中存在的字段生效) $searchMethod = 'search' . ucfirst($field); if (method_exists($this, $searchMethod)) { $this->{$searchMethod}($query, $value); continue; } // 应用$search中定义的搜索规则(如=、like等) $this->applySearchRule($query, $field, $value, $this->search[$field]); } } /** * 应用搜索规则. */ 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); } } }