nativescript-couchbase-vuex-orm
一个插件,用于在运行时使用 Couchbase 生成 Vuex ORM 风格的动作。
npm i --save nativescript-couchbase-vuex-orm

Nativescript Couchbase Vuex ORM

nativescript-couchbase-vuex-orm

一个模块,使用 nativescript-couchbase-plugin 自动生成传统的 ORM 风格模块 Vuex 动作。非常适合 Vue NativeScript 项目。例如。


示例

给定几个 "模型" 类...

export class Document {
constructor () {
this.hasMany(['ChildDocument']),
this.properties({
name: 'Default Document Name'
})
}
}

export class ChildDocument {
constructor () {
this.belongsTo = 'Document'
}
}

...以及一些存储设置(见下文),以下动作将自动可用,可在 vue 模板上分发!

import { mapGetters, mapActions } from 'vuex'
...
export default {
methods () {
...mapActions[
'Document/getById',
'Document/create',
'Document/update',
'Document/addChildDocument',
'Document/removeChildDocument',
'Document/reorderChildDocument',
'Document/delete',
'ChildDocument/create',
'ChildDocument/update'
/* etc. */]
}
}

因此,经过几次分发后,您的 Couchbase-lite 数据库中的数据将类似于以下内容

`select * from Document` [{
_type: 'Document',
childDocuments: [
'6f0a1418-ea43-4cd2-a652-5202f4f251fb',
'6f0a1418-ea43-4cd2-a652-5202f4f251fc'
],
id: '6f0a1418-ea43-4cd2-a652-5202f4f251fa',
name: 'Default Document Name'
}]

`select * from ChildDocument` [
{
_parentId: '6f0a1418-ea43-4cd2-a652-5202f4f251fa',
_type: 'ChildDocument',
id: '6f0a1418-ea43-4cd2-a652-5202f4f251fb'
},
{
_parentId: '6f0a1418-ea43-4cd2-a652-5202f4f251fa',
_type: 'ChildDocument',
id: '6f0a1418-ea43-4cd2-a652-5202f4f251fc'
}
]

要求


安装

要使用 NPM 安装,请在所需项目目录中输入以下命令。

npm install --save nativescript-couchbase-vuex-orm

您的存储 必须 在其根状态对象中包含一个具有 Couchbase-lite DB 的 $db。您的 "模型" 类如下所示(我们继续使用上面的 DocumentChildDocument 示例)

import { Couchbase } from 'nativescript-couchbase-plugin'
import { ORM } from 'nativescript-couchbase-vuex-orm'

import { Document, ChildDocument } from './Example.js'

const store = {
state: {
$db: new Couchbase('<your-db-name-here>'),
},
modules: {
Document: new ORM(Document).build(),
ChildDocument: new ORM(ChildDocument).build()
}
}

注意。模型类不能作为 vuex 子模块在 其他 模型类中使用(尽管有雄心壮志的开发者可以分叉此项目以寻求此功能!)如果您希望使用集合创建内部对象,请将一个对象字面量添加到模型的 properties 对象中。


模型抽象类

模型类应如下所示(这里我使用了可以有许多 Stories 的 WorkFlow 的示例)

class WorkFlow {
constructor () {
// a collection can only belong to one parent but can be featured in the hasMany of multiple instance types. A Model's `belongsTo` property merely enforces a `_parentId` property is present on the child collections of a given parent.
this.belongsTo = 'Organisation'

this.hasMany = [
// NB. delete ALWAYS cascades by default

// - EITHER -
// String ChildClassName
'Story',

// (v^1.2.0)
// - OR -
// Model ChildClass
// NB. to eager load children, a Model MUST be supplied
Story

// - OR -
// Object ChildModel
{
// --------------
// - EITHER -
// String ChildClass i.e. 'class ChildClass { ... }'
name: 'Story',

// (v^1.2.0)
// - OR -
// Model ChildClass
// NB. to eager load children, a Model MUST be supplied
name: Story,
// --------------

// (optional) a plural name to use instead of bolting an "s" onto the name property (the default)
pluralName: 'Stories',

// override cascade behaviour with explicit property
cascade: false
}
],

// default model properties initialised as below:
this.properties = {
someString: 'Foo',
someNumber: 1234,
someObject: {
foo: 'bar'
}
}

// (v^1.3.0)
// Still want to use the module as a normal part of your vue store as well?
// You can optionally add state, mutations and getters too!
this.state = {
foo: 'test',
bar: 1234
}
this.mutations = {
setFooBar (state, { foo, bar }) {
state.foo = foo
state.bar = bar
}
}
// all states are automatically mapped to default getters.
// e.g. `store.getters[Workflow/foo] = state => state.someStoreVariable`
// you can optionally add custom (or override) getters as below.
this.getters = {
getFooBar: state => state.foo + state.bar
}
}

// custom actions are added outside of your constructor
async customAction ({ dispatch, rootState }, args) {
// e.g.
// 1) call an instance modeled on the local class
const yourModelName = await dispatch('getById', args.id)
// 2) call parent using the retrieved parent Id
const parentModel = await dispatch(
'SomeParentModel/getById',
yourModelName._parentId,
{ root: true })
// 3) return the name property
return parentModel.name
}
}

