数据仓库

概述

数据仓库(Repository)层是 XinAdmin 后端架构中负责数据访问的重要组成部分,它位于服务层(Service)和模型层(Model)之间。数据仓库的主要职责包括:

  • 数据访问抽象:封装底层数据库操作,提供统一的访问接口
  • 业务逻辑隔离:将数据操作逻辑与业务逻辑分离
  • 查询构建:提供灵活的查询条件构建机制
  • 验证处理:统一的数据验证和错误处理
  • 模型关系管理:处理模型间的关联关系

XinAdmin 的数据仓库遵循 Repository 模式,所有仓库类都继承自 BaseRepository 并实现 RepositoryInterface,确保了统一的接口和一致的行为。

BaseRepository

BaseRepository 是所有仓库类的基类,提供了一套完整的通用方法来简化数据操作。它实现了 RepositoryInterface 接口,并提供了丰富的功能支持。

CRUD 方法

BaseRepository 提供了标准的 CRUD(创建、读取、更新、删除)操作方法:

  1. 创建(Create)

    • 方法:create(array $data): bool
    • 功能:新增数据记录
    • 流程:数据验证 → 创建记录 → 返回结果
    • 示例:
      public function create(array $data): bool
      {
          $data = $this->validation($data); // 数据验证
          if(empty($data)) {
              throw new RepositoryException('Validation failed: empty data');
          }
          $model = $this->model()->create($data); // 创建记录
          return !!$model; // 返回布尔结果
      }
  2. 读取(Read)

    • 单条查询:find(int $id): array
    • 列表查询:list(array $params): array
    • 功能:根据ID获取单条记录或根据条件获取多条记录
    • 示例:
      public function find(int $id): array
      {
          $model = $this->model()->find($id);
          if (empty($model)) {
              throw new RepositoryException("Model not found");
          }
          return $model->toArray();
      }
  3. 更新(Update)

    • 方法:update(int $id, array $data): bool
    • 功能:更新指定ID的记录
    • 流程:数据验证 → 查找记录 → 更新 → 返回结果
    • 示例:
      public function update(int $id, array $data): bool
      {
          $validated = $this->validation($data); // 数据验证
          $model = $this->model()->find($id); // 查找记录
          if (empty($model)) {
              throw new RepositoryException('Model not found');
          }
          return $model->update($validated); // 更新记录
      }
  4. 删除(Delete)

    • 方法:delete(int $id): bool
    • 功能:删除指定ID的记录
    • 示例:
      public function delete(int $id): bool
      {
          $model = $this->model()->find($id);
          if (empty($model)) {
              throw new RepositoryException('Model not found');
          }
          return $model->delete(); // 删除记录
      }

查询方法

BaseRepository 提供了强大的查询构建功能,支持多种查询方式:

  1. 基础列表查询

    public function list(array $params): array
    {
        $pageSize = $params['pageSize'] ?? 10; // 默认每页10条
        $query = $this->model();
        return $this->buildSearch($params, $query) // 构建查询条件
            ->paginate($pageSize) // 分页
            ->toArray();
    }
  2. 条件查询构建

    • 支持多种比较操作符(=, <>, <, >, <=, >=)
    • 支持模糊查询(like, afterLike, beforeLike)
    • 支持日期查询(date, betweenDate)
    • 支持快速搜索(任意字段匹配)
  3. 验证方法

    • validation(array $data):根据规则验证数据
    • rules():定义验证规则
    • messages():定义验证错误消息

关联查询

数据仓库支持模型间的关联查询,可以在子类中通过模型关系实现复杂的数据检索。例如,在用户仓库中可以关联角色信息:

// 在 SysUserRepository 中
$model = $this->model()->with('roles')->find($id);

// 或者在查询时添加关联
public function listWithRoles(array $params): array
{
    $pageSize = $params['pageSize'] ?? 10;
    $query = $this->model()->with('roles');
    return $this->buildSearch($params, $query)
        ->paginate($pageSize)
        ->toArray();
}

RepositoryInterface

RepositoryInterface 定义了数据仓库的标准接口,确保所有仓库类具有一致的方法签名。接口包含以下方法:

  • find(int $id): array - 根据ID查找单条记录
  • list(array $params): array - 列表查询
  • create(array $data): bool - 创建记录
  • update(int $id, array $data): bool - 更新记录
  • delete(int $id): bool - 删除记录

所有仓库类都必须实现这些方法,保证了统一的 API 接口。

创建仓库

