mcpField
U
umbraco-repository-pattern

umbraco-repository-pattern

github.com/umbraco开发27392 次安装6 周前更新
代码仓库

关于 umbraco-repository-pattern

用于构建 Umbraco 后台仓储和数据源的技能,帮助开发者组织数据操作、可选的会话存储及协调上下文;并建议自定义 API 调用使用带 Umbraco 身份验证的生成式 OpenAPI 客户端。

主要功能

  1. 01仓储与数据源模式
  2. 02CRUD 操作结构
  3. 03可选会话存储
  4. 04基于上下文的协调
  5. 05OpenAPI 客户端身份验证指引

适用场景

  1. 01分离界面与数据访问
  2. 02切换底层数据源
  3. 03在上下文中协调数据和状态
  4. 04构建树形仓储

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/umbraco/umbraco-cms-backoffice-skills --skill umbraco-repository-pattern

安装后在 Claude Code 中输入/umbraco-repository-pattern确认已加载。

详细介绍

Umbraco 仓储模式

这是什么?

仓储是 Backoffice 处理数据请求和更新通知的入口,它将数据访问与各种数据源(服务器、离线数据库、存储、Signal-R)解耦。仓储提供了管理数据操作的结构化方式,将业务逻辑与直接的数据访问分离,便于维护和扩展。仓储在后台使用数据源,让使用方无需了解数据的存储位置或方式即可处理数据。

重要:API 身份验证

绝不要使用原始的 fetch() 进行自定义 API 调用。 这样会导致 401 Unauthorized 错误。

始终使用配置了 Umbraco 身份验证上下文的生成式 OpenAPI 客户端。 设置说明请参阅 umbraco-openapi-client 技能。

文档

实现前,请始终获取最新文档:

工作流

  1. 获取文档 - 对上述 URL 使用 WebFetch
  2. 询问问题 - 要管理哪些数据?需要哪些 CRUD 操作?数据源类型是什么?
  3. 生成代码 - 根据最新文档实现仓储和数据源
  4. 说明 - 展示创建的内容及其用法

最小示例

仓储注册
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { MyRepository } from './my-repository.js';

const repositoryManifest = {
  type: 'repository',
  alias: 'My.Repository',
  name: 'My Repository',
  api: MyRepository,
};

umbExtensionsRegistry.register(repositoryManifest);
基本仓储实现(使用 OpenAPI 客户端)
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
// Import from generated OpenAPI client (see umbraco-openapi-client skill)
import { MyExtensionService } from '../api/index.js';

export class MyRepository extends UmbControllerBase {
  constructor(host: UmbControllerHost) {
    super(host);
  }

  async getAll() {
    // Use generated OpenAPI client - handles auth automatically
    const response = await MyExtensionService.getAll();
    return response.data;
  }

  async getById(id: string) {
    const response = await MyExtensionService.getById({ path: { id } });
    return response.data;
  }

  async create(data: any) {
    const response = await MyExtensionService.create({ body: data });
    return response.data;
  }

  async update(id: string, data: any) {
    const response = await MyExtensionService.update({
      path: { id },
      body: data,
    });
    return response.data;
  }

  async delete(id: string) {
    await MyExtensionService.delete({ path: { id } });
  }
}
在上下文中使用仓储
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { MyRepository } from './my-repository.js';

export class MyContext extends UmbContextBase<MyContext> {
  #repository = new MyRepository(this);
  #items = new UmbArrayState([]);

  readonly items = this.#items.asObservable();

  async loadItems() {
    const data = await this.#repository.getAll();
    this.#items.setValue(data);
  }

  async createItem(item: any) {
    const newItem = await this.#repository.create(item);
    this.#items.setValue([...this.#items.getValue(), newItem]);
  }

  async deleteItem(id: string) {
    await this.#repository.delete(id);
    this.#items.setValue(
      this.#items.getValue().filter(item => item.id !== id)
    );
  }
}
带数据源的仓储(使用 OpenAPI 客户端)
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { MyDataSource } from './my-data-source.js';

export class MyRepository extends UmbControllerBase {
  #dataSource = new MyDataSource(this);

  async getAll() {
    return await this.#dataSource.getAll();
  }

  async getById(id: string) {
    return await this.#dataSource.getById(id);
  }
}

// Data Source - uses generated OpenAPI client
import { MyExtensionService } from '../api/index.js';

export class MyDataSource extends UmbControllerBase {
  async getAll() {
    // Use OpenAPI client - NEVER raw fetch()
    const response = await MyExtensionService.getAll();
    return response.data;
  }

  async getById(id: string) {
    const response = await MyExtensionService.getById({ path: { id } });
    return response.data;
  }
}
树形仓储示例
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UmbTreeDataSource } from '@umbraco-cms/backoffice/tree';

export class MyTreeRepository extends UmbControllerBase {
  #dataSource: UmbTreeDataSource;

  constructor(host: UmbControllerHost) {
    super(host);
    this.#dataSource = new MyTreeDataSource(this);
  }

  async getRootItems() {
    return await this.#dataSource.getRootItems();
  }

  async getChildrenOf(parentKey: string) {
    return await this.#dataSource.getChildrenOf(parentKey);
  }

  async getAncestorsOf(key: string) {
    return await this.#dataSource.getAncestorsOf(key);
  }
}

核心概念

仓储:数据操作的入口,抽象数据访问

数据源:实际获取/存储数据的底层机制

存储:可选层,使用 State 对象在整个会话期间保存数据

宿主元素:必须为仓储实例化一个宿主,才能正确渲染上下文

关注点分离:

  • 仓储 = 提供哪些操作
  • 数据源 = 如何获取/存储数据
  • 存储 = 在哪里缓存数据
  • 上下文 = 由谁协调一切

优势:

  • UI 与数据层松耦合
  • 数据源可替换
  • 更易测试和维护
  • 数据访问模式一致

使用模式:

  1. 定义包含 CRUD 方法的仓储
  2. 实现负责实际 I/O 的数据源
  3. 在上下文中实例化仓储
  4. 上下文协调状态和仓储
  5. UI 观察上下文中的状态

就是这样!始终获取最新文档,保持示例精简,并生成完整可运行的代码。

评论

支持 Markdown