API

请记住 所有调用都使用 await/async 或 Promise.then() 格式

getById(String id || { String id, Boolean lazy, Number maxDepth })Object<CouchbaseCollection> collection

  • 根据提供的哈希 ID 返回一个集合。
  • ID 必须与给定的模型类型相同
  • (自 v^1.2.0 开始) 如果提供一个对象而不是字符串 ID
    • lazy 属性设置为 false 以在原地加载数据,而不是其 ID 引用(注意只有 在父级的 hasMany 属性中声明为 Model 类的子级才能加载)。
    • (可选)定义一个 maxDepth 属性以指定 ORM 应注入的子级深度

create(Object properties)String id

  • 创建一个新的集合并用提供的属性填充它
  • 由于这是一个 NoSQL 数据库,因此不强制执行字段

update({ String id, Object props })Boolean success

  • 更新给定的集合
  • ID 必须与给定的模型类型相同

delete(String id)Boolean success

  • 删除给定的集合
  • 将删除级联到提供的子集合
  • 注意 删除项 不会 受到强制执行,并且不会从关联的父级(或任何具有关联 hasMany 数组的实例)中删除引用

add[Child](String id | { String id, Object child: {String id | Object props } })Number indexNumber

  • 将新创建或现有子级添加到集合中
  • 如果提供父级 ID 作为参数,则将创建具有默认参数的新项
  • 如果提供一个包含父ID和一个包含有效子ID的子对象,子ID将被添加到父集合中(如果子对象属于指定的父对象,则相应的子对象的_parentId属性将被更新)。
  • 如果提供一个包含父ID和一个包含props对象的子对象,将使用提供的属性创建一个新的子对象。

示例

给定一个hasMany "Card"的"Deck"模型,创建一张新卡片并将其添加到包含提供的foo属性的牌组中。

await Deck.addCard({ id: "0xparentId", child: { props: { foo: 'bar' } }})

remove[Child](String parentId | { String id, Object child: {String id, Boolean delete } })Boolean success

  • 从集合中删除并(可选)删除一个子对象
  • 如果提供父ID作为参数,则最后一个子对象将从父的hasMany集合中弹出,但不会被删除。
  • 如果提供一个包含父ID和包含id的子对象,则将从父的hasMany集合中删除该子对象。
  • 如果提供一个包含父ID和包含id属性和标记为truedelete属性的子对象,则完全删除该对象。

示例

给定一个hasMany "Card"的"Deck"模型,删除一张卡片并删除它。

await Deck.removeCard({ id: "0xparentId", child: { id: "0xchildId", delete: true }})

reorder[Child]({ String id, String fromIndex, String toIndex })Boolean success

  • 在父对象(或任何hasMany)内重新排序子对象的索引
  • 不会触及子对象集合。

示例

给定一个hasMany "Card"的"Deck"模型,将卡片的索引从0移动到5。

Deck.reorderCard({ id: "0xparentId", fromIndex: 0, toIndex: 5 }})

问题

以下是一些我首次发现这个项目时可能会有的问题

存储在state对象中的项是否存储在数据库中?

不是。只有属性将被添加到数据库中。vuex状态、突变和getter与数据库是分开的。

数据库属性在模块的state对象或通过vuex getter中可见吗?

不是。所有数据库事务,包括数据检索,都应使用提供的操作完成。

我可以在自定义ORM操作中访问状态、突变和getter吗?

是的!这是在您的数据库操作和应用程序状态之间通信的推荐方式。

当我使用有效的ID使用getById时,我没有得到任何东西,为什么?

一个常见的原因是您正在使用错误的模块的getById。ORM是类型敏感的。注意定义保留的ORM属性(例如_typeid_parentId)。如果其他所有方法都失败了,那么ORM真的不是那么庞大或难以阅读。所以如果有什么感觉不对劲,那么找到原因应该不会太难。我上面介绍的所有约定都非常严格,所以请确保按照说明精确地输入所有内容。

您对这个内容进行单元测试吗?

是的,事实上我有大约99.9%的覆盖率,但由于我在一个私有项目中使用了这个代码的集成版本,我恐怕无法发布测试,因为它们都是基于我现有的源代码的存根。你只能相信我。简而言之,我使用这些东西,并且它们在我的构建中都能正确工作。

您对这个项目有路线图吗?

现在还没有,它似乎是随着我正在工作的代码有机地成长的。当我看到我想让它做的事情时,我想其他人也会有同样的热情!话虽如此,我愿意接受建议(尽管我可能不会立即处理它们!)