创建新的数据仓库需要遵循以下步骤:

  1. 确定仓库名称:根据业务实体命名,如用户仓库命名为 SysUserRepository

  2. 创建仓库文件:仓库文件应放置在 app/Repositories/ 目录下,对于系统相关仓库则放在 app/Repositories/Sys/ 目录

  3. 继承 BaseRepository:新仓库类必须继承 BaseRepository

    <?php
    
    namespace App\Repositories\Sys;
    
    use App\Repositories\BaseRepository;
    use App\Models\Sys\YourModel;
    use Illuminate\Database\Eloquent\Builder;
    
    class YourRepository extends BaseRepository
    {
        // 仓库实现
    }
  4. 实现模型方法:必须实现 model() 抽象方法,返回对应的查询构建器

    protected function model(): Builder
    {
        return YourModel::query();
    }
  5. 配置搜索字段:设置 $searchField$quickSearchField 属性

    protected array $searchField = [
        'id' => '=',
        'name' => 'like',
        'status' => '=',
        'created_at' => 'date',
    ];
    
    protected array $quickSearchField = ['name', 'description', 'id'];
  6. 定义验证规则:根据需要重写 rules()messages() 方法

    protected function rules(): array
    {
        return [
            'name' => 'required|unique:your_table,name',
            'status' => 'required|in:0,1',
        ];
    }
    
    protected function messages(): array
    {
        return [
            'name.required' => '名称不能为空',
            'name.unique' => '名称已存在',
        ];
    }
  7. 自定义业务方法:根据业务需求添加特定的数据操作方法

    public function findByStatus(int $status): array
    {
        return $this->model()
            ->where('status', $status)
            ->get()
            ->toArray();
    }

查询构建

数据仓库提供了灵活的查询构建机制,支持多种查询方式和参数处理。

条件查询

条件查询支持以下操作符:

  • =:精确匹配
  • <>:不等于
  • <, >:小于、大于
  • <=, >=:小于等于、大于等于
  • like:模糊匹配(前后包含)
  • afterLike:后缀匹配
  • beforeLike:前缀匹配
  • date:日期匹配
  • betweenDate:日期范围匹配

示例配置:

protected array $searchField = [
    'id' => '=',
    'name' => 'like',
    'status' => '=',
    'created_at' => 'betweenDate',
];

排序

支持动态排序功能,通过 sorter 参数控制:

// 参数示例
$params = [
    'sorter' => '{"name":"ascend"}' // 或者 ['name' => 'ascend']
];

// 排序处理逻辑
if (isset($params['sorter']) && $params['sorter']) {
    if(is_array($params['sorter'])) {
        $sorter = $params['sorter'];
    } else {
        $sorter = json_decode($params['sorter'], true);
    }
    if (count($sorter) > 0) {
        $column = array_keys($sorter)[0];
        $direction = $sorter[$column] == 'ascend' ? 'asc' : 'desc';
        $model->orderBy($column, $direction);
    }
}

分页

内置分页功能,默认每页10条记录,可通过 pageSize 参数调整:

public function list(array $params): array
{
    $pageSize = $params['pageSize'] ?? 10;
    $query = $this->model();
    return $this->buildSearch($params, $query)
        ->paginate($pageSize)
        ->toArray();
}

关联加载

支持通过 with() 方法进行关联加载,减少 N+1 查询问题:

// 在仓库中添加关联查询方法
public function listWithRelations(array $params): array
{
    $pageSize = $params['pageSize'] ?? 10;
    $query = $this->model()->with(['relation1', 'relation2']);
    return $this->buildSearch($params, $query)
        ->paginate($pageSize)
        ->toArray();
}

最佳实践

  1. 统一命名:仓库类名应与对应模型保持一致,如 UserModel 对应 UserRepository

  2. 职责分离:仓库层应专注于数据访问逻辑,不应包含业务逻辑

  3. 验证前置:所有数据操作前应进行适当的验证

  4. 异常处理:使用 RepositoryException 进行错误处理

  5. 资源清理:确保及时释放数据库连接等资源

  6. 性能优化:合理使用索引、分页和关联查询,避免大数据量查询

  7. 安全考虑:防止 SQL 注入,验证所有输入参数

  8. 代码复用:充分利用 BaseRepository 提供的通用方法,避免重复造轮子

  9. 文档注释:为公共方法添加详细的 PHPDoc 注释

  10. 测试覆盖:为仓库方法编写单元测试,确保数据操作的正确性

遵循这些最佳实践可以确保数据仓库层的健壮性、可维护性和高性能。