diff --git a/README.md b/README.md
index 91d2dacd..996f5bd7 100644
--- a/README.md
+++ b/README.md
@@ -5,18 +5,15 @@
Vuex ORM
+
+
+
-
-
-
-
-
-
@@ -32,7 +29,7 @@ Vuex ORM is heavily inspired by Redux recipe of ["Normalizing State Shape"](http
Super Love Sponsors
-
+
@@ -48,7 +45,7 @@ Vuex ORM is heavily inspired by Redux recipe of ["Normalizing State Shape"](http
-
+
Big Love Sponsors
@@ -131,7 +128,7 @@ Compile files and generate bundles in `dist` directory.
$ yarn lint
```
-Lint files using a rule of Standard JS.
+Lint files using [Prettier](https://prettier.io/).
```bash
$ yarn test
diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index 57ba590f..f047b015 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -69,7 +69,16 @@ const sidebars = {
title: 'Model',
collapsable: false,
children: [
- '/api/model/model'
+ '/api/model/model',
+ '/api/model/attributes',
+ '/api/model/lifecycle-hooks'
+ ]
+ },
+ {
+ title: 'Query',
+ collapsable: false,
+ children: [
+ '/api/query/query'
]
}
]
@@ -77,7 +86,7 @@ const sidebars = {
module.exports = {
title: 'Vuex ORM',
- description: 'The Vuex plugin to enable Object-Relational Mapping access to the Vuex Store.',
+ description: 'The Vuex plugin to enable Object-Relational Mapping (ORM) access to the Vuex Store.',
base: '/',
diff --git a/docs/api/container/container.md b/docs/api/container/container.md
index 8688f58c..7d49ce42 100644
--- a/docs/api/container/container.md
+++ b/docs/api/container/container.md
@@ -4,13 +4,13 @@ sidebarDepth: 2
# Container
-The Container is the global object that holds Vuex Store instance. The only purpose of the Container is to hold store instance so that we can use it in other places. Please refer to [Database](/api/database/database) to see why we need this global object in the first place.
+The Container holds the Vuex Store instance. The only purpose of the Container is to hold store instance so that it can be used throughout the library. It is tightly coupled with the [Database](/api/database/database) instance.
## Static Properties
-### store
+### `store`
-- **`static store: Vuex.Store`**
+- **Type**: `Vuex.Store`
The store instance that Vuex ORM is being installed.
@@ -18,12 +18,12 @@ The Container is the global object that holds Vuex Store instance. The only purp
### register
-- **`static register(store: Vuex.Store): void`**
+- **Type**: `(store: Vuex.Store) => void`
Register a store instance to the Container.
```js
- Container.register(store)
+ Container.register(new Vuex.Store())
```
- This method is called during the Vuex plugin installation, so usually, you would never have to use this method.
+ This method is called during the Vuex plugin installation.
diff --git a/docs/api/database/database.md b/docs/api/database/database.md
index f26fa97c..a20e3126 100644
--- a/docs/api/database/database.md
+++ b/docs/api/database/database.md
@@ -4,45 +4,13 @@ sidebarDepth: 2
# Database
-The Database is the object that holds all Models and Modules that are registered to the Vuex ORM. It is also responsible for generating the whole database relational schema from registered models. This schema is used to "Normalize" data before persisting to the Vuex Store.
+The Database holds all models and modules registered to Vuex ORM. It is also responsible for generating the database relational schema from the registered models. This schema is used to "Normalize" data before persisting to the Vuex Store.
-When using Vuex ORM, you will unlikely need to use the Database class after it's registered to the Vuex Store. But for those who are curious, we'll describe why the Database object exists in the first place.
+In Vuex ORM, models may have relations with other models. To resolve those relations, models require some form of tracking in order to communicate with one another.
-In Vuex ORM, any Model can have any type of relationship with other Models. To resolve those relationships, we need to store all Models somewhere so that a Model can reference each other. That's where the Database comes in to play. You can get any registered Model like this.
+The Database is designed to track all registered models, build relational schema from those models, and register the database to the Vuex Store instance (contained in [Container](/api/container/container)).
-```js
-const database = new Database()
-
-database.register(User)
-
-const user = database.model('users')
-```
-
-You might wonder why do we need to store all Models in one place since the related Models are passed at Model when defining relationships like below.
-
-```js
-import { Model } from '@vuex-orm/core'
-import Post from './Post'
-
-class User extends Model {
- static entity = 'users'
-
- static fields () {
- return {
- id: this.attr(null),
- posts: this.hasMany(Post, 'user_id') // <- Passing related Model.
- }
- }
-}
-```
-
-So, can't we just resolve relationship directly from the Model? Unfortunately no, we can't. The primary reason is that Vuex ORM is built on top of Vuex, and Vuex ORM is calling Vuex Getters/Actions/Mutations to interact with the Vuex Store. In fact, you can call Vuex Actions directly to create or fetch data.
-
-Vuex Module doesn't have access to Model. It must resolve the Model from the entity name, which is a `string`. When a user calls actions like `store.dispatch('entities/users/insert', { ... })`, we must somehow get User Model by the namespace, which is `users` in `entities/users/insert`. Well, Vuex ORM actions are getting Models from the Database.
-
-Finally, the created Database instance is registered to the Vuex Store instance, then it's registered to the [Container](../container/container) so we have access to it from everywhere.
-
-You can access the database instance through the store instance, or Container.
+The database instance can be accessed through the store instance, or [Container](/api/container/container).
```js
// Through the store instance.
@@ -54,77 +22,69 @@ import { Container } from '@vuex-orm/core'
Container.store.$db()
```
+
## Instance Properties
-### store
+### `store`
-- **`store!: Vuex.Store`**
+- **Type**: `Vuex.Store`
The Vuex Store instance.
-### namespace
+### `namespace`
-- **`namespace!: string`**
+- **Type**: `string = 'entities'`
- The namespace of the Vuex Store Module where all entities are registered under. the default is `entities`.
+ The namespace of the Vuex Store module where all entities are registered under.
+
+ The default namespace is `entities` and can be configured during setup.
-### entities
+- **See also**: [Changing the Namespace](/guide/model/database-registration.md#changing-the-namespace)
-- **`entities: Entity[] = []`**
+### `entities`
- The list of entities registered to the Vuex Store. It contains models and modules with its name. The Entity interface looks like below.
+- **Type**: `Array`
- ```ts
- interface Entity {
- name: string
- base: string
- model: typeof Model
- module: Vuex.Module
- }
- ```
+ The collection of entities registered to the Vuex Store. It contains references to the models and corresponding modules.
-### schema
+### `schema`
-- **`schemas: Schemas = {}`**
+- **Type**: `Object`
- The database schema definition. This schema is going to be used when normalizing the data before persisting them to the Vuex Store. Schemas interface is a list of Normalizr schema.
+ The database schema definition. This schema is used when normalizing data before persisting it to the Vuex Store.
- ```ts
- interface Schemas {
- [entity: string]: NormalizrSchema.Entity
- }
- ```
## Instance Methods
-### register
+### `register`
-- **`register(model: typeof Model, module: Vuex.Module = {}): void`**
+- **Type**: `(model: Model, module: Object) => void`
- Register a model and a module to Database.
+ Register a model and a Vuex module to the Database.
```js
- database.register(User, users)
- ```
+ const database = new Database()
- You can omit registering a module.
+ // With a Vuex module.
+ database.register(User, users)
- ```js
+ // Without a Vuex module.
database.register(User)
```
-### start
+### `start`
-- **`start (store: Vuex.Store, namespace: string): void`**
+- **Type**: `(store: Vuex.Store, namespace: string) => void`
- This method will generate Vuex Module and Normalizr schema tree from the registered Models. It will be called when adding Vuex ORM to Vuex as a plugin.
+ Generate Vuex Module and Normalizr schema tree from the registered models. This method is invoked when adding Vuex ORM to Vuex as a plugin.
-### model
+### `model`
-- **`model(model: T): T`**
- **`model(model: string): typeof Model`**
+- **Type**: `(model: Model | string): Model`
- Get the model of the given name from the entities list. It is going to through error if the model was not found.
+ Get the model by entity from the entities list. If a model is passed as the argument, then the model [entity](/api/model/model.md#entity) will be used.
+
+ Throws an error if the model is not found.
```js
const user = database.model('users')
@@ -132,11 +92,11 @@ Container.store.$db()
// User
```
-### models
+### `models`
-- **`models (): { [name: string]: typeof Model }`**
+- **Type**: `() => Object`
- Get all models from the entities list. The result will be object with key being the entity name for the Model.
+ Get all models from the entities list. The result will be a plain object with key being the entity name for the model.
```js
const models = database.model()
@@ -144,21 +104,26 @@ Container.store.$db()
// { users: User, posts: Post }
```
-### baseModel
+### `baseModel`
-- **`baseModel(model: T): T`**
- **`baseModel(model: string): typeof Model`**
+- **Type**: `(model: Model | string) => Model`
- Get the base model of the given name from the entities list. The base Model is only relevant when the model is inheriting another model to achieve Single Table inheritance feature. It is going to through error if the model was not found.
+ Get the base model by entity from the entities list. If a model is passed as the argument, then the model [baseEntity](/api/model/model.md#baseentity) will be used.
+
+ The `baseModel` is only relevant when the model is inheriting another model to achieve Single Table Inheritance.
+
+ Throws an error if the model is not found.
-### module
+### `module`
+
+- **Type**: `(name: string) => Vuex.Module`
-- **`module (name: string): Vuex.Module`**
+ Get the module by entity from the entities list.
- Get the module of the given name from the entities list. It is going to through error if the module was not found.
+ Throws an error if the model is not found.
-### modules
+### `modules`
-- **`modules (): { [name: string]: Vuex.Module }`**
+- **Type**: `() => Object`
- Get all modules from the entities list. The result will be object with key being the entity name for the Module.
+ Get all modules from the entities list. The result will be a plain object with key being the entity name for the module.
diff --git a/docs/api/deleting-data.md b/docs/api/deleting-data.md
deleted file mode 100644
index 892150c5..00000000
--- a/docs/api/deleting-data.md
+++ /dev/null
@@ -1,145 +0,0 @@
-# Deleting Data
-
-You can delete data from the store by calling the delete Method on the model class or dispatching the `delete` action. Both expecting the first argument to be `String`, `Number`, `Function` or `Object`.
-
-If you use `String` or `Number`, a record that matches the condition with its primary key is going to be deleted.
-
-If you use `Function`, that function is going to be used to determine which record to delete. The function takes the record as the argument and must return boolean.
-
-By passing in an `Object` as argument the object is expacted to have a `where` key which have to be `String`, `Number` or `Function` exactly like described above.
-
-## Delete Data By Primary Key Value
-
-```js
-// Initial state.
-let state = {
- entities: {
- users: {
- '1': { id: 1, name: 'John' },
- '2': { id: 1, name: 'Jane' }
- }
- }
-}
-
-
-// Delete single data by primary key value with model class.
-User.delete(1);
-
-// Or you can pass obejct as argument as well.
-User.delete({ where: 1 })
-
-// Or you can delete data from an existing model instance.
-const user = await User.find(1)
-user.delete()
-
-// Or you can delete single data by primary key value with vuex action.
-store.dispatch('entities/users/delete', 1)
-
-// Or you can pass obejct as argument as well.
-store.dispatch('entities/users/delete', { where: 1 })
-
-// State after `delete`
-state = {
- entities: {
- users: {
- '2': { id: 1, name: 'Jane' }
- }
- }
-}
-```
-
-## Delete Data By Closure
-
-```js
-// Initial state.
-let state = {
- entities: {
- users: {
- '1': { id: 1, name: 'John' },
- '2': { id: 1, name: 'Jane' },
- '3': { id: 1, name: 'George' }
- },
- posts: {
- '1': { id: 1, user_id: 1 },
- '2': { id: 2, user_id: 2 },
- '3': { id: 3, user_id: 3 }
- }
- }
-}
-
-// Delete data by closure.
-User.delete((record) => {
- return record.id === 1 || record.name === 'Jane'
-})
-
-// Or with object style.
-User.delete({
- where (record) {
- return record.id === 1 || record.name === 'Jane'
- }
-})
-
-// State after `delete`.
-state = {
- entities: {
- users: {
- '3': { id: 1, name: 'George' }
- },
- posts: {
- '1': { id: 1, user_id: 1 },
- '2': { id: 2, user_id: 2 },
- '3': { id: 3, user_id: 3 }
- }
- }
-}
-```
-
-## Delete All Data
-
-You can delete all data in once by `deleteAll` action.
-
-```js
-// Delete all data for an entity
-User.deleteAll()
-
-// State after `deleteAll`.
-let state = {
- entities: {
- users: {},
- posts: {
- '1': { id: 1, user_id: 1 },
- '2': { id: 2, user_id: 2 },
- '3': { id: 3, user_id: 3 }
- }
- }
-}
-
-// Delete all data for all entities
-store.dispatch('entities/deleteAll')
-
-// State after `deleteAll`.
-state = {
- entities: {
- users: {},
- posts: {}
- }
-}
-```
-
-## Dispatch Action From Root Module
-
-You can also dispatch action from root module. Remember to pass `entity` name in this case.
-
-```js
-store.dispatch('entities/delete', {
- entity: 'users',
- where: 1
-})
-```
-
-Note that since root module needs `entity`, you can't dispatch root module action by passing condition directly.
-
-```js
-// This would not work.
-store.dispatch('entities/delete', 1)
-```
diff --git a/docs/api/model/attributes.md b/docs/api/model/attributes.md
new file mode 100644
index 00000000..44b9ef27
--- /dev/null
+++ b/docs/api/model/attributes.md
@@ -0,0 +1,294 @@
+# Attributes
+
+## Field Attributes
+
+Field attributes aid in the handling of default values and casting. Generic
+types, primitive types and UID types are supported.
+
+Any field types that allow `null` values may be chained with `nullable()` to
+prevent casting. For example, a null value on a number attribute will resolve
+as `0` which may be an undesired outcome.
+
+```js
+{
+ active: this.number(1).nullable()
+}
+```
+
+**See also**: [Defining Models: Field Attributes](/guide/model/defining-models.md#field-attributes)
+
+### `attr`
+
+- **Type**: `(value: any, mutator?: () => any) => Object`
+
+ Create a generic attribute. Any value can be passed as the default value.
+
+ A closure may also be passed:
+
+ ```js
+ {
+ rand: this.attr(() => Math.random())
+ }
+ ```
+
+### `string`
+
+- **Type**: `(value: any, mutator?: () => string | null) => Object`
+
+ Create a string attribute. Values will be cast to [String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String).
+
+### `number`
+
+- **Type**: `(value: any, mutator?: () => number | null) => Object`
+
+ Create a number attribute. Values will be cast to [Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number).
+
+### `boolean`
+
+- **Type**: `(value: any, mutator?: () => boolean | null) => Object`
+
+ Create a boolean attribute. Values will be cast to [Boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean).
+
+### `uid`
+
+- **Type**: `(value?: () => string | number) => Object`
+
+ Create a Unique ID attribute. When a field of this type is omitted it will
+ generate an auto-incremented number with a `$uid` prefix (e.g. `$uid32`).
+
+ A closure may also be passed:
+
+ ```js
+ import { v4 as uuidv4 } from 'uuid'
+
+ {
+ id: this.uid(() => uuidv4()) // '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
+ }
+ ```
+
+
+## Relation Attributes
+
+### `hasOne`
+
+- **Type**: `(related: Model | string, foreignKey: string, localKey?: string) => Object`
+
+ Create a 1:1 relation where two models may have only one record on
+ either side of the relationship connected by a single foreign key.
+ For example, a `User` may own a single `Post`.
+
+ This relation may be reversed using the [belongsTo](#belongsto) attribute.
+
+ | Arguments | |
+ |---------------|----------------------------------------------------------------|
+ | `related`* | The target model e.g. `Post` or `'posts'`. |
+ | `foreignKey`* | The foreign key on the source model e.g. `user_id`. |
+ | `localKey` | The custom key on the source model it's foreign key points to. |
+
+- **See also**: [Relationships: One To One](/guide/model/relationships.md#one-to-one)
+
+### `belongsTo`
+
+- **Type**: `(parent: Model | string, foreignKey: string, ownerKey?: string) => Object`
+
+ Create a one-to-one/many inverse relation where a single record
+ belongs to one or more source models connected by a single foreign key.
+ For example, a single `Profile` may belong to one or more `User` records.
+
+ This relation attribute may be used to create an inverse relation of
+ [hasOne](#hasone) and [hasMany](#hasmany).
+
+ | Arguments | |
+ |---------------|----------------------------------------------------------------|
+ | `parent`* | The source model e.g. `User` or `'users'`. |
+ | `foreignKey`* | The foreign key on the target model e.g. `user_id`. |
+ | `ownerKey` | The custom key on the target model it's foreign key points to. |
+
+- **See also**: [Relationships: One To One Inverse](/guide/model/relationships.md#one-to-one-inverse), [One To Many Inverse](/guide/model/relationships.md#one-to-many)
+
+### `hasMany`
+
+- **Type**: `(related: Model | string, foreignKey: string, localKey?: string) => Object`
+
+ Create a one-to-many relation where a single model may own any amount of
+ records belonging to another model.
+ For example, a `Post` may have an infinite number of `Comment` records.
+
+ | Arguments | |
+ |---------------|----------------------------------------------------------------|
+ | `related`* | The target model e.g. `Comment` or `'comments'`. |
+ | `foreignKey`* | The foreign key on the source model e.g. `post_id`. |
+ | `localKey` | The custom key on the source model it's foreign key points to. |
+
+ This relation may be reversed using the [belongsTo](#belongsto) attribute.
+
+- **See also**: [Relationships: One To Many](/guide/model/relationships.md#one-to-many)
+
+### `hasManyBy`
+
+- **Type**: `(parent: Model | string, foreignKey: string, ownerKey?: string) => Object`
+
+ Create a one-to-many relation where a single model may own many records on
+ another model by declaring a field on the target model that contains an array
+ of foreign keys to the source model.
+ For example, a `Cluster` may own any amount of `Node` records who's foreign
+ keys are declared as an array on the `Cluster` model.
+
+ | Arguments | |
+ |---------------|-----------------------------------------------------------------|
+ | `parent`* | The source model e.g. `Node` or `'nodes'`. |
+ | `foreignKey`* | The foreign keys on the target model e.g. `node_ids`. |
+ | `ownerKey` | The custom key on the source model it's foreign keys points to. |
+
+- **See also**: [Relationships: Has Many By](/guide/model/relationships.md#has-many-by)
+
+### `hasManyThrough`
+
+- **Type**: `(related: Model | string, through: Model | string, firstKey: string, secondKey: string, localKey?: string, secondLocalKey?: string) => Object`
+
+ Create a one-to-many intermediate relation where a single model can own any
+ amount of records attached to another model through an intermediate relation.
+ For example, a `Country` can have many distant `Post` records through a `User`.
+ The `User` may belong to a `Country` and the `Post` records belong to a `User`.
+
+ | Arguments | |
+ |------------------|----------------------------------------------------------------------|
+ | `related`* | The source model e.g. `Post` or `'posts'`. |
+ | `through`* | The intermediate model e.g. `User` or `'users'` |
+ | `firstKey`* | The foreign key on the intermediate model e.g. `country_id`. |
+ | `secondKey`* | The foreign key on the source model e.g. `user_id` |
+ | `localKey` | The custom key on the target model it's foreign key points to. |
+ | `secondLocalKey` | The custom key on the intermediate model it's foreign key points to. |
+
+- **See also**: [Relationships: Has Many Through](/guide/model/relationships.md#has-many-through)
+
+### `belongsToMany`
+
+- **Type**: `(related: Model | string, pivot: Model | string, foreignPivotKey: string, relatedPivotKey: string, parentKey?: string, relatedKey?: string) => Object`
+
+ Create a many-to-many relation where two models may own any amount of records
+ on either side of the relationship connected by an intermediate model.
+ For example, a `User` may own many `Role` records and `Role` may belong to
+ many `User` records.
+
+ The intermediate model connects the two models by a foreign key on the source
+ and target model.
+
+ | Arguments | |
+ |--------------------|----------------------------------------------------------------------|
+ | `related`* | The source model e.g. `Role` or `'roles'`. |
+ | `pivot`* | The intermediate model e.g. `RoleUser` or `'roleUser'` |
+ | `foreignPivotKey`* | The foreign key on the target model e.g. `user_id`. |
+ | `relatedPivotKey`* | The foreign key on the source model e.g. `role_id` |
+ | `parentKey` | The custom key on the target model it's foreign key points to. |
+ | `relatedKey` | The custom key on the intermediate model it's foreign key points to. |
+
+- **See also**: [Relationships: Many To Many](/guide/model/relationships.md#many-to-many)
+
+### `morphOne`
+
+- **Type**: `(related: Model | string, id: string, type: string, localKey?: string) => Object`
+
+ Create a one-to-one polymorphic relation where a single model may own a single
+ record on more than one type of model connected by a single target model.
+ For example, `User` and `Post` may own a single `Comment` scoped through
+ `Commentable`.
+
+ This relation attribute behaves similar to [hasOne](#hasone) but without
+ the model type constraint.
+
+ | Arguments | |
+ |------------|---------------------------------------------------------------------------|
+ | `related`* | The target model e.g. `Comment` or `'comments'`. |
+ | `id`* | The attribute to hold the target model foreign key e.g. `commentable_id`. |
+ | `type`* | The attribute to hold the target model entity e.g. `commentable_type`. |
+ | `localKey` | The custom key on the target model it's foreign key points to. |
+
+- **See also**: [Relationships: One To One (Polymorphic)](/guide/model/relationships.md#one-to-one-polymorphic)
+
+### `morphTo`
+
+- **Type**: `(id: string, type: string) => Object`
+
+ Create a one-to-one or one-to-many polymorphic inverse relation where a
+ single record may belong to more than one type of model connected by a single
+ target model.
+ For example, a `Comment` may belong to `User` and `Post` scoped through
+ `Commentable`.
+
+ This relation attribute behaves similar to [belongsTo](#belongsto) but without
+ the model type constraint. It may be used to create an inverse relation
+ of [morphOne](#morphone) and [morphMany](#morphmany).
+
+ | Arguments | |
+ |-----------|---------------------------------------------------------------------------|
+ | `id`* | The attribute holding the source model foreign key e.g. `commentable_id`. |
+ | `type`* | The attribute holding the source model entity e.g. `commentable_type`. |
+
+- **See also**: [Relationships: One To One (Polymorphic)](/guide/model/relationships.md#one-to-one-polymorphic)
+
+### `morphMany`
+
+- **Type**: `(related: Model | string, id: string, type: string, localKey?: string) => Object`
+
+ Create a one-to-many polymorphic relation where models of more than one type
+ may own many records on a single model connected by a single target model.
+ For example, `Video` and `Post` may own an infinite number of `Comment` records
+ scoped through `Commentable`.
+
+ This relation attribute behaves similar to [hasMany](#hasmany) but without
+ the model type constraint.
+
+ | Arguments | |
+ |------------|---------------------------------------------------------------------------|
+ | `related`* | The target model e.g. `Comment` or `'comments'`. |
+ | `id`* | The attribute to hold the target model foreign key e.g. `commentable_id`. |
+ | `type`* | The attribute to hold the target model entity e.g. `commentable_type`. |
+ | `localKey` | The custom key on the target model it's foreign key points to. |
+
+- **See also**: [Relationships: One To Many (Polymorphic)](/guide/model/relationships.md#one-to-many-polymorphic)
+
+### `morphToMany`
+
+- **Type**: `(related: Model | string, pivot: Model | string, relatedId: string, id: string, type: string, parentKey?: string, relatedKey?: string) => Object`
+
+ Create a many-to-many polymorphic relation where more than one type of model
+ may own any number of records on the target model connected by an intermediate model.
+ For example, an infinite number of `Tag` may belong to any amount of `User`
+ and `Post` records scoped through `Taggable`.
+
+ This relation attribute behaves similar to [belongsToMany](#belongstomany)
+ but without the model type constraint.
+
+ | Arguments | |
+ |--------------|-----------------------------------------------------------------------|
+ | `related`* | The source model e.g. `Tag` or `'tags'` |
+ | `pivot`* | The intermediate model e.g. `Taggable` |
+ | `relatedId`* | The attribute to hold the source model foreign key e.g. `tag_id` |
+ | `id`* | The attribute to hold the target model foreign key e.g. `taggable_id` |
+ | `type`* | The attribute to hold the target model entity e.g. `taggable_type` |
+ | `parentKey` | The custom key on the target model it's foreign key points to. |
+ | `relatedKey` | The custom key on the source model it's foreign key points to. |
+
+- **See also**: [Relationships: Many To Many (Polymorphic)](/guide/model/relationships.md#many-to-many-polymorphic)
+
+### `morphedByMany`
+
+- **Type**: `(related: Model | string, pivot: Model | string, relatedId: string, id: string, type: string, parentKey?: string, relatedKey?: string) => Object`
+
+ Create a many-to-many polymorphic inverse relation where a record on the
+ target model belongs to more than one type of source model connected by an
+ intermediate model.
+ For example, `Tag` may belong to many `Post` and `User` scoped through `Taggable`.
+
+ | Arguments | |
+ |--------------|-----------------------------------------------------------------------|
+ | `related`* | The target model e.g. `Post` or `'posts'` |
+ | `pivot`* | The intermediate model e.g. `Taggable` |
+ | `relatedId`* | The attribute to hold the source model foreign key e.g. `tag_id` |
+ | `id`* | The attribute to hold the target model foreign key e.g. `taggable_id` |
+ | `type`* | The attribute to hold the target model entity e.g. `taggable_type` |
+ | `parentKey` | The custom key on the source model it's foreign key points to. |
+ | `relatedKey` | The custom key on the target model it's foreign key points to. |
+
+- **See also**: [Relationships: Many To Many Inverse (Polymorphic)](/guide/model/relationships.md#defining-the-inverse-of-the-relationship-2)
diff --git a/docs/api/model/lifecycle-hooks.md b/docs/api/model/lifecycle-hooks.md
new file mode 100644
index 00000000..d4fde0d7
--- /dev/null
+++ b/docs/api/model/lifecycle-hooks.md
@@ -0,0 +1,105 @@
+# Lifecycle Hooks
+
+Vuex ORM triggers several lifecycle hooks while interacting with the store. Lifecycle hooks allow interaction with particular points in a query lifecycle, executing code each time a record is saved, updated or retrieved from the store.
+
+Lifecycle hooks can be configured on models as static methods. Hooks registered on models are only triggered when they interact with the store.
+
+There are two forms of lifecycle hooks. [Select Lifecycle Hooks](#select-hooks) and [Mutation Lifecycle Hooks](#mutation-hooks) both of which offer several hooks during store interaction.
+
+**Example**:
+
+```js
+class User extends Model {
+ static entity = 'users'
+
+ static fields () {
+ /* ... */
+ }
+
+ static beforeSelect (records) {
+ return users.filter(user => user.admin !== 'admin')
+ }
+
+ static beforeCreate (record) {
+ record.published = true
+ }
+}
+```
+
+::: tip
+Lifecycle hooks are documented in order of execution.
+:::
+
+**See also**: [Global Hooks](/api/query/query.md#hooks)
+
+
+## Select Hooks
+
+Select lifecycle hooks are called when retrieving data from the store. They are triggered at several stages of the retrieval process. Each of these hooks receive an array of model instances that are being processed by a query. The number of instances may vary as it traverses through the selection process.
+
+Select lifecycle hooks **must always return a collection** of model instances.
+
+### `beforeSelect`
+
+- **Type**: `(records: Array) => Array`
+
+ Triggered at the very beginning of the retrieving process before any query filters are applied.
+
+### `afterWhere`
+
+- **Type**: `(records: Array) => Array`
+
+ Triggered after any [where](/api/query/query.md#where) clauses have been applied.
+
+### `afterOrderBy`
+
+- **Type**: `(records: Array) => Array`
+
+ Triggered after any [orderBy](/api/query/query.md#orderby) filters have been applied.
+
+### `afterLimit`
+
+- **Type**: `(records: Array) => Array`
+
+ Triggered after any [limit](/api/query/query.md#limit) and [offset](/api/query/query.md#offset) filters have been applied.
+
+
+## Mutation Hooks
+
+Mutation lifecycle hooks are called when mutating data in the store.
+
+### `beforeCreate`
+
+- **Type**: `(record: Model | Array) => Model | Array | boolean`
+
+ Triggered before records are inserted into the store. Returning `false` will prevent persisting records to the store.
+
+### `afterCreate`
+
+- **Type**: `(record: Model | Array) => void`
+
+ Triggered after records are inserted into the store.
+
+### `beforeUpdate`
+
+- **Type**: `(record: Model | Array) => Model | Array | boolean`
+
+ Triggered before records are updated in the store. Returning `false` will prevent persisting records to the store.
+
+### `afterUpdate`
+
+- **Type**: `(record: Model | Array) => void`
+
+ Triggered after records are updated in the store.
+
+### `beforeDelete`
+
+- **Type**: `(record: Model | Array) => Model | Array | boolean`
+
+ Triggered before records are deleted from the store. Returning `false` will prevent records being deleted from the store.
+
+### `afterDelete`
+
+- **Type**: `(record: Model | Array) => void`
+
+ Triggered after records are deleted from the store.
diff --git a/docs/api/model/model.md b/docs/api/model/model.md
index 0f52581b..04ee769d 100644
--- a/docs/api/model/model.md
+++ b/docs/api/model/model.md
@@ -1,46 +1,236 @@
----
-sidebarDepth: 2
----
-
# Model
+## Static Properties
+
+### `entity`
+
+- **Type**: `string`
+
+ The name of the module under which a model will store entity data.
+
+- **See also**: [Defining Models: Entity Name](/guide/model/defining-models.md#entity-name)
+
+### `baseEntity`
+
+- **Type**: `string`
+
+ The reference to the base entity name if the model extends another model. This is usually defined when using [Single Table Inheritance](/guide/model/single-table-inheritance.md).
+
+### `primaryKey`
+
+- **Type**: `string | Array = 'id'`
+
+ The primary key to be used for the model. This defaults to `id` and can be overridden to suit the needs of your application.
+
+ Composite primary keys can be defined by setting this property as an array of keys.
+
+- **See also**: [Defining Models: Primary Key](/guide/model/defining-models.md#primary-key)
+
+### `typeKey`
+
+- **Type**: `string = 'type'`
+
+ The discriminator key to be used for the model when using [Single Table Inheritance](/guide/model/single-table-inheritance.md).
+
+- **See also**: [Discriminator Field Override](/guide/model/single-table-inheritance.md#discriminator-field)
+
+
## Static Methods
-### store
+### `new`
+
+- **Type**: `() => Promise`
+
+ Create new records with all declared fields filled with their default values.
+
+ Returns a Promise that resolves with the newly created record.
+
+- **See also**: [Inserting & Updating](/guide/data/inserting-and-updating.md#inserting-updating)
+
+### `create`
+
+- **Type**: `(payload: Object | Array, options?: Object) => Promise`
-- **`store(): void`**
+- **Options**: `{ create, insert, update, insertOrUpdate }`
- Get Vuex Store instance.
+ Create records by replacing all existing entity data in the store with the payload provided. This will flush all existing records before inserting new records.
+
+ The `options` argument notifies Vuex ORM on how to handle any relationships. [Details](/guide/data/inserting-and-updating.md#insert-method-for-relationships).
+
+ Returns a Promise that resolves with created records.
+
+- **See also**: [Inserting & Updating](/guide/data/inserting-and-updating.md#inserting-updating)
+
+### `insert`
+
+- **Type**: `(payload: Object | Array, options?: Object) => Promise`
+
+- **Options**: `{ create, insert, update, insertOrUpdate }`
+
+ Insert records by adding new records and replacing any existing records that
+ match the primary key of the records provided in the payload.
+
+ The `options` argument notifies Vuex ORM on how to handle any relationships. [Details](/guide/data/inserting-and-updating.md#insert-method-for-relationships).
+
+ Returns a Promise that resolves with inserted records.
+
+- **See also**: [Inserting & Updating](/guide/data/inserting-and-updating.md#inserting-updating)
+
+### `update`
+
+- **Type**: `(payload: Object | Array | (model) => void, options?: Object) => Promise`
+
+- **Condition**: `{ where: number | string | (model) => boolean }`
+
+- **Options**: `{ create, insert, update, insertOrUpdate }`
+
+ Update existing records that matches the primary key against the `where`
+ condition. In the absence of a condition, records will be matched
+ against primary keys in the payload itself.
+
+ The `where` condition may also be a closure for pragmatic conditionals.
+
+ ```js
+ User.update(
+ { name: 'Johnny Doe' },
+ { where: (user) => user.name === 'John Doe' }
+ )
+ ```
+
+ A closure may also be passed as the payload, only when accompanied by a `where`
+ condition, as an alternate syntax for handling record data.
```js
- const store = User.store()
+ User.update((user) => { user.name = 'Johnny Doe' }, where: 1 })
```
-### dispatch
+ Additional `options` provided notifies Vuex ORM on how to handle any relationships. [Details](/guide/data/inserting-and-updating.md#insert-method-for-relationships).
+
+ Returns a Promise that resolves with updated records.
+
+- **See also**: [Inserting & Updating](/guide/data/inserting-and-updating.md#updates)
+
+### `insertOrUpdate`
+
+- **Type**: `(payload: Object | Array, options?: Object) => Promise`
-- **`dispatch(method: string, payload?: any): Promise`**
+- **Options**: `'create'` | `'insert'` | `'update'` | `'insertOrUpdate'`
- Dispatch a store action. It will generate module namespace automatically.
+ Inserts new records and updates any existing. Similar to [insert](#insert)
+ except the payload is explored for records that require insertion and records
+ that already exist and need only updating.
+
+ Two mutations are committed to the store, one of which inserts records,
+ and the other updates.
+
+ The `options` argument notifies Vuex ORM on how to handle any relationships. [Details](/guide/data/inserting-and-updating.md#insert-method-for-relationships).
+
+ Returns a Promise that resolves with inserted and/or updated records.
+
+- **See also**: [Inserting & Updating](/guide/data/inserting-and-updating.md#insert-or-update)
+
+### `delete`
+
+- **Type**:
+ - `(id: string | number | Array) => Promise`
+ - `(condtion: (model) => boolean): Promise`
+ - `(payload: any): Promise`
+
+ Delete entity records by primary key or condition.
+
+ ```js
+ // Delete by primary key
+ User.delete(1)
+
+ // Delete by composite primary key
+ User.delete([1, 2])
+
+ // Delete by conditional closure
+ User.delete((user) => user.subscription === 'expired')
+ ```
+
+- **See also**: [Deleting](/guide/data/deleting.md)
+
+### `deleteAll`
+
+- **Type**: `() => Promise`
+
+ Delete all entity records from the store.
+
+- **See also**: [Delete All Data](/guide/data/deleting.md#delete-all-data)
+
+### `all`
+
+- **Type**: `() => Array`
+
+ Get all entity records.
+
+- **See also**: [Get All Data](/guide/data/retrieving.md#get-all-data)
+
+### `find`
+
+- **Type**: `(id: string | number | Array) => Object | null`
+
+ Find a record by primary key.
+
+- **See also**: [Get Single Data](/guide/data/retrieving.md#get-single-data)
+
+### `findIn`
+
+- **Type**: `(idList: Array>) => Array`
+
+ Find records by an array of primary keys.
+
+- **See also**: [Get Multiple Data by Primary Keys](/guide/data/retrieving.md#get-multiple-data-by-primary-keys)
+
+### `query`
+
+- **Type**: `() => Query`
+
+ Get a new [Query Builder](/guide/data/retrieving.md#query-builder) instance.
+
+### `store`
+
+- **Type**: `() => Vuex.Store`
+
+ Get the Vuex Store instance.
+
+### `database`
+
+- **Type**: `() => Database`
+
+ Get the database instance from store.
+
+### `dispatch`
+
+- **Type**: `(method: string, payload?: Object, options?: Object) => Promise`
+
+ Dispatches a store action. The module namespace is generated automatically.
```js
- User.dispatch('create', { data: { /* ... */ } })
+ User.dispatch('create', { data: [...] })
```
-### getters
+- **See also**: [Call Module Methods From Model](/guide/digging-deeper/vuex-module.md#call-module-methods-from-model)
+
+### `getters`
-- **`getters(method: string): any`**
+- **Type**: `(method: string) => any`
- Call a getter. It will generate module namespace automatically.
+ Calls a store getter. The module namespace is generated automatically.
```js
- const users = User.$getters('all')()
+ const users = User.getters('all')()
```
-### namespace
+- **See also**: [Calling Getters](/guide/digging-deeper/vuex-module.md#calling-getters)
-- **`namespace(method: string): string`**
+### `namespace`
- Get namespaced string to be used for dispathing actions or calling getters.
+- **Type**: `(method: string) => string`
+
+ Creates a namespaced string respresentation for the given method. Useful when
+ dispatching actions or calling getters.
```js
const method = User.namespace('create')
@@ -48,11 +238,23 @@ sidebarDepth: 2
// 'entities/users/create'
```
-### hydrate
+### `commit`
+
+- **Type**: `(callback: (state: Object) => void) => void`
+
+ Commits a store mutation. The module namespace is generated automatically.
+
+ ```js
+ User.commit((state) => { state.fetching = true })
+ ```
+
+- **See also**: [Mutating State](/guide/digging-deeper/vuex-module.md#mutating-state)
+
+### `hydrate`
-- **`hydrate(record?: Record): Record`**
+- **Type**: `(record?: Object) => Object`
- Fill any missing fields in the given record with the default value defined in the model schema. Note that the returned object is not Model instance, it's plain object.
+ Fill any missing fields in the given record with the default values defined in the model schema. The return type is a plain object.
```js
User.hydrate({ id: 1 })
@@ -60,7 +262,7 @@ sidebarDepth: 2
// { id: 1, name: 'Default Name' }
```
- If you pass relational data, those will be hydrated as well.
+ Relational data will also be hydrated if it exists.
```js
User.hydrate({
@@ -83,123 +285,208 @@ sidebarDepth: 2
*/
```
- > **NOTE:** `hydrate` method will not "normalize" the given data. It will fill any missing field, but it wouldn't attach correct id value to the foreign field, for example adding `id` value of the user to the `user_id` field of the post, or increment the value specified by the `uid` attribute.
-
-## Instance Methods
+ ::: tip
+ Hydrating will not "normalize" the given data. It will fill any missing
+ fields, but it will not attach correct id value to the foreign field, for
+ example adding `id` value of the user to the `user_id` field of the post, or
+ increment the value specified by the `uid` attribute.
+ :::
-### $store
+### `getFields`
-- **`$store(): void`**
+- **Type**: `() => Object`
- Get Vuex Store instance.
+ Get the fields definition of the model and its relations.
```js
- const user = new User()
+ class User extends Model {
+ static entity = 'users'
- const store = user.$store()
+ static fields () {
+ return {
+ id: this.attr(null),
+ name: this.attr('John Doe')
+ }
+ }
+ }
+
+ const fields = (new User()).$fields()
+
+ /*
+ {
+ id: {
+ value: null, // default value
+ ...
+ },
+ name: {
+ value: John Doe, // default value
+ ...
+ }
+ }
+ */
```
-### $dispatch
-- **`$dispatch(method: string, payload?: any): Promise`**
+## Instance Properties
- Dispatch a store action. It will generate module namespace automatically.
+### `$id`
- ```js
- const user = new User()
+- **Type**: `string | null`
- user.$dispatch('create', { data: { /* ... */ } })
- ```
+ Stores the value of the primary key. This is the generated index identifier for a
+ record and returns a string representation of the primary key.
-### $getters
+## Instance Methods
-- **`$getters(method: string): any`**
+### `$create`
- Call a getter. It will generate module namespace automatically.
+- **Type**: `(payload: Object | Array, options?: Object) => Promise`
- ```js
- const user = new User()
+- **Options**: `{ create, insert, update, insertOrUpdate }`
- const users = user.$getters('all')()
- ```
+ The instance method of [create()](#create).
+
+### `$insert`
+
+- **Type**: `(payload: Object | Array, options?: Object) => Promise`
+
+- **Options**: `{ create, insert, update, insertOrUpdate }`
+
+ The instance method of [insert()](#insert).
+
+### `$update`
+
+- **Type**: `(payload: Object | Array | Function, options?: Object) => Promise`
+
+- **Condition**: `{ where: number | string | (record) => boolean }`
+
+- **Options**: `{ create, insert, update, insertOrUpdate }`
+
+ The instance method of [update()](#update) with one exception:
+
+ - Omitting a condition and primary key from the payload results in `where`
+ using the primary key from the instance itself.
-### $namespace
+ ```js
+ const user = User.find(1)
-- **`$namespace(method: string): string`**
+ user.$update({ name: 'Johnny Doe'})
+ ```
- Get namespaced string to be used for dispathing actions or calling getters.
+ is equvalent to:
+
+ ```js
+ User.update({ name: 'Johnny Doe' }, { where: 1 })
+ ```
+
+### `$insertOrUpdate`
+
+- **Type**: `(payload: Object | Array, options?: Object) => Promise`
+
+- **Options**: `'create'` | `'insert'` | `'update'` | `'insertOrUpdate'`
+
+ The instance method of [insertOrUpdate()](#insertorupdate).
+
+### `$save`
+
+- **Type**: `() => Promise`
+
+ Persist the current instance to the store. This method uses the `insertOrUpdate`
+ action internally and it is advised to use `$save()` where the model has been
+ instantiated using the `new` operator.
```js
const user = new User()
- const method = user.$namespace('create')
+ user.name = 'John Doe'
- // 'entities/users/create'
+ user.$save()
```
-### $fields
+- **See also**: [Save Method](/guide/data/inserting-and-updating.md#save-method)
-- **`$fields(): Object`**
+### `$delete`
- Get the `fields` object of the model.
+- **Type**: `() => Promise`
- ```js
- import { Model } from '@vuex-orm/core'
+ Delete the current instance record from the store.
- class User extends Model {
- static entity = 'users'
+### `$deleteAll`
- static fields () {
- return {
- id: this.attr(null),
- name: this.attr('John Doe')
- }
- }
- }
+- **Type**: `() => Promise`
- const user = new User()
+ The instance method of [deleteAll()](#deleteall).
- user.$fields()
+### `$all`
- /*
- {
- username: {
- value: null, // default value
- ...
- },
- name: {
- value: John Doe, // default value
- ...
- }
- }
- */
- ```
+- **Type**: `() => Array`
-### $id
+ The instance method of [all()](#all).
-- **`$id(): any`**
+### `$find`
- Get the value of the primary key.
+- **Type**: `(id: string | number | Array) => Object | null`
- ```js
- import { Model } from '@vuex-orm/core'
+ The instance method of [find()](#find).
- class User extends Model {
- static entity = 'users'
+### `$findIn`
- static primaryKey = 'username'
+- **Type**: `(idList: Array>) => Array`
- static fields () {
- return {
- username: this.attr(null),
- name: this.attr('')
- }
- }
- }
+ The instance method of [findIn()](#findin).
- const user = new User({ username: 'john-doe', name: 'John Doe' })
+### `$query`
- user.$id()
+- **Type**: `() => Query`
- // 'john-doe'
- ```
+ The instance method of [query()](#query).
+
+### `$store`
+
+- **Type**: `() => Vuex.Store`
+
+ Get the Vuex Store instance.
+
+### `$dispatch`
+
+- **Type**: `(method: string, payload?: Object, options?: Object) => Promise`
+
+ The instance method of [dispatch()](#dispatch).
+
+### `$getters`
+
+- **Type**: `(method: string) => any`
+
+ The instance method of [getters()](#getters).
+
+### `$namespace`
+
+- **Type**: `(method: string) => string`
+
+ The instance method of [namespace()](#namespace).
+
+### `$fields`
+
+- **Type**: `() => Object`
+
+ The instance method of [getFields()](#getfields).
+
+### `$primaryKey`
+
+- **Type**: `() => string | Array`
+
+ Get the primary key name for the model. Defaults to `id`.
+
+### `$getAttributes`
+
+- **Type**: `() => Object`
+
+ Equivalent to [hydrate](#hydrate) except it is invoked on the current instance.
+
+### `$toJson`
+
+- **Type**: `() => Object`
+
+ Serializes fields and their values into JSON.
+
+- **See also**: [Serialization](/guide/digging-deeper/serialization.md)
diff --git a/docs/api/query/query.md b/docs/api/query/query.md
new file mode 100644
index 00000000..0b4eda0c
--- /dev/null
+++ b/docs/api/query/query.md
@@ -0,0 +1,251 @@
+# Query
+
+Query instances may be obtained by calling the [query](/api/model/model.md#query) method from a model.
+
+**See also**: [Query Builder](/guide/data/retrieving.md#query-builder)
+
+## Filters
+
+### `where`
+
+- **Type**:
+
+ - `(field: string, value: any) => Query`
+ - `(field: string, value: (model) => boolean) => Query`
+ - `(field: (model, query) => void) => Query`
+
+ Add a "where" condition. Subsequent "where" conditions are combined with the `and` operator.
+
+ ```js
+ // Field equality
+ User.query().where('age', '20')
+
+ // Field equality by expression
+ User.query().where('age', (user) => user.age > 20)
+
+ // Expression
+ User.query().where((user) => user.age > 20 && user.sex === 'female')
+
+ // Constraint groups
+ User.query().where('role', 'user').where((model, query) => {
+ query.where('age', '20').orWhere('sex', 'female')
+ })
+ ```
+
+ Closures will receive two arguments - a model instance and a query instance. The query instance allows for adding constraint groups such as, but not limited to, parameter grouping. This type of closure does not require a return value. However, expressions must return a boolean.
+
+ ::: warning NOTE
+ Expression closures require a boolean return type. In such cases returning truthy/falsy, such as `array.length`, `null` or `undefined`, will result in unexpected side-effects ([#402](https://github.com/vuex-orm/vuex-orm/issues/402)). This is a design limitation since constraint closures don't expect a return type.
+ :::
+
+- **See also**: [Where Clauses](/guide/data/retrieving.md#where-clauses)
+
+### `orWhere`
+
+- **Type**:
+
+ - `(field: string, value: any) => Query`
+ - `(field: string, value: (model) => boolean) => Query`
+ - `(field: (model, query) => void) => Query`
+
+ Extend [where()](#where) condition with the `or` operator.
+
+### `whereId`
+
+- **Type**: `(value: number | string) => Query`
+
+ Filter records by primary key.
+
+### `whereIdIn`
+
+- **Type**: `(values: Array) => Query`
+
+ Filter records by an array of primary keys.
+
+### `offset`
+
+- **Type**: `(offset: number) => Query`
+
+ Add an offset expression to skip a certain number of records before beginning to return the result.
+
+### `limit`
+
+- **Type**: `(limit: number) => Query`
+
+ Add a limit expression to constrain the number of records to be returned.
+
+### `orderBy`
+
+- **Type**: `(field: string | (model) => any, direction: string = 'asc') => Query`
+
+- **Directions**: `'asc'` | `'desc'`
+
+ Sort the result set by field. The default sort direction is `asc`.
+
+ A closure may be passed as the first argument which is applied on each result iteration. The closure may return a value to apply as the sorting value.
+
+ ```js
+ // Sort user name by its 3rd character
+ User.query().orderBy(user => user.name[2])
+ ```
+
+- **See also**: [Order By](/guide/data/retrieving.md#order-by)
+
+
+## Relations
+
+By default, Vuex ORM does not query relations unless explicitly told to with the following methods.
+
+**See also**: [Retrieving Relationships](/guide/data/retrieving.md#relationships)
+
+### `with`
+
+- **Type**: `(name: string | Array, constraint: (query) => void | null = null) => Query`
+
+ Eager load relations by attribute name.
+
+ Using dot notation, it is possible to load child relations.
+
+ ```js
+ Users.query().with('posts.comments')
+ ```
+
+ In addition, using a dot notation wildcard makes it possible to load all child relations of a relation.
+
+ ```js
+ Users.query().with('posts.*')
+ ```
+
+ Constraints may be added by passing a closure as the second argument. The closure will receive a query instance as a single argument.
+
+ ```js
+ User.query().with('posts', (query) => {
+ query.where('published', true)
+ })
+ ```
+
+### `withAll`
+
+- **Type**: `() => Query`
+
+ Eager load all relations defined on the model.
+
+### `withAllRecursive`
+
+- **Type**: `(depth: number = 3) => Query`
+
+ Eager load all relations recursively. By default the recursion `depth` is 3 levels.
+
+### `has`
+
+- **Type**: `(relation: string, operator?: string | number, count?: number) => Query`
+
+ Add a conditional constraint based on relation existence.
+
+ Where `count` is supplied as the second argument, relations will be constrained to a number of records it holds.
+
+ Additionally, an operator can be added as the second argument in addition to the `count` as the third argument for a more concise comparison. Supported operators are: `'>'`, `'>='`, `'='`, `'<='`, `'<'`.
+
+ ```js
+ // Filter posts having at least 2 comments
+ Post.query().has('comments', 2)
+
+ // Filter posts having more than 2 comments
+ Post.query().has('comments', '>', 2)
+ ```
+
+### `hasNot`
+
+- **Type**: `(relation: string, operator?: string | number, count?: number) => Query`
+
+ Add a conditional constraint based on relation absence. See [has()](#has) for detail on usage.
+
+### `whereHas`
+
+- **Type**: `(relation: string, constraint: (query) => void) => Query`
+
+ Add a conditional "where" constraint based on relation existence.
+
+ ```js
+ Post.query().whereHas('comments', (query) => {
+ query.where('user_id', 1)
+ })
+ ```
+
+### `whereHasNot`
+
+- **Type**: `(relation: string, constraint: (query) => void) => Query`
+
+ Add a conditional "where" constraint based on relation absence. See [whereHas()](#wherehas) for detail on usage.
+
+
+## Results
+
+### `get`
+
+- **Type**: `() => Array`
+
+ Execute the query chain and return the result.
+
+### `first`
+
+- **Type**: `() => Object | null`
+
+ Execute the query chain and return the first record from the result set.
+
+### `last`
+
+- **Type**: `() => Object | null`
+
+ Execute the query chain and return the last record from the result set.
+
+### `count`
+
+- **Type**: `() => number`
+
+ Execute the query chain and return the total number of records in the result set.
+
+### `min`
+
+- **Type**: `(field: string) => number`
+
+ Execute the query chain and return the [min](https://en.wikipedia.org/wiki/Maxima_and_minima) value of field.
+
+### `max`
+
+- **Type**: `(field: string) => number`
+
+ Execute the query chain and return the [max](https://en.wikipedia.org/wiki/Maxima_and_minima) value of field.
+
+### `sum`
+
+- **Type**: `(field: string) => number`
+
+ Execute the query chain and eturn the [sum](https://en.wikipedia.org/wiki/Summation)
+ value of field.
+
+## Lifecycle Hooks
+
+Registering global lifecycle hooks grants interaction with particular points in a query lifecycle on a global scale. Liefecycle hooks can also be [registered on models](../model/lifecycle-hooks.md). The number of arguments passed to a callback also depends on the type of hook being registered.
+
+**See also**: [Lifecycle Hooks](/guide/digging-deeper/lifecycle-hooks)
+
+### `on`
+
+- **Type**: `(on: string, callback: (model) => boolean | void) => number`
+
+ ```js
+ Query.on('beforeCreate', (record) => [...])
+ ```
+
+ Certain [lifecycle hooks](/guide/digging-deeper/lifecycle-hooks) allow a boolean to be returned to permit or prevent state mutations.
+
+### `off`
+
+- **Type**: `(id: number) => boolean`
+
+ ```js
+ const hookId = Query.on('beforeCreate', (record) => [...])
+
+ Query.off(hookId)
+ ```
diff --git a/docs/guide/data/inserting-and-updating.md b/docs/guide/data/inserting-and-updating.md
index b043b4a7..8938f576 100644
--- a/docs/guide/data/inserting-and-updating.md
+++ b/docs/guide/data/inserting-and-updating.md
@@ -595,7 +595,11 @@ User.insert({
}
```
-However, note that there is a caveat with "Has Many Through" relationship. When creating data that contains "Has Many Through" relationship without intermediate pivot records, the intermediate record will not be generated. Let's say you have the following model definitions.
+### Generating Intermediate Records
+
+When creating data that contains [Has One Through](../model/relationships.md#has-one-through) or [Has Many Through](../model/relationships.md#has-many-through) relations, intermediate records will not be generated without specifying supporting relations that allows Vuex ORM to normalize the data before persisting to the store.
+
+For example, let's take a look at the following `hasManyThrough` relation. The `Country` model can access many `Post` records through a `User` with the support of `hasMany` relations defined on `Country` and `User`.
```js
class Country extends Model {
@@ -634,42 +638,7 @@ class Post extends Model {
}
```
-And then you try to save the following data.
-
-```js
-Country.create({
- data: {
- id: 1,
- posts: [
- { id: 1 },
- { id: 2 }
- ]
- }
-})
-```
-
-Vuex ORM will normalize the data and save them to the store as below.
-
-```js
-{
- countries: {
- data: {
- 1: { id: 1 }
- }
- },
- users: {
- data: {}
- },
- posts: {
- data: {
- 1: { id: 1, user_id: null },
- 2: { id: 2, user_id: null }
- }
- }
-}
-```
-
-See there is no users record, and `user_id` at `posts` becomes empty. This happens because Vuex ORM wouldn't have any idea how post data relate to the intermediate User. Hence if you create data like this, you wouldn't be able to retrieve them by getters anymore. In such cases, it is recommended to create data with the intermediate records.
+By adding the `hasMany` relation attribute on both `Country` and `User`, the following data structure can generate records for all the models required for the `hasManyThrough` relation.
```js
Country.create({
@@ -693,6 +662,10 @@ Country.create({
})
```
+The `users` property will populate `User` model through the `users` attribute defined on the `Country` model. Subsequently, nesting `posts` within a user record will populate the `Post` model through the `posts` attribute defined on the `User` model, thus creating `Post` records. This is all made possible by strategically placing the relevant relations as demonstrated.
+
+The same principle applies to the `hasOneThrough` relation, since it is a 1:1 version of `hasManyThrough`, by replacing `hasMany` with `hasOne` relations.
+
### Get Newly Inserted Data
Both `insert` and `create` return the inserted data as a Promise so that you can get them as a return value. The return value will contain all of the data that was created.
diff --git a/docs/guide/digging-deeper/vuex-module.md b/docs/guide/digging-deeper/vuex-module.md
index aad3de51..675465ff 100644
--- a/docs/guide/digging-deeper/vuex-module.md
+++ b/docs/guide/digging-deeper/vuex-module.md
@@ -109,7 +109,7 @@ const count = store.state.users.count
store.commit('entities/users/add', 3)
```
-## Call Module Methods fom Model
+## Call Module Methods From Model
An alternative to calling store methods directly, you may access the store instance from a model as well to dispatch actions or call getters.
diff --git a/docs/guide/model/relationships.md b/docs/guide/model/relationships.md
index a644619b..e686cd6b 100644
--- a/docs/guide/model/relationships.md
+++ b/docs/guide/model/relationships.md
@@ -434,6 +434,66 @@ user.podcasts.forEach((podcast) => {
})
```
+## Has One Through
+
+The "has-one-through" relationship links models through a single intermediate relation. For example, if each `User` has one `Profile`, and each `Profile` is associated with one `Image`, then the `User` may access `Image` through `Profile`. Let's look at the models required to define this relationship:
+
+```js
+class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ image: this.hasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ }
+ }
+}
+
+class Profile extends Model {
+ static entity = 'profiles'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ user_id: this.attr(null)
+ }
+ }
+}
+
+class Image extends Model {
+ static entity = 'images'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ profile_id: this.attr(null)
+ }
+ }
+}
+```
+
+Though `Image` does not contain a `user_id` attribute, the `hasOneThrough` relation can provide access to the user's profile image to the `User` model. Vuex ORM can accomplish this by inspecting the `user_id` on the intermediary model, in this case `Profile`, which then goes on to matching it with `Image` models it is associated with.
+
+The `hasOneThrough` relation attribute accepts the following arguments:
+
+```js
+this.hasOneThrough(
+ Image, // The final model we wish to access.
+ Profile, // The intermediate "through" model.
+ 'user_id', // Foreign key on the intermediate "through" model.
+ 'profile_id', // Foreign key on the final model.
+ 'local_user_id', // Optional: custom key on the intermediate "through" model.
+ 'local_profile_id' // Optional: custom key on the final model.
+)
+```
+
+::: tip
+When creating data for models containing a `hasOneThrough` relation, you will need to define supporting relations in order to generate intermediate records.
+
+**Further reading**: [Generating Intermediate Records](../data/inserting-and-updating.html#generating-intermediate-records)
+:::
+
## Has Many Through
The "has-many-through" relationship provides a convenient shortcut for accessing distant relations via an intermediate relation. For example, a Country might have many Posts through an intermediate User. In this example, you could easily gather all posts for a given country. Let's look at the models required to define this relationship:
@@ -490,7 +550,11 @@ this.hasManyThrough(
)
```
-> **NOTE:** When creating data that contains `hasManyThrough` relationship without intermediate relation, the intermediate record will not be generated. [See here](../data/inserting-and-updating.html#generating-pivot-records) for more details.
+::: tip
+When creating data for models containing a `hasManyThrough` relation, you will need to define supporting relations in order to generate intermediate records.
+
+**Further reading**: [Generating Intermediate Records](../data/inserting-and-updating.html#generating-intermediate-records)
+:::
## One To One (Polymorphic)
diff --git a/docs/guide/model/single-table-inheritance.md b/docs/guide/model/single-table-inheritance.md
index f1fe6066..9961fbd6 100644
--- a/docs/guide/model/single-table-inheritance.md
+++ b/docs/guide/model/single-table-inheritance.md
@@ -73,7 +73,7 @@ const people = Person.all()
/*
[
Person { id: 1, name: 'John Doe' },
- Adult { id: 2, name: 'Jane Doe', job: 'Software Engineer' }
+ Adult { id: 2, name: 'Jane Doe', job: 'Software Engineer' }
]
*/
```
@@ -127,19 +127,17 @@ Now, you can create mixed types of records at once.
```js
// Creating mixed data.
-Person.insert({
- data: [
- { type:'PERSON', id: 1, name: 'John Doe' },
- { type:'ADULT', id: 2, name: 'Jane Doe', job: 'Software Engineer' }
- ]
-})
+Person.insert([
+ { type: 'PERSON', id: 1, name: 'John Doe' },
+ { type: 'ADULT', id: 2, name: 'Jane Doe', job: 'Software Engineer' }
+])
const people = Person.all()
/*
[
Person { id: 1, name: 'John Doe' },
- Adult { id: 2, name: 'Jane Doe', job: 'Software Engineer' }
+ Adult { id: 2, name: 'Jane Doe', job: 'Software Engineer' }
]
*/
```
@@ -188,19 +186,17 @@ class Adult extends Person {
And now you may use a custom `type` field when inserting data.
```js
-Person.insert({
- data: [
- { person_type: 'PERSON', id: 1, name: 'John Doe' },
- { person_type: 'ADULT', id: 2, name: 'Jane Doe', job: 'Software Engineer' }
- ]
-})
+Person.insert([
+ { person_type: 'PERSON', id: 1, name: 'John Doe' },
+ { person_type: 'ADULT', id: 2, name: 'Jane Doe', job: 'Software Engineer' }
+])
const people = Person.all()
/*
[
Person { id: 1, name: 'John Doe' },
- Adult { id: 2, name: 'Jane Doe', job: 'Software Engineer' }
+ Adult { id: 2, name: 'Jane Doe', job: 'Software Engineer' }
]
*/
```
@@ -247,19 +243,17 @@ Then you can fetch the key with its results.
```js
// Creating mixed data
-Person.insert({
- data: [
- { type:'PERSON', id: 1, name: 'John Doe' },
- { type:'ADULT', id: 2, name: 'Jane Doe', job: 'Software Engineer' }
- ]
-})
+Person.insert([
+ { type: 'PERSON', id: 1, name: 'John Doe' },
+ { type: 'ADULT', id: 2, name: 'Jane Doe', job: 'Software Engineer' }
+])
const people = Person.all()
/*
[
Person { id: 1, name: 'John Doe', type: 'PERSON' },
- Adult { id: 2, name: 'Jane Doe', job: 'Software Engineer', type: 'ADULT' }
+ Adult { id: 2, name: 'Jane Doe', job: 'Software Engineer', type: 'ADULT' }
]
*/
```
@@ -315,20 +309,24 @@ class Address extends Model {
And let's see what would happen in this case.
```js
-Address.insert({
- data: [
- { id: 1, city: 'TOKYO' },
- { id: 2, city: 'PARIS' },
- { id: 3, city: 'BERLIN' }
- ]
-})
+Address.insert([
+ { id: 1, city: 'TOKYO' },
+ { id: 2, city: 'PARIS' },
+ { id: 3, city: 'BERLIN' }
+])
Person.insert({
- data: { id: 1, home_address_id: 1, name: 'John Doe' }
+ id: 1,
+ home_address_id: 1,
+ name: 'John Doe'
})
Adult.insert({
- data: { id: 2, home_address_id: 2, work_address_id: 3, name: 'Jane Doe', job: 'Software Engineer' }
+ id: 2,
+ home_address_id: 2,
+ work_address_id: 3,
+ name: 'Jane Doe',
+ job: 'Software Engineer'
})
const people = Person.query().with(['home_address', 'work_address']).get()
@@ -540,9 +538,7 @@ import Adult from '@/models/Adult';
export default {
created () {
- Adult.insert({
- data: { id: 1, name: 'John Doe', job: 'Software Engineer' }
- })
+ Adult.insert({ id: 1, name: 'John Doe', job: 'Software Engineer' })
}
}
```
diff --git a/src/attributes/index.ts b/src/attributes/index.ts
index 0bd7ed89..fc70103d 100644
--- a/src/attributes/index.ts
+++ b/src/attributes/index.ts
@@ -10,6 +10,7 @@ import HasOne from './relations/HasOne'
import BelongsTo from './relations/BelongsTo'
import HasMany from './relations/HasMany'
import HasManyBy from './relations/HasManyBy'
+import HasOneThrough from './relations/HasOneThrough'
import HasManyThrough from './relations/HasManyThrough'
import BelongsToMany from './relations/BelongsToMany'
import MorphTo from './relations/MorphTo'
@@ -31,6 +32,7 @@ export {
BelongsTo,
HasMany,
HasManyBy,
+ HasOneThrough,
HasManyThrough,
BelongsToMany,
MorphTo,
diff --git a/src/attributes/relations/HasOneThrough.ts b/src/attributes/relations/HasOneThrough.ts
new file mode 100644
index 00000000..17547306
--- /dev/null
+++ b/src/attributes/relations/HasOneThrough.ts
@@ -0,0 +1,151 @@
+import { Schema as NormalizrSchema } from 'normalizr'
+import Schema from '../../schema/Schema'
+import { Record, Records, NormalizedData, Collection } from '../../data'
+import Model from '../../model/Model'
+import Query from '../../query/Query'
+import Constraint from '../../query/contracts/RelationshipConstraint'
+import Relation from './Relation'
+
+export type Entity = typeof Model | string
+
+export default class HasOneThrough extends Relation {
+ /**
+ * The related model.
+ */
+ related: typeof Model
+
+ /**
+ * The "through" parent model.
+ */
+ through: typeof Model
+
+ /**
+ * The near key on the relation.
+ */
+ firstKey: string
+
+ /**
+ * The far key on the relation.
+ */
+ secondKey: string
+
+ /**
+ * The local key on the relation.
+ */
+ localKey: string
+
+ /**
+ * The local key on the intermediary model.
+ */
+ secondLocalKey: string
+
+ /**
+ * Create a new relation instance.
+ */
+ constructor(
+ model: typeof Model,
+ related: Entity,
+ through: Entity,
+ firstKey: string,
+ secondKey: string,
+ localKey: string,
+ secondLocalKey: string
+ ) {
+ super(model) /* istanbul ignore next */
+
+ this.related = this.model.relation(related)
+ this.through = this.model.relation(through)
+ this.firstKey = firstKey
+ this.secondKey = secondKey
+ this.localKey = localKey
+ this.secondLocalKey = secondLocalKey
+ }
+
+ /**
+ * Define the normalizr schema for the relation.
+ */
+ define(schema: Schema): NormalizrSchema {
+ return schema.one(this.related)
+ }
+
+ /**
+ * Since the relation doesn't have a foreign key, there is no relational key
+ * to attach to the given data.
+ */
+ attach(_key: any, _record: Record, _data: NormalizedData): void {
+ return
+ }
+
+ /**
+ * Convert given value to the appropriate value for the attribute.
+ */
+ make(value: any, _parent: Record, _key: string): Model | null {
+ return this.makeOneRelation(value, this.related)
+ }
+
+ /**
+ * Load the relation for the given collection.
+ */
+ load(
+ query: Query,
+ collection: Collection,
+ name: string,
+ constraints: Constraint[]
+ ): void {
+ const relatedQuery = this.getRelation(
+ query,
+ this.related.entity,
+ constraints
+ )
+
+ const throughQuery = query.newQuery(this.through.entity)
+
+ this.addEagerConstraintForThrough(throughQuery, collection)
+
+ const through = throughQuery.get()
+
+ this.addEagerConstraintForRelated(relatedQuery, through)
+
+ const related = this.mapThroughRelations(through, relatedQuery)
+
+ collection.forEach((item) => {
+ const relation = related[item[this.localKey]]
+
+ item[name] = relation || null
+ })
+ }
+
+ /**
+ * Set the constraints for the "through" relation.
+ */
+ addEagerConstraintForThrough(query: Query, collection: Collection): void {
+ query.where(this.firstKey, this.getKeys(collection, this.localKey))
+ }
+
+ /**
+ * Set the constraints for the "related" relation.
+ */
+ addEagerConstraintForRelated(query: Query, collection: Collection): void {
+ query.where(this.secondKey, this.getKeys(collection, this.secondLocalKey))
+ }
+
+ /**
+ * Create a new indexed map for the "through" relation.
+ */
+ mapThroughRelations(through: Collection, relatedQuery: Query): Records {
+ const relations = this.mapSingleRelations(
+ relatedQuery.get(),
+ this.secondKey
+ )
+
+ return through.reduce((records, record) => {
+ const id = record[this.firstKey]
+
+ const related = relations.get(record[this.secondLocalKey])
+
+ records[id] = related || null
+
+ return records
+ }, {})
+ }
+}
diff --git a/src/database/Database.ts b/src/database/Database.ts
index 3323e376..4bb6c97a 100644
--- a/src/database/Database.ts
+++ b/src/database/Database.ts
@@ -45,6 +45,11 @@ export default class Database {
*/
entities: Entity[] = []
+ /**
+ * The hacked models registered to the store.
+ */
+ hackedModels: Models = {}
+
/**
* The normalizr schema.
*/
@@ -63,8 +68,6 @@ export default class Database {
this.store = store
this.namespace = namespace
- this.connect()
-
this.registerModules()
this.createSchema()
@@ -80,10 +83,12 @@ export default class Database {
const entity: Entity = {
name: model.entity,
base: model.baseEntity || model.entity,
- model: this.createBindingModel(model),
+ model,
module
}
+ this.hackedModels[model.entity] = this.createBindingModel(model)
+
this.entities.push(entity)
if (this.isStarted) {
@@ -364,13 +369,6 @@ export default class Database {
this.schemas[entity.name] = Schema.create(entity.model)
}
- /**
- * Inject database to the store instance.
- */
- private connect(): void {
- this.store.$db = () => this
- }
-
/**
* Warn user if the given model is a type of an inherited model that is being
* defined without overwriting `Model.types()` because the user will not be
diff --git a/src/database/HackedDatabase.ts b/src/database/HackedDatabase.ts
new file mode 100644
index 00000000..71b1302c
--- /dev/null
+++ b/src/database/HackedDatabase.ts
@@ -0,0 +1,28 @@
+import Database from './Database'
+import Model from '../model/Model'
+
+export default class HackedDatabase {
+ /**
+ * The database instance to look up models.
+ */
+ database: Database
+
+ /**
+ * Create a new hacked database.
+ */
+ constructor(database: Database) {
+ this.database = database
+ }
+
+ /**
+ * Get the hacked model of the given name from the entities list.
+ */
+ model(model: M): M
+ model(model: string): typeof Model
+ model(model: typeof Model | string): typeof Model | string {
+ /* istanbul ignore next */
+ const name = typeof model === 'string' ? model : model.entity
+
+ return this.database.hackedModels[name]
+ }
+}
diff --git a/src/index.cjs.ts b/src/index.cjs.ts
index d069bf2f..c0159d6c 100644
--- a/src/index.cjs.ts
+++ b/src/index.cjs.ts
@@ -1,11 +1,10 @@
import './polyfills'
-import install, { Install } from './store/install'
-import use, { Use } from './plugins/use'
+import { install } from './store'
+import { use } from './plugins/use'
import Container from './container/Container'
import Database from './database/Database'
import Model from './model/Model'
-import Fields from './model/contracts/Fields'
import Query from './query/Query'
import Attribute from './attributes/Attribute'
import Type from './attributes/types/Type'
@@ -20,56 +19,19 @@ import BelongsTo from './attributes/relations/BelongsTo'
import HasMany from './attributes/relations/HasMany'
import HasManyBy from './attributes/relations/HasManyBy'
import BelongsToMany from './attributes/relations/BelongsToMany'
+import HasOneThrough from './attributes/relations/HasOneThrough'
import HasManyThrough from './attributes/relations/HasManyThrough'
import MorphTo from './attributes/relations/MorphTo'
import MorphOne from './attributes/relations/MorphOne'
import MorphMany from './attributes/relations/MorphMany'
import MorphToMany from './attributes/relations/MorphToMany'
import MorphedByMany from './attributes/relations/MorphedByMany'
+import Repository from './repository/Repository'
import Getters from './modules/Getters'
import Actions from './modules/Actions'
import RootGetters from './modules/RootGetters'
import RootActions from './modules/RootActions'
import RootMutations from './modules/RootMutations'
-import GettersContract from './modules/contracts/Getters'
-import ActionsContract from './modules/contracts/Actions'
-import RootGettersContract from './modules/contracts/RootGetters'
-import RootActionsContract from './modules/contracts/RootActions'
-import RootMutationsContract from './modules/contracts/RootMutations'
-
-export interface VuexORM {
- install: Install
- use: Use
- Container: typeof Container
- Database: typeof Database
- Model: typeof Model
- Fields: Fields
- Query: typeof Query
- Attribute: typeof Attribute
- Type: typeof Type
- Attr: typeof Attr
- String: typeof String
- Number: typeof Number
- Boolean: typeof Boolean
- Uid: typeof Uid
- Relation: typeof Relation
- HasOne: typeof HasOne
- BelongsTo: typeof BelongsTo
- HasMany: typeof HasMany
- HasManyBy: typeof HasManyBy
- BelongsToMany: typeof BelongsToMany
- HasManyThrough: typeof HasManyThrough
- MorphTo: typeof MorphTo
- MorphOne: typeof MorphOne
- MorphMany: typeof MorphMany
- MorphToMany: typeof MorphToMany
- MorphedByMany: typeof MorphedByMany
- Getters: GettersContract
- Actions: ActionsContract
- RootGetters: RootGettersContract
- RootActions: RootActionsContract
- RootMutations: RootMutationsContract
-}
export default {
install,
@@ -90,16 +52,18 @@ export default {
HasMany,
HasManyBy,
BelongsToMany,
+ HasOneThrough,
HasManyThrough,
MorphTo,
MorphOne,
MorphMany,
MorphToMany,
MorphedByMany,
+ Repository,
Getters,
Actions,
RootGetters,
RootActions,
RootMutations,
Query
-} as VuexORM
+}
diff --git a/src/index.ts b/src/index.ts
index d2ebacd2..bcc101f0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,8 +1,8 @@
import './types/vuex'
import './polyfills'
-import install, { Install } from './store/install'
-import use, { Use, PluginComponents } from './plugins/use'
+import { install } from './store'
+import { use, PluginComponents } from './plugins/use'
import Container from './container/Container'
import Database from './database/Database'
import Model from './model/Model'
@@ -20,22 +20,19 @@ import BelongsTo from './attributes/relations/BelongsTo'
import HasMany from './attributes/relations/HasMany'
import HasManyBy from './attributes/relations/HasManyBy'
import BelongsToMany from './attributes/relations/BelongsToMany'
+import HasOneThrough from './attributes/relations/HasOneThrough'
import HasManyThrough from './attributes/relations/HasManyThrough'
import MorphTo from './attributes/relations/MorphTo'
import MorphOne from './attributes/relations/MorphOne'
import MorphMany from './attributes/relations/MorphMany'
import MorphToMany from './attributes/relations/MorphToMany'
import MorphedByMany from './attributes/relations/MorphedByMany'
+import Repository from './repository/Repository'
import Getters from './modules/Getters'
import Actions from './modules/Actions'
import RootGetters from './modules/RootGetters'
import RootActions from './modules/RootActions'
import RootMutations from './modules/RootMutations'
-import GettersContract from './modules/contracts/Getters'
-import ActionsContract from './modules/contracts/Actions'
-import RootGettersContract from './modules/contracts/RootGetters'
-import RootActionsContract from './modules/contracts/RootActions'
-import RootMutationsContract from './modules/contracts/RootMutations'
import Query from './query/Query'
import Record from './data/Record'
import Records from './data/Records'
@@ -48,39 +45,6 @@ import Item from './data/Item'
import Collection from './data/Collection'
import Collections from './data/Collections'
-export interface VuexORM {
- install: Install
- use: Use
- Container: typeof Container
- Database: typeof Database
- Model: typeof Model
- Attribute: typeof Attribute
- Type: typeof Type
- Attr: typeof Attr
- String: typeof String
- Number: typeof Number
- Boolean: typeof Boolean
- Uid: typeof Uid
- Relation: typeof Relation
- HasOne: typeof HasOne
- BelongsTo: typeof BelongsTo
- HasMany: typeof HasMany
- HasManyBy: typeof HasManyBy
- BelongsToMany: typeof BelongsToMany
- HasManyThrough: typeof HasManyThrough
- MorphTo: typeof MorphTo
- MorphOne: typeof MorphOne
- MorphMany: typeof MorphMany
- MorphToMany: typeof MorphToMany
- MorphedByMany: typeof MorphedByMany
- Getters: GettersContract
- Actions: ActionsContract
- RootGetters: RootGettersContract
- RootActions: RootActionsContract
- RootMutations: RootMutationsContract
- Query: typeof Query
-}
-
export {
install,
use,
@@ -102,12 +66,14 @@ export {
HasMany,
HasManyBy,
BelongsToMany,
+ HasOneThrough,
HasManyThrough,
MorphTo,
MorphOne,
MorphMany,
MorphToMany,
MorphedByMany,
+ Repository,
Getters,
Actions,
RootGetters,
@@ -145,16 +111,18 @@ export default {
HasMany,
HasManyBy,
BelongsToMany,
+ HasOneThrough,
HasManyThrough,
MorphTo,
MorphOne,
MorphMany,
MorphToMany,
MorphedByMany,
+ Repository,
Getters,
Actions,
RootGetters,
RootActions,
RootMutations,
Query
-} as VuexORM
+}
diff --git a/src/model/Model.ts b/src/model/Model.ts
index 09e14475..71798d55 100644
--- a/src/model/Model.ts
+++ b/src/model/Model.ts
@@ -15,11 +15,15 @@ import Mutators from '../attributes/contracts/Mutators'
import Predicate from '../query/contracts/Predicate'
import Query from '../query/Query'
import * as Payloads from '../modules/payloads/Actions'
+import * as PersistOptions from '../modules/options/Actions'
import Fields from './contracts/Fields'
import FieldCache from './contracts/FieldCache'
import ModelState from './contracts/State'
import InheritanceTypes from './contracts/InheritanceTypes'
import { toAttributes, toJson } from './Serialize'
+import PayloadBuilder from '../modules/support/PayloadBuilder'
+import { PersistOptions as QueryPersistOptions } from '../query/options'
+import { InsertObject, normalizeInsertPayload } from './support/Payloads'
export default class Model {
/**
@@ -193,6 +197,28 @@ export default class Model {
)
}
+ /**
+ * Create a has one through relationship.
+ */
+ static hasOneThrough(
+ related: typeof Model | string,
+ through: typeof Model | string,
+ firstKey: string,
+ secondKey: string,
+ localKey?: string,
+ secondLocalKey?: string
+ ): Attributes.HasOneThrough {
+ return new Attributes.HasOneThrough(
+ this,
+ related,
+ through,
+ firstKey,
+ secondKey,
+ this.localKey(localKey),
+ this.relation(through).localKey(secondLocalKey)
+ )
+ }
+
/**
* Create a has many through relationship.
*/
@@ -353,7 +379,7 @@ export default class Model {
* Get the database instance from store.
*/
static database(): Database {
- return this.store().$db()
+ return this.store().$database
}
/**
@@ -435,8 +461,8 @@ export default class Model {
/**
* Get query instance.
*/
- static query(this: T): Query> {
- return this.getters('query')()
+ static query(this: M): Query> {
+ return new Query(this.store(), this.entity)
}
/**
@@ -449,8 +475,8 @@ export default class Model {
/**
* Create new data with all fields filled by default values.
*/
- static new(): Promise {
- return this.dispatch('new')
+ static new(this: T): Promise> {
+ return this.query().new()
}
/**
@@ -458,21 +484,25 @@ export default class Model {
* store. If you want to save data without replacing existing records,
* use the `insert` method instead.
*/
- static create(
- this: T,
- payload: Payloads.Create
+ static create(
+ data: Record | Record[] | InsertObject,
+ options?: QueryPersistOptions
): Promise {
- return this.dispatch('create', payload)
+ const { data: d, options: o } = normalizeInsertPayload(data, options)
+
+ return this.query().create(d, o)
}
/**
* Insert records.
*/
- static insert(
- this: T,
- payload: Payloads.Insert
+ static insert(
+ data: Record | Record[] | InsertObject,
+ options?: QueryPersistOptions
): Promise {
- return this.dispatch('insert', payload)
+ const { data: d, options: o } = normalizeInsertPayload(data, options)
+
+ return this.query().insert(d, o)
}
/**
@@ -480,9 +510,10 @@ export default class Model {
*/
static update(
this: T,
- payload: Payloads.Update
+ payload: Payloads.Update,
+ options?: PersistOptions.Update
): Promise {
- return this.dispatch('update', payload)
+ return this.dispatch('update', PayloadBuilder.normalize(payload, options))
}
/**
@@ -490,9 +521,13 @@ export default class Model {
*/
static insertOrUpdate(
this: T,
- payload: Payloads.InsertOrUpdate
+ payload: Payloads.InsertOrUpdate,
+ options?: PersistOptions.InsertOrUpdate
): Promise {
- return this.dispatch('insertOrUpdate', payload)
+ return this.dispatch(
+ 'insertOrUpdate',
+ PayloadBuilder.normalize(payload, options)
+ )
}
/**
@@ -802,7 +837,7 @@ export default class Model {
}
/**
- * Call Vuex Getetrs.
+ * Call Vuex Getters.
*/
$getters(method: string): any {
return this.$self().getters(method)
@@ -852,46 +887,75 @@ export default class Model {
/**
* Create records.
*/
- async $create(payload: Payloads.Create): Promise {
- return this.$dispatch('create', payload)
+ async $create(
+ payload: Payloads.Create,
+ options?: PersistOptions.Create
+ ): Promise {
+ return this.$dispatch('create', PayloadBuilder.normalize(payload, options))
}
/**
- * Create records.
+ * Insert records.
*/
- async $insert(payload: Payloads.Insert): Promise {
- return this.$dispatch('insert', payload)
+ async $insert(
+ payload: Payloads.Insert,
+ options?: PersistOptions.Insert
+ ): Promise {
+ return this.$dispatch('insert', PayloadBuilder.normalize(payload, options))
}
/**
* Update records.
*/
- async $update(payload: Payloads.Update): Promise {
+ async $update(
+ payload: Payloads.Update,
+ options?: PersistOptions.Update
+ ): Promise {
if (Utils.isArray(payload)) {
- return this.$dispatch('update', payload)
+ return this.$dispatch(
+ 'update',
+ PayloadBuilder.normalize(payload, options)
+ )
}
- if (payload.where !== undefined) {
- return this.$dispatch('update', payload)
+ if (
+ payload.where !== undefined ||
+ (options && options.where !== undefined)
+ ) {
+ return this.$dispatch(
+ 'update',
+ PayloadBuilder.normalize(payload, options)
+ )
}
if (this.$self().getIndexIdFromRecord(payload) === null) {
- return this.$dispatch('update', {
- where: this.$self().getIdFromRecord(this),
- data: payload
- })
+ /* istanbul ignore else */
+ if (!options) {
+ options = {}
+ }
+
+ options.where = this.$self().getIdFromRecord(this)
+
+ return this.$dispatch(
+ 'update',
+ PayloadBuilder.normalize(payload, options)
+ )
}
- return this.$dispatch('update', payload)
+ return this.$dispatch('update', PayloadBuilder.normalize(payload, options))
}
/**
* Insert or update records.
*/
async $insertOrUpdate(
- payload: Payloads.InsertOrUpdate
+ payload: Payloads.InsertOrUpdate,
+ options?: PersistOptions.InsertOrUpdate
): Promise {
- return this.$dispatch('insertOrUpdate', payload)
+ return this.$dispatch(
+ 'insertOrUpdate',
+ PayloadBuilder.normalize(payload, options)
+ )
}
/**
diff --git a/src/model/support/Payloads.ts b/src/model/support/Payloads.ts
new file mode 100644
index 00000000..69332c76
--- /dev/null
+++ b/src/model/support/Payloads.ts
@@ -0,0 +1,39 @@
+import { isArray } from '../../support/Utils'
+import { Record } from '../../data'
+import { PersistOptions } from '../../query/options'
+
+export interface InsertObject extends PersistOptions {
+ data: Record | Record[]
+}
+
+export interface InsertPayload {
+ data: Record | Record[]
+ options?: PersistOptions
+}
+
+/**
+ * Normalize the payload arguments for persist methods. It's required for
+ * backward compatibility for the new persist method argument structure.
+ * It may be deprecated and be removed in the future.
+ */
+export function normalizeInsertPayload(
+ data: Record | Record[] | InsertObject,
+ options?: PersistOptions
+): InsertPayload {
+ // If the data is an array, then the data should be an array of data.
+ // Therefore the whole payload should be passed directly as `data`.
+ if (isArray(data)) {
+ return { data, options }
+ }
+
+ // If the data doesn't have a `data` property, it can be assumed that the
+ // user has passed an object as the payload. Therefore the entire payload
+ // should be declared as `data`.
+ if (data.data === undefined) {
+ return { data, options }
+ }
+
+ // If the data contains `data` property, we should extract it and pass that
+ // property as `data`.
+ return { data: data.data, options: data }
+}
diff --git a/src/modules/Actions.ts b/src/modules/Actions.ts
index 8eac9553..82e0573e 100644
--- a/src/modules/Actions.ts
+++ b/src/modules/Actions.ts
@@ -1,4 +1,3 @@
-import { isArray } from '../support/Utils'
import Item from '../data/Item'
import Collection from '../data/Collection'
import Collections from '../data/Collections'
@@ -6,6 +5,7 @@ import Model from '../model/Model'
import ActionsContract from './contracts/Actions'
import ActionContext from './contracts/ActionContext'
import * as Payloads from './payloads/Actions'
+import PayloadBuilder from './support/PayloadBuilder'
/**
* Create new data with all fields filled by default values.
@@ -32,10 +32,11 @@ async function create(
): Promise {
const state = context.state
const entity = state.$name
+ const data = PayloadBuilder.createPersistPayload(payload)
return context.dispatch(
`${state.$connection}/create`,
- { ...payload, entity },
+ { entity, ...data },
{ root: true }
)
}
@@ -51,10 +52,11 @@ async function insert(
): Promise {
const state = context.state
const entity = state.$name
+ const data = PayloadBuilder.createPersistPayload(payload)
return context.dispatch(
`${state.$connection}/insert`,
- { ...payload, entity },
+ { entity, ...data },
{ root: true }
)
}
@@ -68,32 +70,11 @@ async function update(
): Promise- {
const state = context.state
const entity = state.$name
+ const data = PayloadBuilder.createPersistPayload(payload)
- // If the payload is an array, then the payload should be an array of
- // data so let's pass the whole payload as data.
- if (isArray(payload)) {
- return context.dispatch(
- `${state.$connection}/update`,
- { entity, data: payload },
- { root: true }
- )
- }
-
- // If the payload doesn't have `data` property, we'll assume that
- // the user has passed the object as the payload so let's define
- // the whole payload as a data.
- if (payload.data === undefined) {
- return context.dispatch(
- `${state.$connection}/update`,
- { entity, data: payload },
- { root: true }
- )
- }
-
- // Else destructure the payload and let root action handle it.
return context.dispatch(
`${state.$connection}/update`,
- { entity, ...payload },
+ { entity, ...data },
{ root: true }
)
}
@@ -109,10 +90,11 @@ async function insertOrUpdate(
): Promise
{
const state = context.state
const entity = state.$name
+ const data = PayloadBuilder.createPersistPayload(payload)
return context.dispatch(
`${state.$connection}/insertOrUpdate`,
- { entity, ...payload },
+ { entity, ...data },
{ root: true }
)
}
diff --git a/src/modules/options/Actions.ts b/src/modules/options/Actions.ts
new file mode 100644
index 00000000..4629e0db
--- /dev/null
+++ b/src/modules/options/Actions.ts
@@ -0,0 +1,13 @@
+import { Condition } from '../payloads/Actions'
+import PrimaryKey from '../../query/options/PrimaryKey'
+import PersistOptions from '../../query/options/PersistOptions'
+
+export type Create = PersistOptions
+
+export type Insert = PersistOptions
+
+export interface Update extends PersistOptions {
+ where?: PrimaryKey | Condition | null
+}
+
+export type InsertOrUpdate = PersistOptions
diff --git a/src/modules/payloads/Actions.ts b/src/modules/payloads/Actions.ts
index 11be4e4f..5cc0e997 100644
--- a/src/modules/payloads/Actions.ts
+++ b/src/modules/payloads/Actions.ts
@@ -4,12 +4,18 @@ import PersistOptions from '../../query/options/PersistOptions'
export type Condition = (record: Record) => boolean
-export interface Create extends PersistOptions {
- data: Record | Record[]
+export type Create = CreateObject | Record[]
+
+export interface CreateObject extends PersistOptions {
+ data?: Record | Record[]
+ [key: string]: any
}
-export interface Insert extends PersistOptions {
- data: Record | Record[]
+export type Insert = InsertObject | Record[]
+
+export interface InsertObject extends PersistOptions {
+ data?: Record | Record[]
+ [key: string]: any
}
export type Update = UpdateObject | Record[]
@@ -20,8 +26,11 @@ export interface UpdateObject extends PersistOptions {
[key: string]: any
}
-export interface InsertOrUpdate extends PersistOptions {
- data: Record | Record[]
+export type InsertOrUpdate = InsertOrUpdateObject | Record[]
+
+export interface InsertOrUpdateObject extends PersistOptions {
+ data?: Record | Record[]
+ [key: string]: any
}
export type DeleteById = string | number | (number | string)[]
diff --git a/src/modules/support/PayloadBuilder.ts b/src/modules/support/PayloadBuilder.ts
new file mode 100644
index 00000000..6a8e4e55
--- /dev/null
+++ b/src/modules/support/PayloadBuilder.ts
@@ -0,0 +1,44 @@
+import { Create, Insert, Update, InsertOrUpdate } from '../payloads/Actions'
+import OptionsBuilder from './OptionsBuilder'
+
+export type PersistPayload = Create | Insert | Update | InsertOrUpdate
+
+export default class PayloadBuilder {
+ /**
+ * Create downstream payload from input data to determine the style of data
+ * being provided for downward compatibility.
+ */
+ static createPersistPayload(payload: PersistPayload): PersistPayload {
+ // If the payload is an array, then the payload should be an array of
+ // data therefore the whole payload should be declared as `data`.
+ if (Array.isArray(payload)) {
+ return { data: payload }
+ }
+
+ // If the payload doesn't have a `data` property, it can be assumed that
+ // the user has passed an object as the payload therefore the entire
+ // payload should be declared as `data`
+ if (payload.data === undefined) {
+ return { data: payload }
+ }
+
+ // It can safely be assumed the user is providing payload with `data` intact.
+ return payload
+ }
+
+ /**
+ * Normalize persist payload by converting the new style of persisting data
+ * through method arguments, while maintaining existing style (data key),
+ * to pass on to the module API.
+ */
+ static normalize(payload?: any, options?: any): PersistPayload {
+ const persistPayload = this.createPersistPayload(payload)
+ const persistOptions = OptionsBuilder.createPersistOptions(payload)
+
+ return {
+ ...persistPayload,
+ ...persistOptions,
+ ...options
+ }
+ }
+}
diff --git a/src/plugins/use.ts b/src/plugins/use.ts
index a7e4d0ce..8058c976 100644
--- a/src/plugins/use.ts
+++ b/src/plugins/use.ts
@@ -1,3 +1,4 @@
+import Repository from '../repository/Repository'
import Model from '../model/Model'
import Attribute from '../attributes/Attribute'
import Type from '../attributes/types/Type'
@@ -12,6 +13,7 @@ import BelongsTo from '../attributes/relations/BelongsTo'
import HasMany from '../attributes/relations/HasMany'
import HasManyBy from '../attributes/relations/HasManyBy'
import BelongsToMany from '../attributes/relations/BelongsToMany'
+import HasOneThrough from '../attributes/relations/HasOneThrough'
import HasManyThrough from '../attributes/relations/HasManyThrough'
import MorphTo from '../attributes/relations/MorphTo'
import MorphOne from '../attributes/relations/MorphOne'
@@ -32,6 +34,7 @@ import Query from '../query/Query'
import Database from '../database/Database'
export interface PluginComponents {
+ Repository: typeof Repository
Model: typeof Model
Attribute: typeof Attribute
Type: typeof Type
@@ -46,6 +49,7 @@ export interface PluginComponents {
HasMany: typeof HasMany
HasManyBy: typeof HasManyBy
BelongsToMany: typeof BelongsToMany
+ HasOneThrough: typeof HasOneThrough
HasManyThrough: typeof HasManyThrough
MorphTo: typeof MorphTo
MorphOne: typeof MorphOne
@@ -71,8 +75,9 @@ export interface Plugin {
export type Use = (plugin: Plugin, options?: Options) => void
-export default function(plugin: Plugin, options: Options = {}): void {
+export function use(plugin: Plugin, options: Options = {}): void {
const components: PluginComponents = {
+ Repository,
Model,
Attribute,
Type,
@@ -87,6 +92,7 @@ export default function(plugin: Plugin, options: Options = {}): void {
HasMany,
HasManyBy,
BelongsToMany,
+ HasOneThrough,
HasManyThrough,
MorphTo,
MorphOne,
diff --git a/src/polyfills/index.ts b/src/polyfills/index.ts
index ff16b43f..b505cb3d 100644
--- a/src/polyfills/index.ts
+++ b/src/polyfills/index.ts
@@ -1,8 +1,6 @@
-/*eslint-disable */
-
+import 'core-js/fn/array/from'
import 'core-js/fn/array/includes'
import 'core-js/fn/object/assign'
import 'core-js/fn/object/entries'
import 'core-js/fn/object/values'
import 'core-js/fn/string/starts-with'
-import 'core-js/fn/array/from'
diff --git a/src/query/Query.ts b/src/query/Query.ts
index 111a45a5..7add3be0 100644
--- a/src/query/Query.ts
+++ b/src/query/Query.ts
@@ -139,7 +139,7 @@ export default class Query {
*/
constructor(store: Store, entity: string) {
this.store = store
- this.database = store.$db()
+ this.database = store.$database
this.model = this.getModel(entity)
this.baseModel = this.getBaseModel(entity)
@@ -157,7 +157,7 @@ export default class Query {
* Delete all records from the store.
*/
static deleteAll(store: Store): void {
- const database = store.$db()
+ const database = store.$database
const models = database.models()
for (const entity in models) {
@@ -795,7 +795,7 @@ export default class Query {
/**
* Create new data with all fields filled by default values.
*/
- new(): T {
+ async new(): Promise {
const model = new this.model().$generateId() as T
this.commitInsert(model.$getAttributes())
@@ -810,8 +810,8 @@ export default class Query {
*/
create(
data: Data.Record | Data.Record[],
- options: Options.PersistOptions
- ): Data.Collections {
+ options?: Options.PersistOptions
+ ): Promise {
return this.persist('create', data, options)
}
@@ -831,8 +831,8 @@ export default class Query {
*/
insert(
data: Data.Record | Data.Record[],
- options: Options.PersistOptions
- ): Data.Collections {
+ options?: Options.PersistOptions
+ ): Promise {
return this.persist('insert', data, options)
}
@@ -858,7 +858,10 @@ export default class Query {
data: Data.Record | Data.Record[] | UpdateClosure,
condition: UpdateCondition,
options: Options.PersistOptions
- ): Data.Item | Data.Collection | Data.Collections {
+ ):
+ | Promise>
+ | Promise>
+ | Promise {
// If the data is array, simply normalize the data and update them.
if (Utils.isArray(data)) {
return this.persist('update', data, options)
@@ -925,10 +928,10 @@ export default class Query {
/**
* Update the state by id.
*/
- updateById(
+ async updateById(
data: Data.Record | UpdateClosure,
id: string | number
- ): Data.Item {
+ ): Promise> {
id = typeof id === 'number' ? id.toString() : this.normalizeIndexId(id)
const record = this.state.data[id]
@@ -951,10 +954,10 @@ export default class Query {
/**
* Update the state by condition.
*/
- updateByCondition(
+ async updateByCondition(
data: Data.Record | UpdateClosure,
condition: Contracts.Predicate
- ): Data.Collection {
+ ): Promise> {
const instances = Object.keys(this.state.data).reduce>(
(instances, id) => {
const instance = this.hydrate(this.state.data[id])
@@ -1066,7 +1069,7 @@ export default class Query {
insertOrUpdate(
data: Data.Record | Data.Record[],
options: Options.PersistOptions
- ): Data.Collections {
+ ): Promise {
return this.persist('insertOrUpdate', data, options)
}
@@ -1098,11 +1101,11 @@ export default class Query {
/**
* Persist data into the state while preserving it's original structure.
*/
- persist(
+ async persist(
method: Options.PersistMethods,
data: Data.Record | Data.Record[],
- options: Options.PersistOptions
- ): Data.Collections {
+ options: Options.PersistOptions = {}
+ ): Promise {
const clonedData = Utils.cloneDeep(data)
const normalizedData = this.normalize(clonedData)
diff --git a/src/repository/Repository.ts b/src/repository/Repository.ts
new file mode 100644
index 00000000..9f81ab90
--- /dev/null
+++ b/src/repository/Repository.ts
@@ -0,0 +1,188 @@
+import { Store } from 'vuex'
+import { ConstructorOf as BaseConstructorOf } from '../types'
+import Database from '../database/Database'
+import { Record, Item, Collection, Collections } from '../data'
+import Model from '../model/Model'
+import * as Payloads from '../modules/payloads/Actions'
+import PayloadBuilder from '../modules/support/PayloadBuilder'
+import Query from '../query/Query'
+import Predicate from '../query/contracts/Predicate'
+import { PersistOptions } from '../query/options'
+import { InsertObject, normalizeInsertPayload } from './support/Payloads'
+
+interface ConstructorOf extends BaseConstructorOf {
+ entity: string
+}
+
+export default class Repository {
+ /**
+ * A special flag to indicate if this is the repository class or not. It's
+ * used when retrieving repository instance from `store.$repo()` method to
+ * determine whether the passed in class is either a repository or a model.
+ */
+ static _isRepository: boolean = true
+
+ /**
+ * The store instance for the repository.
+ */
+ store: Store
+
+ /**
+ * The model for the repository.
+ */
+ model!: ConstructorOf
+
+ /**
+ * Create a new repository instance.
+ */
+ constructor(store: Store, model?: ConstructorOf) {
+ this.store = store
+
+ if (model) {
+ this.model = model
+ }
+ }
+
+ /**
+ * Get the database instance from the store instance.
+ */
+ database(): Database {
+ return this.store.$database
+ }
+
+ /**
+ * Create a namespaced method name for Vuex Module from the given
+ * method name.
+ */
+ namespace(method: string): string {
+ return `${this.database().namespace}/${this.model.entity}/${method}`
+ }
+
+ /**
+ * Call Vuex Getters.
+ */
+ getters(method: string): any {
+ return this.store.getters[this.namespace(method)]
+ }
+
+ /**
+ * Dispatch Vuex Action.
+ */
+ dispatch(method: string, payload?: any): Promise {
+ return this.store.dispatch(this.namespace(method), payload)
+ }
+
+ /**
+ * Create a new model instance.
+ */
+ make(record?: Record): M {
+ return new this.model(record)
+ }
+
+ /**
+ * Get all records.
+ */
+ all(): Collection {
+ return this.getters('all')()
+ }
+
+ /**
+ * Find a record.
+ */
+ find(id: string | number | (number | string)[]): Item {
+ return this.getters('find')(id)
+ }
+
+ /**
+ * Get the record of the given array of ids.
+ */
+ findIn(idList: (number | string | (number | string)[])[]): Collection {
+ return this.getters('findIn')(idList)
+ }
+
+ /**
+ * Get query instance.
+ */
+ query(): Query {
+ return new Query(this.store, this.model.entity)
+ }
+
+ /**
+ * Check wether the associated database contains data.
+ */
+ exists(): boolean {
+ return this.query().exists()
+ }
+
+ /**
+ * Create new data with all fields filled by default values.
+ */
+ new(): Promise {
+ return this.query().new()
+ }
+
+ /**
+ * Save given data to the store by replacing all existing records in the
+ * store. If you want to save data without replacing existing records,
+ * use the `insert` method instead.
+ */
+ create(
+ data: Record | Record[] | InsertObject,
+ options?: PersistOptions
+ ): Promise {
+ const { data: d, options: o } = normalizeInsertPayload(data, options)
+
+ return this.query().create(d, o)
+ }
+
+ /**
+ * Insert records.
+ */
+ insert(
+ data: Record | Record[] | InsertObject,
+ options?: PersistOptions
+ ): Promise {
+ const { data: d, options: o } = normalizeInsertPayload(data, options)
+
+ return this.query().insert(d, o)
+ }
+
+ /**
+ * Update records.
+ */
+ update(
+ payload: Payloads.Update,
+ options?: PersistOptions
+ ): Promise {
+ return this.dispatch('update', PayloadBuilder.normalize(payload, options))
+ }
+
+ /**
+ * Insert or update records.
+ */
+ insertOrUpdate(
+ payload: Payloads.InsertOrUpdate,
+ options?: PersistOptions
+ ): Promise {
+ return this.dispatch(
+ 'insertOrUpdate',
+ PayloadBuilder.normalize(payload, options)
+ )
+ }
+
+ /**
+ * Delete records that matches the given condition.
+ */
+ delete(id: string | number | (number | string)[]): Promise- >
+ delete(condition: Predicate
): Promise>
+ delete(payload: any): any {
+ return this.dispatch('delete', payload)
+ }
+
+ /**
+ * Delete all records from the store.
+ */
+ deleteAll(): Promise> {
+ return this.dispatch('deleteAll')
+ }
+}
diff --git a/src/repository/support/Payloads.ts b/src/repository/support/Payloads.ts
new file mode 100644
index 00000000..69332c76
--- /dev/null
+++ b/src/repository/support/Payloads.ts
@@ -0,0 +1,39 @@
+import { isArray } from '../../support/Utils'
+import { Record } from '../../data'
+import { PersistOptions } from '../../query/options'
+
+export interface InsertObject extends PersistOptions {
+ data: Record | Record[]
+}
+
+export interface InsertPayload {
+ data: Record | Record[]
+ options?: PersistOptions
+}
+
+/**
+ * Normalize the payload arguments for persist methods. It's required for
+ * backward compatibility for the new persist method argument structure.
+ * It may be deprecated and be removed in the future.
+ */
+export function normalizeInsertPayload(
+ data: Record | Record[] | InsertObject,
+ options?: PersistOptions
+): InsertPayload {
+ // If the data is an array, then the data should be an array of data.
+ // Therefore the whole payload should be passed directly as `data`.
+ if (isArray(data)) {
+ return { data, options }
+ }
+
+ // If the data doesn't have a `data` property, it can be assumed that the
+ // user has passed an object as the payload. Therefore the entire payload
+ // should be declared as `data`.
+ if (data.data === undefined) {
+ return { data, options }
+ }
+
+ // If the data contains `data` property, we should extract it and pass that
+ // property as `data`.
+ return { data: data.data, options: data }
+}
diff --git a/src/store/index.ts b/src/store/index.ts
new file mode 100644
index 00000000..552b056f
--- /dev/null
+++ b/src/store/index.ts
@@ -0,0 +1,94 @@
+import { Store, Plugin } from 'vuex'
+import Container from '../container/Container'
+import Database from '../database/Database'
+import HackedDatabase from '../database/HackedDatabase'
+import Repository from '../repository/Repository'
+
+export interface Options {
+ namespace?: string
+}
+
+/**
+ * Install Vuex ORM database to the store.
+ */
+export function install(
+ database: Database,
+ options: Options = {}
+): Plugin {
+ const namespace = options.namespace || 'entities'
+
+ return (store) => {
+ mixin(store, database, namespace)
+ }
+}
+
+/**
+ * Mixin Vuex ORM feature to the store.
+ */
+function mixin(store: Store, database: Database, namespace: string): void {
+ registerDatabase(store, database)
+ mixinRepoFunction(store)
+ mixinDbFunction(store)
+ registerToContainer(store)
+ startDatabase(store, namespace)
+}
+
+/**
+ * Register the database to the store.
+ */
+function registerDatabase(store: Store, database: Database): void {
+ store.$database = database
+}
+
+/**
+ * Mixin repo function to the store.
+ */
+function mixinRepoFunction(store: Store): void {
+ store.$repo = function(modelOrRepository: any): any {
+ const repository = modelOrRepository._isRepository
+ ? new modelOrRepository(this)
+ : new Repository(this, modelOrRepository)
+
+ if (!repository.model) {
+ throw new Error(
+ '[Vuex ORM] The repository was instantiated without a model being ' +
+ 'set. It happens when you forgot to register the model to the ' +
+ 'custom repository. Please check if you have correctly set ' +
+ '`model` property at the repository class.'
+ )
+ }
+
+ return repository
+ }
+}
+
+/**
+ * Mixin db function to the store.
+ */
+function mixinDbFunction(store: Store): void {
+ store.$db = function(): HackedDatabase {
+ /* istanbul ignore next */
+ if (__DEV__) {
+ console.warn(
+ '[Vuex ORM] `store.$db()` method is deprecated. Please use ' +
+ '`store.$repo(Model)` method instead.'
+ )
+ }
+
+ return new HackedDatabase(this.$database)
+ }
+}
+
+/**
+ * Register the store to the container.
+ */
+function registerToContainer(store: Store): void {
+ Container.register(store)
+}
+
+/**
+ * Start the database.
+ */
+function startDatabase(store: Store, namespace: string): void {
+ store.$database.start(store, namespace)
+}
diff --git a/src/store/install.ts b/src/store/install.ts
deleted file mode 100644
index a45102c3..00000000
--- a/src/store/install.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import * as Vuex from 'vuex'
-import Container from '../container/Container'
-import Database from '../database/Database'
-
-export type Install = (
- database: Database,
- options?: Options
-) => Vuex.Plugin
-
-export interface Options {
- namespace?: string
-}
-
-export default (
- database: Database,
- options: Options = {}
-): Vuex.Plugin => {
- const namespace = options.namespace || 'entities'
-
- return (store: Vuex.Store): void => {
- database.start(store, namespace)
-
- Container.register(store)
- }
-}
diff --git a/src/types/index.ts b/src/types/index.ts
new file mode 100644
index 00000000..c5ddf026
--- /dev/null
+++ b/src/types/index.ts
@@ -0,0 +1 @@
+export type ConstructorOf = { new (...args: any[]): C }
diff --git a/src/types/vuex.ts b/src/types/vuex.ts
index bda35228..3eb1d98d 100644
--- a/src/types/vuex.ts
+++ b/src/types/vuex.ts
@@ -1,7 +1,32 @@
+import { ConstructorOf } from '../types'
import Database from '../database/Database'
+import HackedDatabase from '../database/HackedDatabase'
+import Model from '../model/Model'
+import Repository from '../repository/Repository'
declare module 'vuex' {
interface Store {
- $db(): Database
+ /**
+ * The database instance registered to the store.
+ */
+ $database: Database
+
+ /**
+ * Get a new repository instance for the given model.
+ */
+ $repo>(repository: ConstructorOf): R
+ $repo(model: ConstructorOf): Repository
+
+ /**
+ * Get the database attached to the store. It's the old syntax and should
+ * avoid using it.
+ *
+ * It will return a "Hacked" database that capable of finding a copied
+ * model object. It's an old way of interacting with the database.
+ *
+ * TODO: Update the version.
+ * @deprecated Since v0.XX.X
+ */
+ $db(): HackedDatabase
}
}
diff --git a/test/feature/VuexORM.spec.ts b/test/feature/VuexORM.spec.ts
deleted file mode 100644
index f3763cc8..00000000
--- a/test/feature/VuexORM.spec.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import Vue from 'vue'
-import Vuex from 'vuex'
-import VuexORM from '@/index'
-
-describe('Feature – Vuex ORM', () => {
- Vue.use(Vuex)
-
- class User extends VuexORM.Model {
- static entity = 'users'
- }
-
- class Post extends VuexORM.Model {
- static entity = 'posts'
- }
-
- const users = {}
- const posts = {}
-
- it('can install Vuex ORM to the Vuex', () => {
- const database = new VuexORM.Database()
-
- database.register(User, users)
- database.register(Post, posts)
-
- const store = new Vuex.Store({
- plugins: [VuexORM.install(database)]
- })
-
- expect(store.state.entities.$name).toBe('entities')
- expect(store.state.entities.users.$name).toBe('users')
- expect(store.state.entities.posts.$name).toBe('posts')
- })
-
- it('can omit modules', () => {
- const database = new VuexORM.Database()
-
- database.register(User)
- database.register(Post)
-
- const store = new Vuex.Store({
- plugins: [VuexORM.install(database)]
- })
-
- expect(store.state.entities.$name).toBe('entities')
- expect(store.state.entities.users.$name).toBe('users')
- expect(store.state.entities.posts.$name).toBe('posts')
- })
-
- it('can install Vuex ORM to the Vuex under a custom namespace', () => {
- const database = new VuexORM.Database()
-
- database.register(User, users)
- database.register(Post, posts)
-
- const options = { namespace: 'my_entities' }
-
- const store = new Vuex.Store({
- plugins: [VuexORM.install(database, options)]
- })
-
- expect(store.state.my_entities.$name).toBe('my_entities')
- expect(store.state.my_entities.users.$name).toBe('users')
- expect(store.state.my_entities.posts.$name).toBe('posts')
- })
-
- it('can install Vuex ORM along with custom state as a function', () => {
- const database = new VuexORM.Database()
-
- const users = {
- state() {
- return {
- customState: 'Yes, it is custom'
- }
- }
- }
-
- database.register(User, users)
-
- const store = new Vuex.Store({
- plugins: [VuexORM.install(database)]
- })
-
- expect(store.state.entities.users.$name).toBe('users')
- expect(store.state.entities.users.customState).toBe('Yes, it is custom')
- })
-})
diff --git a/test/feature/aggregates/Aggregates.spec.ts b/test/feature/aggregates/Aggregates.spec.ts
index 72031895..98fb11fc 100644
--- a/test/feature/aggregates/Aggregates.spec.ts
+++ b/test/feature/aggregates/Aggregates.spec.ts
@@ -24,13 +24,11 @@ describe('Feature – Aggregates', () => {
})
it('can get count of the data', async () => {
- await User.create({
- data: [
- { id: 1, role: 'admin' },
- { id: 2, role: 'user' },
- { id: 3, role: 'admin' }
- ]
- })
+ await User.create([
+ { id: 1, role: 'admin' },
+ { id: 2, role: 'user' },
+ { id: 3, role: 'admin' }
+ ])
expect(User.query().count()).toBe(3)
expect(
@@ -41,14 +39,12 @@ describe('Feature – Aggregates', () => {
})
it('can get max value of the specified field', async () => {
- await User.create({
- data: [
- { id: 8, role: 'admin' },
- { id: 12, role: 'user' },
- { id: 11, role: 'admin' },
- { id: 'A', role: 'admin' }
- ]
- })
+ await User.create([
+ { id: 8, role: 'admin' },
+ { id: 12, role: 'user' },
+ { id: 11, role: 'admin' },
+ { id: 'A', role: 'admin' }
+ ])
expect(User.query().max('id')).toBe(12)
expect(User.query().max('role')).toBe(0)
@@ -60,14 +56,12 @@ describe('Feature – Aggregates', () => {
})
it('can get min value of the specified field', async () => {
- await User.create({
- data: [
- { id: 8, role: 'admin' },
- { id: 12, role: 'user' },
- { id: 11, role: 'admin' },
- { id: 'A', role: 'admin' }
- ]
- })
+ await User.create([
+ { id: 8, role: 'admin' },
+ { id: 12, role: 'user' },
+ { id: 11, role: 'admin' },
+ { id: 'A', role: 'admin' }
+ ])
expect(User.query().min('id')).toBe(8)
expect(User.query().min('role')).toBe(0)
@@ -79,14 +73,12 @@ describe('Feature – Aggregates', () => {
})
it('can get sum value of the specified field', async () => {
- await User.create({
- data: [
- { id: 8, role: 'admin' },
- { id: 12, role: 'user' },
- { id: 11, role: 'admin' },
- { id: 'A', role: 'admin' }
- ]
- })
+ await User.create([
+ { id: 8, role: 'admin' },
+ { id: 12, role: 'user' },
+ { id: 11, role: 'admin' },
+ { id: 'A', role: 'admin' }
+ ])
expect(User.query().sum('id')).toBe(31)
expect(
diff --git a/test/feature/attributes/Boolean.spec.ts b/test/feature/attributes/Boolean.spec.ts
index 9e37218e..6d7b1e83 100644
--- a/test/feature/attributes/Boolean.spec.ts
+++ b/test/feature/attributes/Boolean.spec.ts
@@ -22,18 +22,16 @@ describe('Feature – Attributes – Boolean', () => {
it('casts the value to `Boolean` when creating data', async () => {
const store = createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1 },
- { id: 2, bool: '' },
- { id: 3, bool: 'string' },
- { id: 4, bool: '0' },
- { id: 5, bool: 0 },
- { id: 6, bool: 1 },
- { id: 7, bool: true },
- { id: 8, bool: null }
- ]
- })
+ await User.create([
+ { id: 1 },
+ { id: 2, bool: '' },
+ { id: 3, bool: 'string' },
+ { id: 4, bool: '0' },
+ { id: 5, bool: 0 },
+ { id: 6, bool: 1 },
+ { id: 7, bool: true },
+ { id: 8, bool: null }
+ ])
const expected = createState({
users: {
@@ -54,18 +52,16 @@ describe('Feature – Attributes – Boolean', () => {
it('casts the value to `Boolean` when retrieving data', async () => {
createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1 },
- { id: 2, bool: '' },
- { id: 3, bool: 'string' },
- { id: 4, bool: '0' },
- { id: 5, bool: 0 },
- { id: 6, bool: 1 },
- { id: 7, bool: true },
- { id: 8, bool: null }
- ]
- })
+ await User.create([
+ { id: 1 },
+ { id: 2, bool: '' },
+ { id: 3, bool: 'string' },
+ { id: 4, bool: '0' },
+ { id: 5, bool: 0 },
+ { id: 6, bool: 1 },
+ { id: 7, bool: true },
+ { id: 8, bool: null }
+ ])
const users = User.all()
@@ -99,18 +95,16 @@ describe('Feature – Attributes – Boolean', () => {
createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1 },
- { id: 2, bool: '' },
- { id: 3, bool: 'string' },
- { id: 4, bool: '0' },
- { id: 5, bool: 0 },
- { id: 6, bool: 1 },
- { id: 7, bool: true },
- { id: 8, bool: null }
- ]
- })
+ await User.create([
+ { id: 1 },
+ { id: 2, bool: '' },
+ { id: 3, bool: 'string' },
+ { id: 4, bool: '0' },
+ { id: 5, bool: 0 },
+ { id: 6, bool: 1 },
+ { id: 7, bool: true },
+ { id: 8, bool: null }
+ ])
const users = User.all()
diff --git a/test/feature/attributes/Number.spec.ts b/test/feature/attributes/Number.spec.ts
index ffa1f7cb..218480af 100644
--- a/test/feature/attributes/Number.spec.ts
+++ b/test/feature/attributes/Number.spec.ts
@@ -22,18 +22,16 @@ describe('Feature – Attributes – Number', () => {
it('casts the value to `Number` when creating data', async () => {
const store = createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1 },
- { id: 2, num: 1 },
- { id: 3, num: 1.5 },
- { id: 4, num: '2' },
- { id: 5, num: '2.5' },
- { id: 6, num: true },
- { id: 7, num: false },
- { id: 8, num: null }
- ]
- })
+ await User.create([
+ { id: 1 },
+ { id: 2, num: 1 },
+ { id: 3, num: 1.5 },
+ { id: 4, num: '2' },
+ { id: 5, num: '2.5' },
+ { id: 6, num: true },
+ { id: 7, num: false },
+ { id: 8, num: null }
+ ])
const expected = createState({
users: {
@@ -54,16 +52,14 @@ describe('Feature – Attributes – Number', () => {
it('casts the value to `Number` when retrieving data', async () => {
createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1 },
- { id: 2, num: 1 },
- { id: 3, num: '2' },
- { id: 4, num: true },
- { id: 5, num: false },
- { id: 6, num: null }
- ]
- })
+ await User.create([
+ { id: 1 },
+ { id: 2, num: 1 },
+ { id: 3, num: '2' },
+ { id: 4, num: true },
+ { id: 5, num: false },
+ { id: 6, num: null }
+ ])
const users = User.all()
@@ -95,16 +91,14 @@ describe('Feature – Attributes – Number', () => {
createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1 },
- { id: 2, num: 1 },
- { id: 3, num: '2' },
- { id: 4, num: true },
- { id: 5, num: false },
- { id: 6, num: null }
- ]
- })
+ await User.create([
+ { id: 1 },
+ { id: 2, num: 1 },
+ { id: 3, num: '2' },
+ { id: 4, num: true },
+ { id: 5, num: false },
+ { id: 6, num: null }
+ ])
const users = User.all()
diff --git a/test/feature/attributes/String.spec.ts b/test/feature/attributes/String.spec.ts
index 1f92f96b..4e5b11c9 100644
--- a/test/feature/attributes/String.spec.ts
+++ b/test/feature/attributes/String.spec.ts
@@ -22,15 +22,13 @@ describe('Feature – Attributes – String', () => {
it('casts the value to `String` when creating data', async () => {
const store = createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1 },
- { id: 2, str: 'value' },
- { id: 3, str: 1 },
- { id: 4, str: true },
- { id: 5, str: null }
- ]
- })
+ await User.create([
+ { id: 1 },
+ { id: 2, str: 'value' },
+ { id: 3, str: 1 },
+ { id: 4, str: true },
+ { id: 5, str: null }
+ ])
const expected = createState({
users: {
@@ -48,15 +46,13 @@ describe('Feature – Attributes – String', () => {
it('casts the value to `String` when retrieving data', async () => {
createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1 },
- { id: 2, str: 'value' },
- { id: 3, str: 1 },
- { id: 4, str: true },
- { id: 5, str: null }
- ]
- })
+ await User.create([
+ { id: 1 },
+ { id: 2, str: 'value' },
+ { id: 3, str: 1 },
+ { id: 4, str: true },
+ { id: 5, str: null }
+ ])
const users = User.all()
@@ -87,15 +83,13 @@ describe('Feature – Attributes – String', () => {
createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1 },
- { id: 2, str: 'value' },
- { id: 3, str: 1 },
- { id: 4, str: true },
- { id: 5, str: null }
- ]
- })
+ await User.create([
+ { id: 1 },
+ { id: 2, str: 'value' },
+ { id: 3, str: 1 },
+ { id: 4, str: true },
+ { id: 5, str: null }
+ ])
const users = User.all()
diff --git a/test/feature/attributes/Uid.spec.ts b/test/feature/attributes/Uid.spec.ts
index b0871dc6..1854f631 100644
--- a/test/feature/attributes/Uid.spec.ts
+++ b/test/feature/attributes/Uid.spec.ts
@@ -28,9 +28,7 @@ describe('Feature – Attributes – Uid', () => {
it('generates uid as a default value', async () => {
const store = createStore([{ model: User }])
- await User.create({
- data: [{}, {}, {}]
- })
+ await User.create([{}, {}, {}])
const expected = createState({
users: {
@@ -46,12 +44,10 @@ describe('Feature – Attributes – Uid', () => {
it('will do nothing if the value exists', async () => {
const store = createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1, id2: 'id1' },
- { id: 2, id2: 'id2' }
- ]
- })
+ await User.create([
+ { id: 1, id2: 'id1' },
+ { id: 2, id2: 'id2' }
+ ])
const expected = createState({
users: {
@@ -85,7 +81,7 @@ describe('Feature – Attributes – Uid', () => {
it('generates user provided uid as a default value', async () => {
const store = createStore([{ model: User }])
- await User.create({ data: [{}] })
+ await User.create([{}])
const expected = createState({
users: {
diff --git a/test/feature/basics/Create.spec.ts b/test/feature/basics/Create.spec.ts
deleted file mode 100644
index 055fdba9..00000000
--- a/test/feature/basics/Create.spec.ts
+++ /dev/null
@@ -1,156 +0,0 @@
-import { createStore } from 'test/support/Helpers'
-import Model from '@/model/Model'
-
-describe('Feature – Basics – Create', () => {
- class User extends Model {
- static entity = 'users'
-
- static fields() {
- return {
- id: this.attr(null),
- name: this.attr('JD')
- }
- }
- }
-
- it('can create a data', async () => {
- const store = createStore([{ model: User }])
-
- await User.create({
- data: { id: 1, name: 'John Doe' }
- })
-
- const expected = {
- 1: { $id: '1', id: 1, name: 'John Doe' }
- }
-
- expect(store.state.entities.users.data[1]).not.toBeInstanceOf(User)
- expect(store.state.entities.users.data).toEqual(expected)
- })
-
- it('can create list of data', async () => {
- const store = createStore([{ model: User }])
-
- await store.dispatch('entities/users/create', {
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
-
- const expected = {
- 1: { $id: '1', id: 1, name: 'John Doe' },
- 2: { $id: '2', id: 2, name: 'Jane Doe' }
- }
-
- expect(store.state.entities.users.data).toEqual(expected)
- })
-
- it('replaces any existing records', async () => {
- const store = createStore([{ model: User }])
-
- await store.dispatch('entities/users/create', {
- data: { id: 1, name: 'John Doe' }
- })
-
- await store.dispatch('entities/users/create', {
- data: { id: 2, name: 'Jane Doe' }
- })
-
- const expected = {
- 2: { $id: '2', id: 2, name: 'Jane Doe' }
- }
-
- expect(store.state.entities.users.data).toEqual(expected)
- })
-
- it('cleans all existing records when passing empty object', async () => {
- const store = createStore([{ model: User }])
-
- await store.dispatch('entities/users/create', {
- data: { id: 1, name: 'John Doe' }
- })
-
- await store.dispatch('entities/users/create', {
- data: {}
- })
-
- const expected = {}
-
- expect(store.state.entities.users.data).toEqual(expected)
- })
-
- it('cleans all existing records when passing empty array', async () => {
- const store = createStore([{ model: User }])
-
- await store.dispatch('entities/users/create', {
- data: { id: 1, name: 'John Doe' }
- })
-
- await store.dispatch('entities/users/create', {
- data: []
- })
-
- const expected = {}
-
- expect(store.state.entities.users.data).toEqual(expected)
- })
-
- it('fills missing fields with the default value', async () => {
- const store = createStore([{ model: User }])
-
- await store.dispatch('entities/users/create', {
- data: { id: 1 }
- })
-
- const expected = {
- 1: { $id: '1', id: 1, name: 'JD' }
- }
-
- expect(store.state.entities.users.data).toEqual(expected)
- })
-
- it('returns a newly created data', async () => {
- const store = createStore([{ model: User }])
-
- const collection = await store.dispatch('entities/users/create', {
- data: { id: 1, name: 'John Doe' }
- })
-
- const expected = {
- users: [new User({ $id: '1', id: 1, name: 'John Doe' })]
- }
-
- expect(collection).toEqual(expected)
- })
-
- it('returns list of newly created data', async () => {
- const store = createStore([{ model: User }])
-
- const collection = await store.dispatch('entities/users/create', {
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
-
- const expected = {
- users: [
- new User({ $id: '1', id: 1, name: 'John Doe' }),
- new User({ $id: '2', id: 2, name: 'Jane Doe' })
- ]
- }
-
- expect(collection).toEqual(expected)
- })
-
- it('returns null when creating empty data', async () => {
- const store = createStore([{ model: User }])
-
- const collection = await store.dispatch('entities/users/create', {
- data: {}
- })
-
- expect(collection).toEqual({})
- })
-})
diff --git a/test/feature/basics/Delete.spec.ts b/test/feature/basics/Delete.spec.ts
index 968d396b..ba0481ea 100644
--- a/test/feature/basics/Delete.spec.ts
+++ b/test/feature/basics/Delete.spec.ts
@@ -37,9 +37,7 @@ describe('Feature – Basics – Delete', () => {
it('can delete a record by specifying the id', async () => {
const store = getStore()
- await User.create({
- data: [{ id: 1 }, { id: 2 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }])
await User.delete(1)
@@ -56,9 +54,7 @@ describe('Feature – Basics – Delete', () => {
it('can delete a record by specifying id as a string', async () => {
const store = getStore()
- await User.create({
- data: [{ id: 'string_id_1' }, { id: 'string_id_2' }]
- })
+ await User.create([{ id: 'string_id_1' }, { id: 'string_id_2' }])
await User.delete('string_id_2')
@@ -75,13 +71,11 @@ describe('Feature – Basics – Delete', () => {
it('can delete records by specifying a closure', async () => {
const store = getStore()
- await User.create({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' },
- { id: 3, name: 'Jane Doe' }
- ]
- })
+ await User.create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' },
+ { id: 3, name: 'Jane Doe' }
+ ])
await User.delete((user) => user.name === 'Jane Doe')
@@ -98,9 +92,7 @@ describe('Feature – Basics – Delete', () => {
it('does nothing if the specified id does not exist', async () => {
const store = getStore()
- await User.create({
- data: [{ id: 1 }, { id: 2 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }])
await User.delete(3)
@@ -118,9 +110,7 @@ describe('Feature – Basics – Delete', () => {
it('returns deleted item', async () => {
const store = getStore()
- await User.create({
- data: [{ id: 1 }, { id: 2 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }])
const user = await User.delete(1)
@@ -140,13 +130,11 @@ describe('Feature – Basics – Delete', () => {
it('returns all deleted records as a collection when specifying a closure', async () => {
const store = getStore()
- await User.create({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' },
- { id: 3, name: 'Jane Doe' }
- ]
- })
+ await User.create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' },
+ { id: 3, name: 'Jane Doe' }
+ ])
const users = await User.delete((user) => user.name === 'Jane Doe')
@@ -169,12 +157,10 @@ describe('Feature – Basics – Delete', () => {
it('deletes itself by instance method', async () => {
const store = getStore()
- await User.create({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ await User.create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
const user = User.find(2) as User
@@ -193,9 +179,7 @@ describe('Feature – Basics – Delete', () => {
it('can delete all records in the entity', async () => {
const store = getStore()
- await User.create({
- data: [{ id: 1 }, { id: 2 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }])
await User.deleteAll()
@@ -210,9 +194,7 @@ describe('Feature – Basics – Delete', () => {
it('can delete all records in the entity by the instance method', async () => {
const store = getStore()
- await User.create({
- data: [{ id: 1 }, { id: 2 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }])
await new User().$deleteAll()
@@ -227,13 +209,9 @@ describe('Feature – Basics – Delete', () => {
it('can delete all records in the entire entities', async () => {
const store = getStore()
- await User.create({
- data: [{ id: 1 }, { id: 2 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }])
- await Post.create({
- data: [{ id: 3 }, { id: 4 }]
- })
+ await Post.create([{ id: 3 }, { id: 4 }])
await store.dispatch('entities/deleteAll')
diff --git a/test/feature/basics/Delete_Composite_Key.spec.ts b/test/feature/basics/Delete_Composite_Key.spec.ts
index 45ee79d6..ba3aa0e9 100644
--- a/test/feature/basics/Delete_Composite_Key.spec.ts
+++ b/test/feature/basics/Delete_Composite_Key.spec.ts
@@ -20,12 +20,10 @@ describe('Feature – Basics – Delete Composite Key', () => {
it('can delete a record by specifying the composite id as an array', async () => {
const store = getStore()
- await User.create({
- data: [
- { first_id: 1, second_id: 1 },
- { first_id: 1, second_id: 2 }
- ]
- })
+ await User.create([
+ { first_id: 1, second_id: 1 },
+ { first_id: 1, second_id: 2 }
+ ])
await User.delete([1, 1])
@@ -41,12 +39,10 @@ describe('Feature – Basics – Delete Composite Key', () => {
it('deletes itself by instance method even when model has composite primary key', async () => {
const store = getStore()
- await User.create({
- data: [
- { first_id: 1, second_id: 2 },
- { first_id: 3, second_id: 4 }
- ]
- })
+ await User.create([
+ { first_id: 1, second_id: 2 },
+ { first_id: 3, second_id: 4 }
+ ])
const user = User.find([1, 2]) as User
diff --git a/test/feature/basics/Exists.spec.ts b/test/feature/basics/Exists.spec.ts
index 1c04fe67..ba7b056d 100644
--- a/test/feature/basics/Exists.spec.ts
+++ b/test/feature/basics/Exists.spec.ts
@@ -19,21 +19,17 @@ describe('Feature – Exists', () => {
})
it('can check if a database contains data by exists method', async () => {
- const store = createStore([{ model: User }])
+ createStore([{ model: User }])
- await store.dispatch('entities/users/create', {
- data: [{ id: 1 }, { id: 2 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }])
expect(User.exists()).toBe(true)
})
it('can check if a query chain would return data', async () => {
- const store = createStore([{ model: User }])
+ createStore([{ model: User }])
- await store.dispatch('entities/users/create', {
- data: [{ id: 1 }, { id: 2 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }])
expect(
User.query()
diff --git a/test/feature/basics/Insert.spec.ts b/test/feature/basics/Insert.spec.ts
deleted file mode 100644
index 22617b6e..00000000
--- a/test/feature/basics/Insert.spec.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { createStore, createState } from 'test/support/Helpers'
-import Model from '@/model/Model'
-
-describe('Feature – Basics – Insert', () => {
- class User extends Model {
- static entity = 'users'
-
- static fields() {
- return {
- id: this.attr(null),
- name: this.string('John Doe')
- }
- }
- }
-
- const getStore = () => createStore([{ model: User }])
-
- it('can insert a record', async () => {
- const store = getStore()
-
- await User.insert({
- data: { id: 1, name: 'John Doe' }
- })
-
- const expected = createState({
- users: {
- 1: { $id: '1', id: 1, name: 'John Doe' }
- }
- })
-
- expect(store.state.entities).toEqual(expected)
- })
-
- it('does nothing if an empty object is passed', async () => {
- const store = getStore()
-
- await User.insert({
- data: {}
- })
-
- const expected = createState({
- users: {}
- })
-
- expect(store.state.entities).toEqual(expected)
- })
-
- it('can insert record with primary key value of `null`', async () => {
- const store = getStore()
-
- await User.insert({
- data: { id: null, name: 'John Doe' }
- })
-
- const expected = createState({
- users: {
- $uid1: { $id: '$uid1', id: '$uid1', name: 'John Doe' }
- }
- })
-
- expect(store.state.entities).toEqual(expected)
- })
-})
diff --git a/test/feature/basics/New.spec.ts b/test/feature/basics/New.spec.ts
deleted file mode 100644
index 843852a4..00000000
--- a/test/feature/basics/New.spec.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { createStore } from 'test/support/Helpers'
-import Model from '@/model/Model'
-
-describe('Feature – Basics – New', () => {
- class User extends Model {
- static entity = 'users'
-
- // @Uid
- id!: string
-
- // @Str('Default Doe')
- name!: string
-
- static fields() {
- return {
- id: this.uid(),
- name: this.attr('Default Doe')
- }
- }
- }
-
- it('can create new data with all fields filled by default values', async () => {
- const store = createStore([{ model: User }])
-
- await User.new()
- await User.new()
-
- expect(store.state.entities.users.data.$uid1.$id).toBe('$uid1')
- expect(store.state.entities.users.data.$uid1.id).toBe('$uid1')
- expect(store.state.entities.users.data.$uid1.name).toBe('Default Doe')
-
- expect(store.state.entities.users.data.$uid2.$id).toBe('$uid2')
- expect(store.state.entities.users.data.$uid2.id).toBe('$uid2')
- expect(store.state.entities.users.data.$uid2.name).toBe('Default Doe')
- })
-
- it('returns newly created instance', async () => {
- createStore([{ model: User }])
-
- const user = (await User.new()) as User
-
- expect(user.$id).toBe('$uid3')
- expect(user.id).toBe('$uid3')
- expect(user.name).toBe('Default Doe')
- })
-})
diff --git a/test/feature/basics/PersistOptions.spec.ts b/test/feature/basics/PersistOptions.spec.ts
index abdde0a4..5d3c2a85 100644
--- a/test/feature/basics/PersistOptions.spec.ts
+++ b/test/feature/basics/PersistOptions.spec.ts
@@ -1,7 +1,7 @@
import { createStore } from 'test/support/Helpers'
import Model from '@/model/Model'
-describe('Feature – Basics – Create', () => {
+describe('Feature – Basics – Persist Options', () => {
class User extends Model {
static entity = 'users'
@@ -28,21 +28,17 @@ describe('Feature – Basics – Create', () => {
it('can `create` related records', async () => {
const store = createStore([{ model: User }, { model: Post }])
- await User.create({
- data: { id: 1 }
- })
+ await User.create({ id: 1 })
- await Post.create({
- data: { id: 1 }
- })
+ await Post.create({ id: 1 })
- await User.insert({
- data: {
+ await User.insert(
+ {
id: 2,
posts: [{ id: 2 }, { id: 3 }]
},
- create: ['posts']
- })
+ { create: ['posts'] }
+ )
expect(store.state.entities.users.data['1'].id).toBe(1)
expect(store.state.entities.users.data['2'].id).toBe(2)
@@ -54,21 +50,17 @@ describe('Feature – Basics – Create', () => {
it('can `insert` related records', async () => {
const store = createStore([{ model: User }, { model: Post }])
- await User.create({
- data: { id: 1 }
- })
+ await User.create({ id: 1 })
- await Post.create({
- data: { id: 1 }
- })
+ await Post.create({ id: 1 })
- await User.create({
- data: {
+ await User.create(
+ {
id: 2,
posts: [{ id: 2 }, { id: 3 }]
},
- insert: ['posts']
- })
+ { insert: ['posts'] }
+ )
expect(store.state.entities.users.data['1']).toBe(undefined)
expect(store.state.entities.users.data['2'].id).toBe(2)
@@ -80,27 +72,23 @@ describe('Feature – Basics – Create', () => {
it('can `update` related records', async () => {
const store = createStore([{ model: User }, { model: Post }])
- await User.create({
- data: { id: 1 }
- })
+ await User.create({ id: 1 })
- await Post.create({
- data: [
- { id: 1, title: 'Title 01' },
- { id: 2, title: 'Title 02' }
- ]
- })
+ await Post.create([
+ { id: 1, title: 'Title 01' },
+ { id: 2, title: 'Title 02' }
+ ])
- await User.create({
- data: {
+ await User.create(
+ {
id: 2,
posts: [
{ id: 1, title: 'Title 01-Edit' },
{ id: 2, title: 'Title 02-Edit' }
]
},
- update: ['posts']
- })
+ { update: ['posts'] }
+ )
expect(store.state.entities.users.data['1']).toBe(undefined)
expect(store.state.entities.users.data['2'].id).toBe(2)
@@ -113,27 +101,23 @@ describe('Feature – Basics – Create', () => {
it('can `insertOrUpdate` related records', async () => {
const store = createStore([{ model: User }, { model: Post }])
- await User.create({
- data: { id: 1 }
- })
+ await User.create({ id: 1 })
- await Post.create({
- data: [
- { id: 1, title: 'Title 01' },
- { id: 2, title: 'Title 02' }
- ]
- })
+ await Post.create([
+ { id: 1, title: 'Title 01' },
+ { id: 2, title: 'Title 02' }
+ ])
- await User.create({
- data: {
+ await User.create(
+ {
id: 2,
posts: [
{ id: 2, title: 'Title 02-Edit' },
{ id: 3, title: 'Title 03' }
]
},
- insertOrUpdate: ['posts']
- })
+ { insertOrUpdate: ['posts'] }
+ )
expect(store.state.entities.users.data['1']).toBe(undefined)
expect(store.state.entities.users.data['2'].id).toBe(2)
diff --git a/test/feature/basics/Retrieve.spec.ts b/test/feature/basics/Retrieve.spec.ts
index 4f4cd557..156a5080 100644
--- a/test/feature/basics/Retrieve.spec.ts
+++ b/test/feature/basics/Retrieve.spec.ts
@@ -138,23 +138,23 @@ describe('Feature – Retrieve', () => {
describe('#first', () => {
it('can retrieve the first item from the store', async () => {
- createStore([{ model: User }])
+ const store = createStore([{ model: User }])
- await User.insert({
+ await store.dispatch('entities/users/insert', {
data: [{ id: 1 }, { id: 2 }]
})
const expected = { $id: '1', id: 1 }
- const user = User.query().first()
+ const user = store.getters['entities/users/query']().first()
expect(user).toEqual(expected)
})
it('returns `null` if it can not find any record', () => {
- createStore([{ model: User }])
+ const store = createStore([{ model: User }])
- const user = User.query().first()
+ const user = store.getters['entities/users/query']().first()
expect(user).toBe(null)
})
@@ -162,23 +162,23 @@ describe('Feature – Retrieve', () => {
describe('#last', () => {
it('can retrieve the last item from the store', async () => {
- createStore([{ model: User }])
+ const store = createStore([{ model: User }])
- await User.insert({
+ await store.dispatch('entities/users/insert', {
data: [{ id: 1 }, { id: 2 }]
})
const expected = { $id: '2', id: 2 }
- const user = User.query().last()
+ const user = store.getters['entities/users/query']().last()
expect(user).toEqual(expected)
})
it('returns `null` if it can not find any record', () => {
- createStore([{ model: User }])
+ const store = createStore([{ model: User }])
- const user = User.query().last()
+ const user = store.getters['entities/users/query']().last()
expect(user).toBe(null)
})
diff --git a/test/feature/basics/Update.spec.ts b/test/feature/basics/Update.spec.ts
index 49ef34d9..5fee0347 100644
--- a/test/feature/basics/Update.spec.ts
+++ b/test/feature/basics/Update.spec.ts
@@ -24,15 +24,17 @@ describe('Feature – Basics – Update', () => {
}
}
- createStore([{ model: User }])
+ const store = createStore([{ model: User }])
- await User.create({
+ await store.dispatch('entities/users/create', {
data: { id: 0, name: 'John Doe', age: 30 }
})
- await User.update({ id: 0, age: 24 })
+ await store.dispatch('entities/users/update', {
+ data: { id: 0, age: 24 }
+ })
- const user = User.find(0) as User
+ const user = store.getters['entities/users/find'](0)
expect(user.name).toBe('John Doe')
expect(user.age).toBe(24)
@@ -613,13 +615,13 @@ describe('Feature – Basics – Update', () => {
}
}
- createStore([{ model: User }])
+ const store = createStore([{ model: User }])
- await User.create({
+ await store.dispatch('entities/users/create', {
data: { id: 1, name: 'John Doe' }
})
- await User.update({
+ await store.dispatch('entities/users/update', {
where: 1,
data: { id: 2 }
})
diff --git a/test/feature/hooks/Global.spec.ts b/test/feature/hooks/Global.spec.ts
index cc921e83..2b08fd76 100644
--- a/test/feature/hooks/Global.spec.ts
+++ b/test/feature/hooks/Global.spec.ts
@@ -28,13 +28,11 @@ describe('Feature – Hooks – Global', () => {
createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1, role: 'admin' },
- { id: 2, role: 'admin' },
- { id: 3, role: 'user' }
- ]
- })
+ await User.create([
+ { id: 1, role: 'admin' },
+ { id: 2, role: 'admin' },
+ { id: 3, role: 'user' }
+ ])
const expected = [{ $id: '1', id: 1, role: 'admin' }]
@@ -88,6 +86,7 @@ describe('Feature – Hooks – Global', () => {
const persistedHookId1 = Query.on('afterWhere', callbackFunction)
expect(Query.hooks.afterWhere.length).toBe(1)
+
User.all()
expect(Query.hooks.afterWhere.length).toBe(1)
diff --git a/test/feature/hooks/Global_Create.spec.ts b/test/feature/hooks/Global_Create.spec.ts
index 766c57cb..7c4d655f 100644
--- a/test/feature/hooks/Global_Create.spec.ts
+++ b/test/feature/hooks/Global_Create.spec.ts
@@ -32,12 +32,10 @@ describe('Feature – Hooks – Global Create', () => {
model.role = 'admin'
})
- await User.create({
- data: [
- { id: 1, role: 'admin' },
- { id: 2, role: 'user' }
- ]
- })
+ await User.create([
+ { id: 1, role: 'admin' },
+ { id: 2, role: 'user' }
+ ])
const users = User.all()
@@ -53,12 +51,10 @@ describe('Feature – Hooks – Global Create', () => {
model.role = 'not admin'
})
- await User.create({
- data: [
- { id: 1, role: 'admin' },
- { id: 2, role: 'user' }
- ]
- })
+ await User.create([
+ { id: 1, role: 'admin' },
+ { id: 2, role: 'user' }
+ ])
const users = User.all()
@@ -73,12 +69,10 @@ describe('Feature – Hooks – Global Create', () => {
}
})
- await User.create({
- data: [
- { id: 1, role: 'admin' },
- { id: 2, role: 'user' }
- ]
- })
+ await User.create([
+ { id: 1, role: 'admin' },
+ { id: 2, role: 'user' }
+ ])
const users = User.all()
@@ -93,9 +87,7 @@ describe('Feature – Hooks – Global Create', () => {
hit = true
})
- await User.create({
- data: { id: 1, role: 'admin' }
- })
+ await User.create({ id: 1, role: 'admin' })
expect(hit).toBe(true)
})
diff --git a/test/feature/hooks/Global_Delete.spec.ts b/test/feature/hooks/Global_Delete.spec.ts
index ad9aba00..5c8c4147 100644
--- a/test/feature/hooks/Global_Delete.spec.ts
+++ b/test/feature/hooks/Global_Delete.spec.ts
@@ -34,9 +34,7 @@ describe('Feature – Hooks – Global Delete', () => {
hit = true
})
- await User.create({
- data: { id: 1, role: 'user' }
- })
+ await User.create({ id: 1, role: 'user' })
await User.delete(1)
@@ -51,9 +49,7 @@ describe('Feature – Hooks – Global Delete', () => {
return false
})
- await User.create({
- data: { id: 1, role: 'user' }
- })
+ await User.create({ id: 1, role: 'user' })
await User.delete(1)
@@ -69,9 +65,7 @@ describe('Feature – Hooks – Global Delete', () => {
hit = true
})
- await User.create({
- data: { id: 1, role: 'user' }
- })
+ await User.create({ id: 1, role: 'user' })
await User.delete(1)
diff --git a/test/feature/hooks/Global_Update.spec.ts b/test/feature/hooks/Global_Update.spec.ts
index 1afd43a5..2cf4ad43 100644
--- a/test/feature/hooks/Global_Update.spec.ts
+++ b/test/feature/hooks/Global_Update.spec.ts
@@ -32,13 +32,9 @@ describe('Feature – Hooks – Global Update', () => {
model.role = 'admin'
})
- await User.create({
- data: { id: 1, role: 'user' }
- })
+ await User.create({ id: 1, role: 'user' })
- await User.update({
- data: { id: 1, role: 'guest' }
- })
+ await User.update({ id: 1, role: 'guest' })
const user = User.find(1)
@@ -52,13 +48,9 @@ describe('Feature – Hooks – Global Update', () => {
}
})
- await User.create({
- data: { id: 1, role: 'user' }
- })
+ await User.create({ id: 1, role: 'user' })
- await User.update({
- data: { id: 1, role: 'admin' }
- })
+ await User.update({ id: 1, role: 'admin' })
const user = User.find(1)
@@ -72,13 +64,9 @@ describe('Feature – Hooks – Global Update', () => {
hit = true
})
- await User.create({
- data: { id: 1, role: 'user' }
- })
+ await User.create({ id: 1, role: 'user' })
- await User.update({
- data: { id: 1, role: 'admin' }
- })
+ await User.update({ id: 1, role: 'admin' })
expect(hit).toBe(true)
})
diff --git a/test/feature/hooks/Local_Binding.spec.ts b/test/feature/hooks/Local_Binding.spec.ts
index 6124251b..f0e2e193 100644
--- a/test/feature/hooks/Local_Binding.spec.ts
+++ b/test/feature/hooks/Local_Binding.spec.ts
@@ -34,9 +34,7 @@ describe('Hooks – Local Binding', () => {
createStore([{ model: User }])
- await User.insert({
- data: { id: 1, name: 'John Doe', age: 20 }
- })
+ await User.insert({ id: 1, name: 'John Doe', age: 20 })
const expected = { $id: '1', id: 1, name: 'John Doe', age: 30 }
diff --git a/test/feature/hooks/Local_Create.spec.ts b/test/feature/hooks/Local_Create.spec.ts
index f2366fe9..ae91f40a 100644
--- a/test/feature/hooks/Local_Create.spec.ts
+++ b/test/feature/hooks/Local_Create.spec.ts
@@ -30,9 +30,7 @@ describe('Hooks – Local Create', () => {
createStore([{ model: User }])
- await User.create({
- data: { id: 1, name: 'John Doe', age: 20 }
- })
+ await User.create({ id: 1, name: 'John Doe', age: 20 })
const expected = { $id: '1', id: 1, name: 'John Doe', age: 30 }
@@ -67,9 +65,7 @@ describe('Hooks – Local Create', () => {
createStore([{ model: User }])
- await User.create({
- data: { id: 1, name: 'John Doe', age: 20 }
- })
+ await User.create({ id: 1, name: 'John Doe', age: 20 })
const expected = { $id: '1', id: 1, name: 'John Doe', age: 20 }
@@ -106,12 +102,10 @@ describe('Hooks – Local Create', () => {
createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1, name: 'John Doe', age: 20 },
- { id: 2, name: 'Jane Doe', age: 24 }
- ]
- })
+ await User.create([
+ { id: 1, name: 'John Doe', age: 20 },
+ { id: 2, name: 'Jane Doe', age: 24 }
+ ])
const users = User.all()
@@ -148,8 +142,6 @@ describe('Hooks – Local Create', () => {
createStore([{ model: User }])
- await User.create({
- data: { id: 1, name: 'John Doe', age: 20 }
- })
+ await User.create({ id: 1, name: 'John Doe', age: 20 })
})
})
diff --git a/test/feature/hooks/Local_Delete.spec.ts b/test/feature/hooks/Local_Delete.spec.ts
index e2d3356a..fc219989 100644
--- a/test/feature/hooks/Local_Delete.spec.ts
+++ b/test/feature/hooks/Local_Delete.spec.ts
@@ -28,9 +28,7 @@ describe('Hooks – Local Delete', () => {
createStore([{ model: User }])
- await User.create({
- data: { id: 1, name: 'John Doe' }
- })
+ await User.create({ id: 1, name: 'John Doe' })
await User.delete(1)
@@ -65,12 +63,10 @@ describe('Hooks – Local Delete', () => {
createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ await User.create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
await User.delete(2)
@@ -109,12 +105,10 @@ describe('Hooks – Local Delete', () => {
createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ await User.create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
await User.delete(2)
diff --git a/test/feature/hooks/Local_Insert.spec.ts b/test/feature/hooks/Local_Insert.spec.ts
index 3c2bfe90..54f6a07a 100644
--- a/test/feature/hooks/Local_Insert.spec.ts
+++ b/test/feature/hooks/Local_Insert.spec.ts
@@ -30,9 +30,7 @@ describe('Hooks – Local Insert', () => {
createStore([{ model: User }])
- await User.insert({
- data: { id: 1, name: 'John Doe', age: 20 }
- })
+ await User.insert({ id: 1, name: 'John Doe', age: 20 })
const expected = { $id: '1', id: 1, name: 'John Doe', age: 30 }
@@ -67,9 +65,7 @@ describe('Hooks – Local Insert', () => {
createStore([{ model: User }])
- await User.insert({
- data: { id: 1, name: 'John Doe', age: 20 }
- })
+ await User.insert({ id: 1, name: 'John Doe', age: 20 })
const expected = { $id: '1', id: 1, name: 'John Doe', age: 20 }
@@ -148,8 +144,6 @@ describe('Hooks – Local Insert', () => {
createStore([{ model: User }])
- await User.insert({
- data: { id: 1, name: 'John Doe', age: 20 }
- })
+ await User.insert({ id: 1, name: 'John Doe', age: 20 })
})
})
diff --git a/test/feature/hooks/Local_Update.spec.ts b/test/feature/hooks/Local_Update.spec.ts
index 6e479309..ae861af4 100644
--- a/test/feature/hooks/Local_Update.spec.ts
+++ b/test/feature/hooks/Local_Update.spec.ts
@@ -30,9 +30,7 @@ describe('Hooks – Local Update', () => {
createStore([{ model: User }])
- await User.insert({
- data: { id: 1, name: 'John Doe', age: 20 }
- })
+ await User.insert({ id: 1, name: 'John Doe', age: 20 })
await User.update({ id: 1, name: 'Jane Doe' })
@@ -69,9 +67,7 @@ describe('Hooks – Local Update', () => {
createStore([{ model: User }])
- await User.insert({
- data: { id: 1, name: 'John Doe', age: 20 }
- })
+ await User.insert({ id: 1, name: 'John Doe', age: 20 })
await User.update({ id: 1, name: 'Jane Doe', age: 30 })
@@ -108,12 +104,10 @@ describe('Hooks – Local Update', () => {
createStore([{ model: User }])
- await User.insert({
- data: [
- { id: 1, name: 'John Doe', age: 20 },
- { id: 2, name: 'Jane Doe', age: 24 }
- ]
- })
+ await User.insert([
+ { id: 1, name: 'John Doe', age: 20 },
+ { id: 2, name: 'Jane Doe', age: 24 }
+ ])
await User.update({ id: 1, name: 'Johnny Doe' })
@@ -158,9 +152,7 @@ describe('Hooks – Local Update', () => {
createStore([{ model: User }])
- await User.insert({
- data: { id: 1, name: 'John Doe', age: 20 }
- })
+ await User.insert({ id: 1, name: 'John Doe', age: 20 })
await User.update({ id: 1, age: 30 })
diff --git a/test/feature/inheritance/Inheritance.spec.ts b/test/feature/inheritance/Inheritance.spec.ts
index 0ec1bc1d..8c87cec5 100644
--- a/test/feature/inheritance/Inheritance.spec.ts
+++ b/test/feature/inheritance/Inheritance.spec.ts
@@ -63,9 +63,7 @@ describe('Feature – Inheritance', () => {
it('sets right class for derived entity', async () => {
createStore([Person, Adult])
- await Adult.insert({
- data: { id: 1, name: 'John Doe' }
- })
+ await Adult.insert({ id: 1, name: 'John Doe' })
expect(Adult.find(1)).toBeInstanceOf(Adult)
})
@@ -73,9 +71,7 @@ describe('Feature – Inheritance', () => {
it('should inherit fields from root entity', async () => {
createStore([Person, Adult])
- await Adult.insert({
- data: { id: 2, name: 'John Doe' }
- })
+ await Adult.insert({ id: 2, name: 'John Doe' })
const adult = Adult.find(2) as Adult
@@ -86,13 +82,9 @@ describe('Feature – Inheritance', () => {
it('should have own fields only on derived entity', async () => {
createStore([Person, Adult])
- await Person.insert({
- data: { id: 1, name: 'John' }
- })
+ await Person.insert({ id: 1, name: 'John' })
- await Adult.insert({
- data: { id: 2, name: 'Jane', job: 'Software Engineer' }
- })
+ await Adult.insert({ id: 2, name: 'Jane', job: 'Software Engineer' })
const person = Person.find(1) as any
expect(person.job).toBe(undefined)
diff --git a/test/feature/inheritance/Inheritance_CRUD.spec.ts b/test/feature/inheritance/Inheritance_CRUD.spec.ts
index f56dbb03..3ae829af 100644
--- a/test/feature/inheritance/Inheritance_CRUD.spec.ts
+++ b/test/feature/inheritance/Inheritance_CRUD.spec.ts
@@ -56,13 +56,9 @@ describe('Feature - Inheritance - CRUD', () => {
it('can get mixed results when calling getter of root entity', async () => {
createStore([Person, Adult])
- await Person.insert({
- data: { id: 1, name: 'John' }
- })
+ await Person.insert({ id: 1, name: 'John' })
- await Adult.insert({
- data: { id: 2, name: 'Jane', job: 'Software Engineer' }
- })
+ await Adult.insert({ id: 2, name: 'Jane', job: 'Software Engineer' })
const people = Person.query()
.orderBy('id')
@@ -81,13 +77,9 @@ describe('Feature - Inheritance - CRUD', () => {
it('can get only derived results when calling getter of derived entity', async () => {
createStore([Person, Adult])
- await Person.insert({
- data: { id: 1, name: 'John' }
- })
+ await Person.insert({ id: 1, name: 'John' })
- await Adult.insert({
- data: { id: 2, name: 'Jane', job: 'Software Engineer' }
- })
+ await Adult.insert({ id: 2, name: 'Jane', job: 'Software Engineer' })
const adults = Adult.all()
@@ -100,19 +92,15 @@ describe('Feature - Inheritance - CRUD', () => {
it('should clean only corresponding entities (and derived ones) when calling create', async () => {
createStore([Person, Adult, Child])
- await Person.insert({
- data: [
- { id: 1, name: 'A', type: 'PERSON' },
- { id: 2, name: 'B', type: 'ADULT' },
- { id: 3, name: 'D', type: 'CHILD' }
- ]
- })
+ await Person.insert([
+ { id: 1, name: 'A', type: 'PERSON' },
+ { id: 2, name: 'B', type: 'ADULT' },
+ { id: 3, name: 'D', type: 'CHILD' }
+ ])
expect(Person.all().length).toBe(3)
- await Adult.create({
- data: { id: 4, name: 'E' }
- })
+ await Adult.create({ id: 4, name: 'E' })
expect(Person.all().length).toBe(3)
@@ -125,19 +113,14 @@ describe('Feature - Inheritance - CRUD', () => {
it('should update derived data without changing its class when calling base update', async () => {
createStore([Person, Adult])
- await Person.create({
- data: [
- { id: 1, name: 'A', type: 'PERSON' },
- { id: 2, name: 'B', type: 'ADULT' }
- ]
- })
+ await Person.create([
+ { id: 1, name: 'A', type: 'PERSON' },
+ { id: 2, name: 'B', type: 'ADULT' }
+ ])
expect((Person.find(2) as Adult).name).toBe('B')
- await Person.update({
- where: 2,
- data: { name: 'C' }
- })
+ await Person.update({ name: 'C' }, { where: 2 })
const adult = Person.find(2) as Adult
@@ -148,17 +131,15 @@ describe('Feature - Inheritance - CRUD', () => {
it('should update data with a closure using instanceof', async () => {
createStore([Person, Adult])
- await Person.create({
- data: [
- { id: 1, name: 'A', type: 'PERSON' },
- { id: 2, name: 'B', type: 'ADULT' }
- ]
- })
+ await Person.create([
+ { id: 1, name: 'A', type: 'PERSON' },
+ { id: 2, name: 'B', type: 'ADULT' }
+ ])
- await Person.update({
- where: (record) => record instanceof Adult,
- data: { name: 'C' }
- })
+ await Person.update(
+ { name: 'C' },
+ { where: (record) => record instanceof Adult }
+ )
const adult = Person.find(2) as Adult
@@ -170,16 +151,17 @@ describe('Feature - Inheritance - CRUD', () => {
const store = createStore([Person, Adult])
await Person.create({
- data: { id: 1, name: 'John Doe', job: 'Software Engineer', type: 'ADULT' }
+ id: 1,
+ name: 'John Doe',
+ job: 'Software Engineer',
+ type: 'ADULT'
})
- await Person.insertOrUpdate({
- data: [
- { id: 1, name: 'John Doe', job: 'Writer', type: 'ADULT' },
- { id: 2, name: 'Jane Doe', job: 'QA', type: 'ADULT' },
- { id: 3, name: 'Jane Doe', type: 'PERSON' }
- ]
- })
+ await Person.insertOrUpdate([
+ { id: 1, name: 'John Doe', job: 'Writer', type: 'ADULT' },
+ { id: 2, name: 'Jane Doe', job: 'QA', type: 'ADULT' },
+ { id: 3, name: 'Jane Doe', type: 'PERSON' }
+ ])
const expected = createState({
person: {
@@ -196,17 +178,15 @@ describe('Feature - Inheritance - CRUD', () => {
it('should update only derived data when field is a derived entity field', async () => {
createStore([Person, Adult])
- await Person.create({
- data: [
- { id: 1, name: 'A', type: 'PERSON' },
- { id: 2, name: 'B', job: 'Software Engineer', type: 'ADULT' }
- ]
- })
+ await Person.create([
+ { id: 1, name: 'A', type: 'PERSON' },
+ { id: 2, name: 'B', job: 'Software Engineer', type: 'ADULT' }
+ ])
- await Person.update({
- where: (record) => record instanceof Person,
- data: { job: 'Writer' }
- })
+ await Person.update(
+ { job: 'Writer' },
+ { where: (record) => record instanceof Person }
+ )
const person = Person.find(1) as any
expect(person.job).toBe(undefined)
@@ -218,12 +198,10 @@ describe('Feature - Inheritance - CRUD', () => {
it('should delete record correctly when manipulating a derived entity', async () => {
createStore([Person, Adult])
- await Person.create({
- data: [
- { id: 1, name: 'A', type: 'PERSON' },
- { id: 2, name: 'B', type: 'ADULT' }
- ]
- })
+ await Person.create([
+ { id: 1, name: 'A', type: 'PERSON' },
+ { id: 2, name: 'B', type: 'ADULT' }
+ ])
expect(Person.all().length).toBe(2)
@@ -238,12 +216,10 @@ describe('Feature - Inheritance - CRUD', () => {
it('should delete only derived records when calling deleteAll on derived entity', async () => {
createStore([Person, Adult])
- await Person.create({
- data: [
- { id: 1, name: 'A', type: 'PERSON' },
- { id: 2, name: 'B', type: 'ADULT' }
- ]
- })
+ await Person.create([
+ { id: 1, name: 'A', type: 'PERSON' },
+ { id: 2, name: 'B', type: 'ADULT' }
+ ])
expect(Person.all().length).toBe(2)
diff --git a/test/feature/inheritance/Inheritance_Discriminator.spec.ts b/test/feature/inheritance/Inheritance_Discriminator.spec.ts
index 3204d7bb..2b5f3ba2 100644
--- a/test/feature/inheritance/Inheritance_Discriminator.spec.ts
+++ b/test/feature/inheritance/Inheritance_Discriminator.spec.ts
@@ -28,9 +28,7 @@ describe('Feature - Inheritance - Discriminator field', () => {
createStore([Person, Adult])
- await Person.insert({
- data: { id: 1, name: 'John Doe', type: 'ADULT' }
- })
+ await Person.insert({ id: 1, name: 'John Doe', type: 'ADULT' })
expect(Adult.find(1)).toBeInstanceOf(Adult)
})
@@ -64,9 +62,7 @@ describe('Feature - Inheritance - Discriminator field', () => {
createStore([Person, Adult])
- await Person.insert({
- data: { id: 1, name: 'John Doe', the_key: 'ADULT' }
- })
+ await Person.insert({ id: 1, name: 'John Doe', the_key: 'ADULT' })
expect(Adult.find(1)).toBeInstanceOf(Adult)
})
@@ -104,14 +100,12 @@ describe('Feature - Inheritance - Discriminator field', () => {
createStore([Person, Adult, Child])
- await Person.insert({
- data: [
- { id: 1, name: 'John Doe', type: 'ADULT' },
- { id: 2, name: 'John Doe Jr', type: 'CHILD' },
- { id: 3, name: 'Doe', type: 'PERSON' },
- { id: 4, name: 'Jane Doe', type: 'ADULT' }
- ]
- })
+ await Person.insert([
+ { id: 1, name: 'John Doe', type: 'ADULT' },
+ { id: 2, name: 'John Doe Jr', type: 'CHILD' },
+ { id: 3, name: 'Doe', type: 'PERSON' },
+ { id: 4, name: 'Jane Doe', type: 'ADULT' }
+ ])
const adults = Adult.all()
expect(adults.length).toBe(2)
@@ -155,9 +149,7 @@ describe('Feature - Inheritance - Discriminator field', () => {
createStore([Person, Adult])
// Inserting through "adult" model.
- await Adult.create({
- data: { id: 1, name: 'John Doe' }
- })
+ await Adult.create({ id: 1, name: 'John Doe' })
const adult = Adult.find(1) as Adult
expect(adult.type).toBe('ADULT')
@@ -193,12 +185,10 @@ describe('Feature - Inheritance - Discriminator field', () => {
createStore([Person, Adult])
- await Person.create({
- data: [
- { id: 1, name: 'John Doe', type: 1 },
- { id: 2, name: 'Person Doe', type: 0 }
- ]
- })
+ await Person.create([
+ { id: 1, name: 'John Doe', type: 1 },
+ { id: 2, name: 'Person Doe', type: 0 }
+ ])
const person = Person.find(2) as Person
expect(person.name).toBe('Person Doe')
diff --git a/test/feature/inheritance/Inheritance_Relations.spec.ts b/test/feature/inheritance/Inheritance_Relations.spec.ts
index a25c4e4f..d3e752f5 100644
--- a/test/feature/inheritance/Inheritance_Relations.spec.ts
+++ b/test/feature/inheritance/Inheritance_Relations.spec.ts
@@ -54,16 +54,12 @@ describe('Feature - Inheritance - Relations', () => {
createStore([Person, Adult, Job])
- await Job.insert({
- data: { id: 1, title: 'Software Engineer', adult_id: 2 }
- })
+ await Job.insert({ id: 1, title: 'Software Engineer', adult_id: 2 })
- await Person.insert({
- data: [
- { id: 1, name: 'John', type: 'PERSON' },
- { id: 2, name: 'Jane', type: 'ADULT' }
- ]
- })
+ await Person.insert([
+ { id: 1, name: 'John', type: 'PERSON' },
+ { id: 2, name: 'Jane', type: 'ADULT' }
+ ])
const adult = Adult.query()
.with('jobs')
@@ -128,16 +124,12 @@ describe('Feature - Inheritance - Relations', () => {
createStore([Person, Adult, Job])
- await Job.insert({
- data: { id: 1, title: 'Software Engineer', adult_id: 2 }
- })
+ await Job.insert({ id: 1, title: 'Software Engineer', adult_id: 2 })
- await Person.insert({
- data: [
- { id: 1, name: 'John', type: 'PERSON' },
- { id: 2, name: 'Jane', type: 'ADULT' }
- ]
- })
+ await Person.insert([
+ { id: 1, name: 'John', type: 'PERSON' },
+ { id: 2, name: 'Jane', type: 'ADULT' }
+ ])
const job = Job.query()
.with('adult')
@@ -200,19 +192,15 @@ describe('Feature - Inheritance - Relations', () => {
createStore([Person, Adult, Role])
- await Role.insert({
- data: [
- { id: 1, roleName: 'Role 1' },
- { id: 2, roleName: 'Role 2' }
- ]
- })
+ await Role.insert([
+ { id: 1, roleName: 'Role 1' },
+ { id: 2, roleName: 'Role 2' }
+ ])
- await Person.insert({
- data: [
- { id: 1, name: 'John', type: 'PERSON', role_id: 1 },
- { id: 2, name: 'Jane', type: 'ADULT', role_id: 1 }
- ]
- })
+ await Person.insert([
+ { id: 1, name: 'John', type: 'PERSON', role_id: 1 },
+ { id: 2, name: 'Jane', type: 'ADULT', role_id: 1 }
+ ])
const roles = Role.query()
.with('people')
@@ -275,19 +263,15 @@ describe('Feature - Inheritance - Relations', () => {
createStore([Person, Adult, Role])
- await Role.insert({
- data: [
- { id: 1, roleName: 'Role 1' },
- { id: 2, roleName: 'Role 2' }
- ]
- })
+ await Role.insert([
+ { id: 1, roleName: 'Role 1' },
+ { id: 2, roleName: 'Role 2' }
+ ])
- await Person.insert({
- data: [
- { id: 1, name: 'John', type: 'PERSON', role_id: 1 },
- { id: 2, name: 'Jane', type: 'ADULT', role_id: 2 }
- ]
- })
+ await Person.insert([
+ { id: 1, name: 'John', type: 'PERSON', role_id: 1 },
+ { id: 2, name: 'Jane', type: 'ADULT', role_id: 2 }
+ ])
// Reverse check: getting all people and their associated role.
const people = Person.query()
@@ -360,13 +344,11 @@ describe('Feature - Inheritance - Relations', () => {
createStore([Person, Adult, Child])
- await Person.insert({
- data: [
- { id: 1, name: 'John', type: 'ADULT', child_id: 3 },
- { id: 2, name: 'Jane', type: 'ADULT', child_id: 3 },
- { id: 3, name: 'Jane', type: 'CHILD' }
- ]
- })
+ await Person.insert([
+ { id: 1, name: 'John', type: 'ADULT', child_id: 3 },
+ { id: 2, name: 'Jane', type: 'ADULT', child_id: 3 },
+ { id: 3, name: 'Jane', type: 'CHILD' }
+ ])
const adults = Adult.query()
.with('child')
@@ -433,16 +415,12 @@ describe('Feature - Inheritance - Relations', () => {
createStore([Person, Adult, Job])
- await Job.insert({
- data: { id: 1, title: 'Software Engineer', adult_id: 2 }
- })
+ await Job.insert({ id: 1, title: 'Software Engineer', adult_id: 2 })
- await Person.insert({
- data: [
- { id: 1, name: 'John', type: 'PERSON' },
- { id: 2, name: 'Jane', type: 'ADULT' }
- ]
- })
+ await Person.insert([
+ { id: 1, name: 'John', type: 'PERSON' },
+ { id: 2, name: 'Jane', type: 'ADULT' }
+ ])
const persons = Person.query()
.with(['jobs', 'dummy'])
diff --git a/test/feature/models/All.spec.ts b/test/feature/models/All.spec.ts
index 503110d7..b6b4859e 100644
--- a/test/feature/models/All.spec.ts
+++ b/test/feature/models/All.spec.ts
@@ -16,12 +16,10 @@ describe('Feature – Models – All', () => {
it('can fetch all records via static method', async () => {
createStore([{ model: User }])
- await User.insert({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ await User.insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
const users = User.all()
@@ -37,12 +35,10 @@ describe('Feature – Models – All', () => {
it('can fetch all records via instance method', async () => {
createStore([{ model: User }])
- await User.insert({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ await User.insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
const user = new User()
diff --git a/test/feature/models/Create.spec.ts b/test/feature/models/Create.spec.ts
index ada25bd0..6216af64 100644
--- a/test/feature/models/Create.spec.ts
+++ b/test/feature/models/Create.spec.ts
@@ -17,9 +17,7 @@ describe('Feature – Models – Create', () => {
it('can create a record via static method', async () => {
const store = createStore([{ model: User }])
- await User.create({
- data: { id: 1, name: 'John Doe' }
- })
+ await User.create({ id: 1, name: 'John Doe' })
const expected = createState({
users: {
@@ -30,15 +28,13 @@ describe('Feature – Models – Create', () => {
expect(store.state.entities).toEqual(expected)
})
- it('can create list of record via static method', async () => {
+ it('can create a list of record via static method', async () => {
const store = createStore([{ model: User }])
- await User.create({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ await User.create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
const expected = createState({
users: {
@@ -53,12 +49,10 @@ describe('Feature – Models – Create', () => {
it('returns created records via static method', async () => {
createStore([{ model: User }])
- const data = await User.create({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ const data = await User.create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
expect(data.users.length).toBe(2)
expect(data.users[0]).toBeInstanceOf(User)
@@ -69,9 +63,7 @@ describe('Feature – Models – Create', () => {
const user = new User()
- await user.$create({
- data: { id: 1, name: 'John Doe' }
- })
+ await user.$create({ id: 1, name: 'John Doe' })
const expected = createState({
users: {
@@ -82,17 +74,15 @@ describe('Feature – Models – Create', () => {
expect(store.state.entities).toEqual(expected)
})
- it('can create list of record via instance method', async () => {
+ it('can create a list of record via instance method', async () => {
const store = createStore([{ model: User }])
const user = new User()
- await user.$create({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ await user.$create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
const expected = createState({
users: {
@@ -109,12 +99,10 @@ describe('Feature – Models – Create', () => {
const user = new User()
- const data = await user.$create({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ const data = await user.$create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
expect(data.users.length).toBe(2)
expect(data.users[0]).toBeInstanceOf(User)
@@ -208,9 +196,7 @@ describe('Feature – Models – Create', () => {
expect(data).toEqual(dataBefore)
- await Author.create({
- data
- })
+ await Author.create(data)
const expected = createState({
authors: {
diff --git a/test/feature/models/Insert.spec.ts b/test/feature/models/Insert.spec.ts
index 609f73e1..05be819b 100644
--- a/test/feature/models/Insert.spec.ts
+++ b/test/feature/models/Insert.spec.ts
@@ -17,13 +17,9 @@ describe('Feature – Models – Insert', () => {
it('can insert a record via static method', async () => {
const store = createStore([{ model: User }])
- await User.insert({
- data: { id: 1, name: 'John Doe' }
- })
+ await User.insert({ id: 1, name: 'John Doe' })
- await User.insert({
- data: { id: 2, name: 'Jane Doe' }
- })
+ await User.insert({ id: 2, name: 'Jane Doe' })
const expected = createState({
users: {
@@ -35,15 +31,13 @@ describe('Feature – Models – Insert', () => {
expect(store.state.entities).toEqual(expected)
})
- it('can insert list of record via static method', async () => {
+ it('can insert a list of records via static method', async () => {
const store = createStore([{ model: User }])
- await User.insert({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ await User.insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
const expected = createState({
users: {
@@ -58,12 +52,10 @@ describe('Feature – Models – Insert', () => {
it('returns inserted records via static method', async () => {
createStore([{ model: User }])
- const data = await User.insert({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ const data = await User.insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
expect(data.users.length).toBe(2)
expect(data.users[0]).toBeInstanceOf(User)
@@ -74,13 +66,9 @@ describe('Feature – Models – Insert', () => {
const user = new User()
- await user.$insert({
- data: { id: 1, name: 'John Doe' }
- })
+ await user.$insert({ id: 1, name: 'John Doe' })
- await user.$insert({
- data: { id: 2, name: 'Jane Doe' }
- })
+ await user.$insert({ id: 2, name: 'Jane Doe' })
const expected = createState({
users: {
@@ -92,17 +80,15 @@ describe('Feature – Models – Insert', () => {
expect(store.state.entities).toEqual(expected)
})
- it('can insert list of record via instance method', async () => {
+ it('can insert a list of records via instance method', async () => {
const store = createStore([{ model: User }])
const user = new User()
- await user.$insert({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ await user.$insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
const expected = createState({
users: {
@@ -119,12 +105,10 @@ describe('Feature – Models – Insert', () => {
const user = new User()
- const data = await user.$insert({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ const data = await user.$insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
expect(data.users.length).toBe(2)
expect(data.users[0]).toBeInstanceOf(User)
@@ -218,9 +202,7 @@ describe('Feature – Models – Insert', () => {
expect(data).toEqual(dataBefore)
- await Author.insert({
- data
- })
+ await Author.insert(data)
const expected = createState({
authors: {
diff --git a/test/feature/models/InsertOrUpdate.spec.ts b/test/feature/models/InsertOrUpdate.spec.ts
index 8cc0efde..09b58e49 100644
--- a/test/feature/models/InsertOrUpdate.spec.ts
+++ b/test/feature/models/InsertOrUpdate.spec.ts
@@ -17,16 +17,12 @@ describe('Feature – Models – Insert Or Update', () => {
it('can insert or update records via static method', async () => {
const store = createStore([{ model: User }])
- await User.insert({
- data: [{ id: 1, name: 'John Doe' }]
- })
+ await User.insert([{ id: 1, name: 'John Doe' }])
- await User.insertOrUpdate({
- data: [
- { id: 1, name: 'Jane Doe' },
- { id: 2, name: 'Johnny Doe' }
- ]
- })
+ await User.insertOrUpdate([
+ { id: 1, name: 'Jane Doe' },
+ { id: 2, name: 'Johnny Doe' }
+ ])
const expected = createState({
users: {
@@ -43,16 +39,12 @@ describe('Feature – Models – Insert Or Update', () => {
const user = new User()
- await User.insert({
- data: [{ id: 1, name: 'John Doe' }]
- })
+ await User.insert([{ id: 1, name: 'John Doe' }])
- await user.$insertOrUpdate({
- data: [
- { id: 1, name: 'Jane Doe' },
- { id: 2, name: 'Johnny Doe' }
- ]
- })
+ await user.$insertOrUpdate([
+ { id: 1, name: 'Jane Doe' },
+ { id: 2, name: 'Johnny Doe' }
+ ])
const expected = createState({
users: {
diff --git a/test/feature/models/Query.spec.ts b/test/feature/models/Query.spec.ts
index 8f9c85cb..a66d5691 100644
--- a/test/feature/models/Query.spec.ts
+++ b/test/feature/models/Query.spec.ts
@@ -16,12 +16,10 @@ describe('Feature – Models – Query', () => {
it('can begin query chain via static method', async () => {
createStore([{ model: User }])
- await User.insert({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ await User.insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
const users = User.query().get()
@@ -37,12 +35,10 @@ describe('Feature – Models – Query', () => {
it('can begin query chain via instance method', async () => {
createStore([{ model: User }])
- await User.insert({
- data: [
- { id: 1, name: 'John Doe' },
- { id: 2, name: 'Jane Doe' }
- ]
- })
+ await User.insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
const u = new User()
diff --git a/test/feature/models/Update.spec.ts b/test/feature/models/Update.spec.ts
index a04d18b8..f1f7f13a 100644
--- a/test/feature/models/Update.spec.ts
+++ b/test/feature/models/Update.spec.ts
@@ -178,13 +178,11 @@ describe('Feature – Models – Update', () => {
const store = createStore([{ model: Role }])
- await Role.insert({
- data: { key_1: 1, key_2: 2, name: 'John Doe' }
- })
+ await Role.insert({ key_1: 1, key_2: 2, name: 'John Doe' })
- const user = Role.find([1, 2])
+ const user = Role.find([1, 2]) as Role
- await user?.$update({ name: 'Jane Doe' })
+ await user.$update({ name: 'Jane Doe' })
const expected = createState({
roles: {
@@ -231,10 +229,7 @@ describe('Feature – Models – Update', () => {
data: { id: 1, name: 'John Doe' }
})
- await user.$update({
- where: 1,
- data: { name: 'Jane Doe' }
- })
+ await user.$update({ name: 'Jane Doe' }, { where: 1 })
const expected = createState({
users: {
diff --git a/test/feature/models/static_inserts_create.spec.ts b/test/feature/models/static_inserts_create.spec.ts
new file mode 100644
index 00000000..1e947c6b
--- /dev/null
+++ b/test/feature/models/static_inserts_create.spec.ts
@@ -0,0 +1,102 @@
+import {
+ createStore,
+ createState,
+ serializeCollection
+} from 'test/support/Helpers'
+import { Model } from '@/index'
+
+describe('feature/models/static_inserts_create', () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ name: this.string('')
+ }
+ }
+ }
+
+ it('can create a record', async () => {
+ const store = createStore([User])
+
+ await User.create({ id: 1, name: 'John Doe' })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can create multiple records', async () => {
+ const store = createStore([User])
+
+ await User.create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' },
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('replaces any existing records', async () => {
+ const store = createStore([User])
+
+ await User.create({ id: 1, name: 'John Doe' })
+ await User.create({ id: 2, name: 'Jane Doe' })
+
+ const expected = createState({
+ users: {
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('returns newly created data', async () => {
+ createStore([User])
+
+ const result = await User.create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
+
+ const expected = [
+ { $id: '1', id: 1, name: 'John Doe' },
+ { $id: '2', id: 2, name: 'Jane Doe' }
+ ]
+
+ expect(result.users.length).toBe(2)
+ expect(result.users[0]).toBeInstanceOf(User)
+ expect(result.users[1]).toBeInstanceOf(User)
+ expect(serializeCollection(result.users)).toEqual(expected)
+ })
+
+ // Test for deprecated payload API syntax.
+ it('accepts the "payload" object as an argument', async () => {
+ const store = createStore([User])
+
+ await User.create({
+ data: { id: 1, name: 'John Doe' }
+ })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+})
diff --git a/test/feature/models/static_inserts_insert.spec.ts b/test/feature/models/static_inserts_insert.spec.ts
new file mode 100644
index 00000000..90e7ee0a
--- /dev/null
+++ b/test/feature/models/static_inserts_insert.spec.ts
@@ -0,0 +1,103 @@
+import {
+ createStore,
+ createState,
+ serializeCollection
+} from 'test/support/Helpers'
+import { Model } from '@/index'
+
+describe('feature/models/static_inserts_insert', () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ name: this.string('')
+ }
+ }
+ }
+
+ it('can insert a record', async () => {
+ const store = createStore([User])
+
+ await User.insert({ id: 1, name: 'John Doe' })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can insert multiple records', async () => {
+ const store = createStore([User])
+
+ await User.insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' },
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('keeps existing records', async () => {
+ const store = createStore([User])
+
+ await User.insert({ id: 1, name: 'John Doe' })
+ await User.insert({ id: 2, name: 'Jane Doe' })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' },
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('returns newly created data', async () => {
+ createStore([User])
+
+ const result = await User.insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
+
+ const expected = [
+ { $id: '1', id: 1, name: 'John Doe' },
+ { $id: '2', id: 2, name: 'Jane Doe' }
+ ]
+
+ expect(result.users.length).toBe(2)
+ expect(result.users[0]).toBeInstanceOf(User)
+ expect(result.users[1]).toBeInstanceOf(User)
+ expect(serializeCollection(result.users)).toEqual(expected)
+ })
+
+ // Test for deprecated payload API syntax.
+ it('accepts the "payload" object as an argument', async () => {
+ const store = createStore([User])
+
+ await User.insert({
+ data: { id: 1, name: 'John Doe' }
+ })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+})
diff --git a/test/feature/models/static_inserts_new.spec.ts b/test/feature/models/static_inserts_new.spec.ts
new file mode 100644
index 00000000..1270857d
--- /dev/null
+++ b/test/feature/models/static_inserts_new.spec.ts
@@ -0,0 +1,50 @@
+import { createStore, createState } from 'test/support/Helpers'
+import Uid from '@/support/Uid'
+import { Model } from '@/index'
+
+describe('feature/query/inserts_new', () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.uid(),
+ name: this.attr('John Doe')
+ }
+ }
+
+ id!: string
+ name!: string
+ }
+
+ beforeEach(() => {
+ Uid.reset()
+ })
+
+ it('can create new data with all fields filled by default values', async () => {
+ const store = createStore([User])
+
+ await User.new()
+ await User.new()
+
+ const expected = createState({
+ users: {
+ $uid1: { $id: '$uid1', id: '$uid1', name: 'John Doe' },
+ $uid2: { $id: '$uid2', id: '$uid2', name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('returns a newly created model', async () => {
+ createStore([User])
+
+ const user = await User.new()
+
+ expect(user).toBeInstanceOf(User)
+ expect(user.$id).toBe('$uid1')
+ expect(user.id).toBe('$uid1')
+ expect(user.name).toBe('John Doe')
+ })
+})
diff --git a/test/feature/modules/api_inserts_create.spec.ts b/test/feature/modules/api_inserts_create.spec.ts
new file mode 100644
index 00000000..fd9ea8e8
--- /dev/null
+++ b/test/feature/modules/api_inserts_create.spec.ts
@@ -0,0 +1,95 @@
+import {
+ createStore,
+ createState,
+ serializeCollection
+} from 'test/support/Helpers'
+import { Model } from '@/index'
+
+describe('feature/modules/api_inserts_create', () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ name: this.string('')
+ }
+ }
+ }
+
+ it('can create a record', async () => {
+ const store = createStore([User])
+
+ await store.dispatch('entities/users/create', {
+ data: { id: 1, name: 'John Doe' }
+ })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can create multiple records', async () => {
+ const store = createStore([User])
+
+ await store.dispatch('entities/users/create', {
+ data: [
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ]
+ })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' },
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('replaces any existing records', async () => {
+ const store = createStore([User])
+
+ await store.dispatch('entities/users/create', {
+ data: { id: 1, name: 'John Doe' }
+ })
+ await store.dispatch('entities/users/create', {
+ data: { id: 2, name: 'Jane Doe' }
+ })
+
+ const expected = createState({
+ users: {
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('returns newly created data', async () => {
+ const store = createStore([User])
+
+ const result = await store.dispatch('entities/users/create', {
+ data: [
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ]
+ })
+
+ const expected = [
+ { $id: '1', id: 1, name: 'John Doe' },
+ { $id: '2', id: 2, name: 'Jane Doe' }
+ ]
+
+ expect(result.users.length).toBe(2)
+ expect(result.users[0]).toBeInstanceOf(User)
+ expect(result.users[1]).toBeInstanceOf(User)
+ expect(serializeCollection(result.users)).toEqual(expected)
+ })
+})
diff --git a/test/feature/modules/api_inserts_insert.spec.ts b/test/feature/modules/api_inserts_insert.spec.ts
new file mode 100644
index 00000000..06645906
--- /dev/null
+++ b/test/feature/modules/api_inserts_insert.spec.ts
@@ -0,0 +1,96 @@
+import {
+ createStore,
+ createState,
+ serializeCollection
+} from 'test/support/Helpers'
+import { Model } from '@/index'
+
+describe('feature/modules/inserts_insert', () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ name: this.string('')
+ }
+ }
+ }
+
+ it('can insert a record', async () => {
+ const store = createStore([User])
+
+ await store.dispatch('entities/users/insert', {
+ data: { id: 1, name: 'John Doe' }
+ })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can insert multiple records', async () => {
+ const store = createStore([User])
+
+ await store.dispatch('entities/users/insert', {
+ data: [
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ]
+ })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' },
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('keeps existing records', async () => {
+ const store = createStore([User])
+
+ await store.dispatch('entities/users/insert', {
+ data: { id: 1, name: 'John Doe' }
+ })
+ await store.dispatch('entities/users/insert', {
+ data: { id: 2, name: 'Jane Doe' }
+ })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' },
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('returns newly created data', async () => {
+ const store = createStore([User])
+
+ const result = await store.dispatch('entities/users/insert', {
+ data: [
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ]
+ })
+
+ const expected = [
+ { $id: '1', id: 1, name: 'John Doe' },
+ { $id: '2', id: 2, name: 'Jane Doe' }
+ ]
+
+ expect(result.users.length).toBe(2)
+ expect(result.users[0]).toBeInstanceOf(User)
+ expect(result.users[1]).toBeInstanceOf(User)
+ expect(serializeCollection(result.users)).toEqual(expected)
+ })
+})
diff --git a/test/feature/modules/api_inserts_new.spec.ts b/test/feature/modules/api_inserts_new.spec.ts
new file mode 100644
index 00000000..fe91042a
--- /dev/null
+++ b/test/feature/modules/api_inserts_new.spec.ts
@@ -0,0 +1,50 @@
+import { createStore, createState } from 'test/support/Helpers'
+import Uid from '@/support/Uid'
+import { Model } from '@/index'
+
+describe('feature/modules/api_inserts_new', () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.uid(),
+ name: this.attr('John Doe')
+ }
+ }
+
+ id!: string
+ name!: string
+ }
+
+ beforeEach(() => {
+ Uid.reset()
+ })
+
+ it('can create new data with all fields filled by default values', async () => {
+ const store = createStore([User])
+
+ await store.dispatch('entities/users/new')
+ await store.dispatch('entities/users/new')
+
+ const expected = createState({
+ users: {
+ $uid1: { $id: '$uid1', id: '$uid1', name: 'John Doe' },
+ $uid2: { $id: '$uid2', id: '$uid2', name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('returns a newly created model', async () => {
+ const store = createStore([User])
+
+ const user = await store.dispatch('entities/users/new')
+
+ expect(user).toBeInstanceOf(User)
+ expect(user.$id).toBe('$uid1')
+ expect(user.id).toBe('$uid1')
+ expect(user.name).toBe('John Doe')
+ })
+})
diff --git a/test/feature/modules/modules.spec.ts b/test/feature/modules/modules.spec.ts
new file mode 100644
index 00000000..85607d9d
--- /dev/null
+++ b/test/feature/modules/modules.spec.ts
@@ -0,0 +1,48 @@
+import Vue from 'vue'
+import Vuex, { Module } from 'vuex'
+import VuexORM, { Database, Model } from '@/index'
+
+describe('feature/modules/modules', () => {
+ Vue.use(Vuex)
+
+ it('can install a custom module along with the model', async () => {
+ class User extends Model {
+ static entity = 'users'
+ }
+
+ const users: Module<{ count: number }, any> = {
+ state: () => ({
+ count: 1
+ }),
+ getters: {
+ double: (state) => state.count * 2
+ },
+ actions: {
+ increment({ commit }) {
+ commit('increment')
+ }
+ },
+ mutations: {
+ increment(state) {
+ state.count++
+ }
+ }
+ }
+
+ const database = new Database()
+
+ database.register(User, users)
+
+ const store = new Vuex.Store({
+ plugins: [VuexORM.install(database)]
+ })
+
+ expect(store.state.entities.users.count).toBe(1)
+ expect(store.getters['entities/users/double']).toBe(2)
+
+ await store.dispatch('entities/users/increment')
+
+ expect(store.state.entities.users.count).toBe(2)
+ expect(store.getters['entities/users/double']).toBe(4)
+ })
+})
diff --git a/test/feature/query/inserts_create.spec.ts b/test/feature/query/inserts_create.spec.ts
new file mode 100644
index 00000000..15cbc475
--- /dev/null
+++ b/test/feature/query/inserts_create.spec.ts
@@ -0,0 +1,160 @@
+import {
+ createStore,
+ createState,
+ serializeCollection
+} from 'test/support/Helpers'
+import Uid from '@/support/Uid'
+import { Model } from '@/index'
+
+describe('feature/query/inserts_create', () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ name: this.string('Default Doe')
+ }
+ }
+ }
+
+ beforeEach(() => {
+ Uid.reset()
+ })
+
+ it('can create a record', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).create({ id: 1, name: 'John Doe' })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can create multiple records', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' },
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('replaces any existing records', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).create({ id: 1, name: 'John Doe' })
+ await store.$repo(User).create({ id: 2, name: 'Jane Doe' })
+
+ const expected = createState({
+ users: {
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('cleans all existing records if the empty data is passed', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).create({ id: 1, name: 'John Doe' })
+ await store.$repo(User).create({})
+
+ const expected = createState({
+ users: {}
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('fills missing fields with the default value', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).create({ id: 1 })
+
+ const expected = {
+ 1: { $id: '1', id: 1, name: 'Default Doe' }
+ }
+
+ expect(store.state.entities.users.data).toEqual(expected)
+ })
+
+ it('can insert record with missing primary key value', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).create({ name: 'John Doe' })
+
+ const expected = createState({
+ users: {
+ $uid1: { $id: '$uid1', id: '$uid1', name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can insert record with primary key value of `null`', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).create({ id: null, name: 'John Doe' })
+
+ const expected = createState({
+ users: {
+ $uid1: { $id: '$uid1', id: '$uid1', name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('returns newly created data', async () => {
+ const store = createStore([User])
+
+ const result = await store.$repo(User).create([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
+
+ const expected = [
+ { $id: '1', id: 1, name: 'John Doe' },
+ { $id: '2', id: 2, name: 'Jane Doe' }
+ ]
+
+ expect(result.users.length).toBe(2)
+ expect(result.users[0]).toBeInstanceOf(User)
+ expect(result.users[1]).toBeInstanceOf(User)
+ expect(serializeCollection(result.users)).toEqual(expected)
+ })
+
+ // Test for deprecated payload API syntax.
+ it('accepts the "payload" object as an argument', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).create({
+ data: { id: 1, name: 'John Doe' }
+ })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+})
diff --git a/test/feature/query/inserts_insert.spec.ts b/test/feature/query/inserts_insert.spec.ts
new file mode 100644
index 00000000..afd6f247
--- /dev/null
+++ b/test/feature/query/inserts_insert.spec.ts
@@ -0,0 +1,160 @@
+import {
+ createStore,
+ createState,
+ serializeCollection
+} from 'test/support/Helpers'
+import Uid from '@/support/Uid'
+import { Model } from '@/index'
+
+describe('feature/query/inserts_insert', () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ name: this.string('Default Doe')
+ }
+ }
+ }
+
+ beforeEach(() => {
+ Uid.reset()
+ })
+
+ it('does nothing if the given data is empty', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).insert({})
+
+ const expected = createState({
+ users: {}
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can insert a record', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).insert({ id: 1, name: 'John Doe' })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can insert multiple records', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' },
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('keeps existing records', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).insert({ id: 1, name: 'John Doe' })
+ await store.$repo(User).insert({ id: 2, name: 'Jane Doe' })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' },
+ 2: { $id: '2', id: 2, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('fills missing fields with the default value', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).insert({ id: 1 })
+
+ const expected = {
+ 1: { $id: '1', id: 1, name: 'Default Doe' }
+ }
+
+ expect(store.state.entities.users.data).toEqual(expected)
+ })
+
+ it('can insert record with missing primary key value', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).insert({ name: 'John Doe' })
+
+ const expected = createState({
+ users: {
+ $uid1: { $id: '$uid1', id: '$uid1', name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can insert record with primary key value of `null`', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).insert({ id: null, name: 'John Doe' })
+
+ const expected = createState({
+ users: {
+ $uid1: { $id: '$uid1', id: '$uid1', name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('returns newly created data', async () => {
+ const store = createStore([User])
+
+ const result = await store.$repo(User).insert([
+ { id: 1, name: 'John Doe' },
+ { id: 2, name: 'Jane Doe' }
+ ])
+
+ const expected = [
+ { $id: '1', id: 1, name: 'John Doe' },
+ { $id: '2', id: 2, name: 'Jane Doe' }
+ ]
+
+ expect(result.users.length).toBe(2)
+ expect(result.users[0]).toBeInstanceOf(User)
+ expect(result.users[1]).toBeInstanceOf(User)
+ expect(serializeCollection(result.users)).toEqual(expected)
+ })
+
+ // Test for deprecated payload API syntax.
+ it('accepts the "payload" object as an argument', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).insert({
+ data: { id: 1, name: 'John Doe' }
+ })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+})
diff --git a/test/feature/query/inserts_new.spec.ts b/test/feature/query/inserts_new.spec.ts
new file mode 100644
index 00000000..6b88da7d
--- /dev/null
+++ b/test/feature/query/inserts_new.spec.ts
@@ -0,0 +1,50 @@
+import { createStore, createState } from 'test/support/Helpers'
+import Uid from '@/support/Uid'
+import { Model } from '@/index'
+
+describe('feature/models/static_inserts_new', () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.uid(),
+ name: this.attr('John Doe')
+ }
+ }
+
+ id!: string
+ name!: string
+ }
+
+ beforeEach(() => {
+ Uid.reset()
+ })
+
+ it('can create new data with all fields filled by default values', async () => {
+ const store = createStore([User])
+
+ await store.$repo(User).new()
+ await store.$repo(User).new()
+
+ const expected = createState({
+ users: {
+ $uid1: { $id: '$uid1', id: '$uid1', name: 'John Doe' },
+ $uid2: { $id: '$uid2', id: '$uid2', name: 'John Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('returns a newly created model', async () => {
+ const store = createStore([User])
+
+ const user = await store.$repo(User).new()
+
+ expect(user).toBeInstanceOf(User)
+ expect(user.$id).toBe('$uid1')
+ expect(user.id).toBe('$uid1')
+ expect(user.name).toBe('John Doe')
+ })
+})
diff --git a/test/feature/relations/BelongsToMany_Retrieve.spec.ts b/test/feature/relations/BelongsToMany_Retrieve.spec.ts
index 2bfbe98c..a2e7a44f 100644
--- a/test/feature/relations/BelongsToMany_Retrieve.spec.ts
+++ b/test/feature/relations/BelongsToMany_Retrieve.spec.ts
@@ -66,20 +66,14 @@ describe('Feature – Relations – Belongs To Many – Retrieve', () => {
it('can resolve belongs to many relation', async () => {
createStore([{ model: User }, { model: Role }, { model: RoleUser }])
- await User.create({
- data: [{ id: 1 }, { id: 2 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }])
- await Role.create({
- data: [{ id: 2 }, { id: 3 }]
- })
+ await Role.create([{ id: 2 }, { id: 3 }])
- await RoleUser.create({
- data: [
- { user_id: 1, role_id: 2 },
- { user_id: 1, role_id: 3 }
- ]
- })
+ await RoleUser.create([
+ { user_id: 1, role_id: 2 },
+ { user_id: 1, role_id: 3 }
+ ])
const user = User.query()
.with('roles')
@@ -98,18 +92,16 @@ describe('Feature – Relations – Belongs To Many – Retrieve', () => {
it('can resolve belongs to many relation with pivot data', async () => {
createStore([{ model: User }, { model: Role }, { model: RoleUser }])
- await User.create({
- data: [
- {
- id: 1,
- roles: [
- { id: 2, pivot: { level: 1 } },
- { id: 3, pivot: { level: 2 } }
- ]
- },
- { id: 2 }
- ]
- })
+ await User.create([
+ {
+ id: 1,
+ roles: [
+ { id: 2, pivot: { level: 1 } },
+ { id: 3, pivot: { level: 2 } }
+ ]
+ },
+ { id: 2 }
+ ])
const user = User.query()
.with('roles')
@@ -130,20 +122,14 @@ describe('Feature – Relations – Belongs To Many – Retrieve', () => {
it('can resolve inverse belongs to many relation', async () => {
createStore([{ model: User }, { model: Role }, { model: RoleUser }])
- await User.create({
- data: { id: 1 }
- })
+ await User.create({ id: 1 })
- await Role.create({
- data: [{ id: 2 }, { id: 3 }]
- })
+ await Role.create([{ id: 2 }, { id: 3 }])
- await RoleUser.create({
- data: [
- { user_id: 1, role_id: 2 },
- { user_id: 1, role_id: 3 }
- ]
- })
+ await RoleUser.create([
+ { user_id: 1, role_id: 2 },
+ { user_id: 1, role_id: 3 }
+ ])
const role = Role.query()
.with('users')
@@ -156,9 +142,7 @@ describe('Feature – Relations – Belongs To Many – Retrieve', () => {
it('returns empty collection if the relation is not found', async () => {
createStore([{ model: User }, { model: Role }, { model: RoleUser }])
- await User.create({
- data: { id: 1 }
- })
+ await User.create({ id: 1 })
const user = User.query()
.with('roles')
@@ -170,13 +154,9 @@ describe('Feature – Relations – Belongs To Many – Retrieve', () => {
it('returns empty collection if the Role relation from RoleUser is not found', async () => {
createStore([{ model: User }, { model: Role }, { model: RoleUser }])
- await User.create({
- data: { id: 1 }
- })
+ await User.create({ id: 1 })
- await RoleUser.create({
- data: [{ user_id: 1 }]
- })
+ await RoleUser.create([{ user_id: 1 }])
const user = User.query()
.with('roles')
@@ -188,21 +168,15 @@ describe('Feature – Relations – Belongs To Many – Retrieve', () => {
it('can resolve belongs to many relation with has constraint', async () => {
createStore([{ model: User }, { model: Role }, { model: RoleUser }])
- await User.create({
- data: { id: 1 }
- })
+ await User.create({ id: 1 })
- await Role.create({
- data: [{ id: 2 }, { id: 3 }]
- })
+ await Role.create([{ id: 2 }, { id: 3 }])
- await RoleUser.create({
- data: [
- { user_id: 1, role_id: 2 },
- { user_id: 1, role_id: 3 },
- { user_id: 2, role_id: 4 }
- ]
- })
+ await RoleUser.create([
+ { user_id: 1, role_id: 2 },
+ { user_id: 1, role_id: 3 },
+ { user_id: 2, role_id: 4 }
+ ])
const roles = Role.query()
.has('users')
@@ -354,7 +328,7 @@ describe('Feature – Relations – Belongs To Many – Retrieve', () => {
}
]
- await User.create({ data })
+ await User.create(data)
const users = User.query()
.with('roles.permissions')
@@ -433,17 +407,11 @@ describe('Feature – Relations – Belongs To Many – Retrieve', () => {
createStore([{ model: User }, { model: Role }, { model: RoleUser }])
- await User.create({
- data: [{ id: 1 }]
- })
+ await User.create([{ id: 1 }])
- await Role.create({
- data: { id: 1 }
- })
+ await Role.create({ id: 1 })
- await RoleUser.create({
- data: { id: 1, user_id: 1, role_id: 1 }
- })
+ await RoleUser.create({ id: 1, user_id: 1, role_id: 1 })
const users = User.query()
.with('roles')
@@ -458,10 +426,8 @@ describe('Feature – Relations – Belongs To Many – Retrieve', () => {
createStore([{ model: User }, { model: Role }, { model: RoleUser }])
await User.create({
- data: {
- id: 1,
- roles: [{ id: 1 }, { id: 3 }, { id: 2 }]
- }
+ id: 1,
+ roles: [{ id: 1 }, { id: 3 }, { id: 2 }]
})
const ascending = User.query()
diff --git a/test/feature/relations/HasMany.spec.ts b/test/feature/relations/HasMany.spec.ts
index 9f0c9065..ec8fa695 100644
--- a/test/feature/relations/HasMany.spec.ts
+++ b/test/feature/relations/HasMany.spec.ts
@@ -1,4 +1,4 @@
-import { createStore, createState } from 'test/support/Helpers'
+import { createStore } from 'test/support/Helpers'
import Model from '@/model/Model'
describe('Features – Relations – Has Many', () => {
@@ -25,71 +25,65 @@ describe('Features – Relations – Has Many', () => {
}
it('can create data containing the has many relation', async () => {
- const store = createStore([{ model: User }, { model: Post }])
+ createStore([{ model: User }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: {
- id: 1,
- posts: [{ id: 1 }, { id: 2 }]
- }
+ await User.create({
+ id: 1,
+ posts: [{ id: 1 }, { id: 2 }]
})
- const expected = createState({
- users: {
- 1: { $id: '1', id: 1, posts: [] }
- },
- posts: {
- 1: { $id: '1', id: 1, user_id: 1 },
- 2: { $id: '2', id: 2, user_id: 1 }
- }
- })
+ const expectedUsers = [{ $id: '1', id: 1, posts: [] }]
+
+ expect(User.all()).toEqual(expectedUsers)
- expect(store.state.entities).toEqual(expected)
+ const expectedPosts = [
+ { $id: '1', id: 1, user_id: 1 },
+ { $id: '2', id: 2, user_id: 1 }
+ ]
+
+ expect(Post.all()).toEqual(expectedPosts)
})
it('can update data and insert has many relation', async () => {
- const store = createStore([{ model: User }, { model: Post }])
+ createStore([{ model: User }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: { id: 1 }
- })
+ await User.create({ id: 1 })
- await store.dispatch('entities/users/update', {
- insert: ['posts'],
- data: {
+ await User.update(
+ {
id: 1,
posts: [{ id: 1 }, { id: 2 }]
- }
- })
-
- const expected = createState({
- users: {
- 1: { $id: '1', id: 1, posts: [] }
},
- posts: {
- 1: { $id: '1', id: 1, user_id: 1 },
- 2: { $id: '2', id: 2, user_id: 1 }
+ {
+ insert: ['posts']
}
- })
+ )
- expect(store.state.entities).toEqual(expected)
+ const expectedUsers = [{ $id: '1', id: 1, posts: [] }]
+
+ expect(User.all()).toEqual(expectedUsers)
+
+ const expectedPosts = [
+ { $id: '1', id: 1, user_id: 1 },
+ { $id: '2', id: 2, user_id: 1 }
+ ]
+
+ expect(Post.all()).toEqual(expectedPosts)
})
it('can resolve the has many relation', async () => {
- const store = createStore([{ model: User }, { model: Post }])
-
- await store.dispatch('entities/users/create', {
- data: [
- {
- id: 1,
- posts: [{ id: 1 }, { id: 2 }]
- },
- {
- id: 2,
- posts: null
- }
- ]
- })
+ createStore([{ model: User }, { model: Post }])
+
+ await User.create([
+ {
+ id: 1,
+ posts: [{ id: 1 }, { id: 2 }]
+ },
+ {
+ id: 2,
+ posts: null
+ }
+ ])
const expected = [
{
@@ -107,7 +101,7 @@ describe('Features – Relations – Has Many', () => {
}
]
- const user = store.getters['entities/users/query']()
+ const user = User.query()
.with('posts')
.all()
@@ -128,13 +122,11 @@ describe('Features – Relations – Has Many', () => {
}
}
- const store = createStore([{ model: User }, { model: Post }])
+ createStore([{ model: User }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: {
- user_id: 1,
- posts: [{ id: 1 }, { id: 2 }]
- }
+ await User.create({
+ user_id: 1,
+ posts: [{ id: 1 }, { id: 2 }]
})
const expected = {
@@ -146,7 +138,7 @@ describe('Features – Relations – Has Many', () => {
]
}
- const user = store.getters['entities/users/query']()
+ const user = User.query()
.with('posts')
.find(1)
@@ -177,25 +169,22 @@ describe('Features – Relations – Has Many', () => {
}
}
- const store = createStore([{ model: User }, { model: Post }])
+ createStore([{ model: User }, { model: Post }])
await User.create({
- data: {
- id: 1,
- local_key: 'local_key',
- posts: [{ id: 1 }]
- }
+ id: 1,
+ local_key: 'local_key',
+ posts: [{ id: 1 }]
})
- const expected = createState({
- users: {
- 1: { $id: '1', id: 1, local_key: 'local_key', posts: [] }
- },
- posts: {
- 1: { $id: '1', id: 1, user_id: 'local_key' }
- }
- })
+ const expectedUsers = [
+ { $id: '1', id: 1, local_key: 'local_key', posts: [] }
+ ]
+
+ expect(User.all()).toEqual(expectedUsers)
+
+ const expectedPosts = [{ $id: '1', id: 1, user_id: 'local_key' }]
- expect(store.state.entities).toEqual(expected)
+ expect(Post.all()).toEqual(expectedPosts)
})
})
diff --git a/test/feature/relations/HasManyBy.spec.ts b/test/feature/relations/HasManyBy.spec.ts
index d1772f0d..80ec2524 100644
--- a/test/feature/relations/HasManyBy.spec.ts
+++ b/test/feature/relations/HasManyBy.spec.ts
@@ -29,10 +29,8 @@ describe('Features – Relations – Has Many By', () => {
const store = createStore([{ model: User }, { model: Post }])
await User.insert({
- data: {
- id: 1,
- posts: [{ id: 1 }, { id: 2 }]
- }
+ id: 1,
+ posts: [{ id: 1 }, { id: 2 }]
})
const expected = createState({
@@ -73,9 +71,7 @@ describe('Features – Relations – Has Many By', () => {
const store = createStore([{ model: User }, { model: Post }])
- await User.insert({
- data: { id: 1, posts: [] }
- })
+ await User.insert({ id: 1, posts: [] })
const expected = createState({
users: {
@@ -112,9 +108,7 @@ describe('Features – Relations – Has Many By', () => {
const store = createStore([{ model: User }, { model: Post }])
- await User.insert({
- data: { id: 1, posts: null }
- })
+ await User.insert({ id: 1, posts: null })
const expected = createState({
users: {
@@ -152,10 +146,8 @@ describe('Features – Relations – Has Many By', () => {
createStore([{ model: User }, { model: Post }])
await User.insert({
- data: {
- id: 1,
- posts: [{ id: 1 }, { id: 2 }]
- }
+ id: 1,
+ posts: [{ id: 1 }, { id: 2 }]
})
const user = User.query()
@@ -200,12 +192,10 @@ describe('Features – Relations – Has Many By', () => {
createStore([{ model: User }, { model: Post }])
- await User.insert({
- data: [
- { id: 1, posts: [{ id: 1 }, { id: 2 }] },
- { id: 2, post_ids: [], posts: [] }
- ]
- })
+ await User.insert([
+ { id: 1, posts: [{ id: 1 }, { id: 2 }] },
+ { id: 2, post_ids: [], posts: [] }
+ ])
const users = User.query()
.with('posts')
@@ -269,9 +259,7 @@ describe('Features – Relations – Has Many By', () => {
createStore([{ model: User }, { model: Post }])
- await User.insert({
- data: { id: 1, posts: [{ id: 1 }, { id: 3 }, { id: 2 }] }
- })
+ await User.insert({ id: 1, posts: [{ id: 1 }, { id: 3 }, { id: 2 }] })
const ascending = User.query()
.with('posts', (query) => {
diff --git a/test/feature/relations/HasManyThrough.spec.ts b/test/feature/relations/HasManyThrough.spec.ts
index 588c7737..98507929 100644
--- a/test/feature/relations/HasManyThrough.spec.ts
+++ b/test/feature/relations/HasManyThrough.spec.ts
@@ -62,10 +62,8 @@ describe('Features – Relations – Has Many Through', () => {
])
await Country.insert({
- data: {
- id: 1,
- posts: [{ id: 1 }, { id: 2 }]
- }
+ id: 1,
+ posts: [{ id: 1 }, { id: 2 }]
})
const expected = createState({
@@ -136,26 +134,20 @@ describe('Features – Relations – Has Many Through', () => {
createStore([{ model: Country }, { model: User }, { model: Post }])
- await User.insert({
- data: [
- { id: 1, country_id: 1 },
- { id: 2, country_id: 1 },
- { id: 3, country_id: 2 }
- ]
- })
+ await User.insert([
+ { id: 1, country_id: 1 },
+ { id: 2, country_id: 1 },
+ { id: 3, country_id: 2 }
+ ])
- await Post.insert({
- data: [
- { id: 1, user_id: 1 },
- { id: 2, user_id: 2 },
- { id: 3, user_id: 3 },
- { id: 4, user_id: 1 }
- ]
- })
+ await Post.insert([
+ { id: 1, user_id: 1 },
+ { id: 2, user_id: 2 },
+ { id: 3, user_id: 3 },
+ { id: 4, user_id: 1 }
+ ])
- await Country.insert({
- data: [{ id: 1 }, { id: 2 }]
- })
+ await Country.insert([{ id: 1 }, { id: 2 }])
const country = Country.query()
.with('posts')
@@ -230,26 +222,20 @@ describe('Features – Relations – Has Many Through', () => {
createStore([{ model: Country }, { model: User }, { model: Post }])
- await User.insert({
- data: [
- { id: 'string-id-1', country_id: 'string-id-1' },
- { id: 'string-id-2', country_id: 'string-id-1' },
- { id: 'string-id-3', country_id: 'string-id-2' }
- ]
- })
+ await User.insert([
+ { id: 'string-id-1', country_id: 'string-id-1' },
+ { id: 'string-id-2', country_id: 'string-id-1' },
+ { id: 'string-id-3', country_id: 'string-id-2' }
+ ])
- await Post.insert({
- data: [
- { id: 'string-id-1', user_id: 'string-id-1' },
- { id: 'string-id-2', user_id: 'string-id-2' },
- { id: 'string-id-3', user_id: 'string-id-3' },
- { id: 'string-id-4', user_id: 'string-id-1' }
- ]
- })
+ await Post.insert([
+ { id: 'string-id-1', user_id: 'string-id-1' },
+ { id: 'string-id-2', user_id: 'string-id-2' },
+ { id: 'string-id-3', user_id: 'string-id-3' },
+ { id: 'string-id-4', user_id: 'string-id-1' }
+ ])
- await Country.insert({
- data: [{ id: 'string-id-1' }, { id: 'string-id-2' }]
- })
+ await Country.insert([{ id: 'string-id-1' }, { id: 'string-id-2' }])
const country = Country.query()
.with('posts')
@@ -321,9 +307,7 @@ describe('Features – Relations – Has Many Through', () => {
createStore([{ model: Country }, { model: User }, { model: Post }])
- await Country.insert({
- data: [{ id: 1 }]
- })
+ await Country.insert([{ id: 1 }])
const country = Country.query()
.with('posts')
@@ -387,15 +371,11 @@ describe('Features – Relations – Has Many Through', () => {
createStore([{ model: Country }, { model: User }, { model: Post }])
await User.insert({
- data: {
- id: 1,
- country_id: 1
- }
+ id: 1,
+ country_id: 1
})
- await Country.insert({
- data: [{ id: 1 }]
- })
+ await Country.insert([{ id: 1 }])
const country = Country.query()
.with('posts')
@@ -464,25 +444,19 @@ describe('Features – Relations – Has Many Through', () => {
createStore([{ model: Country }, { model: User }, { model: Post }])
- await User.insert({
- data: [
- { u_id: 1, country_id: 1 },
- { u_id: 2, country_id: 1 },
- { u_id: 3, country_id: 2 }
- ]
- })
+ await User.insert([
+ { u_id: 1, country_id: 1 },
+ { u_id: 2, country_id: 1 },
+ { u_id: 3, country_id: 2 }
+ ])
- await Post.insert({
- data: [
- { p_id: 1, user_id: 1 },
- { p_id: 2, user_id: 2 },
- { p_id: 3, user_id: 3 }
- ]
- })
+ await Post.insert([
+ { p_id: 1, user_id: 1 },
+ { p_id: 2, user_id: 2 },
+ { p_id: 3, user_id: 3 }
+ ])
- await Country.insert({
- data: [{ c_id: 1 }, { c_id: 2 }]
- })
+ await Country.insert([{ c_id: 1 }, { c_id: 2 }])
const country = Country.query()
.with('posts')
@@ -568,28 +542,22 @@ describe('Features – Relations – Has Many Through', () => {
createStore([{ model: Country }, { model: User }, { model: Post }])
- await User.insert({
- data: [
- { id: 11, u_id: 1, country_id: 1 },
- { id: 12, u_id: 2, country_id: 1 },
- { id: 13, u_id: 3, country_id: 2 }
- ]
- })
+ await User.insert([
+ { id: 11, u_id: 1, country_id: 1 },
+ { id: 12, u_id: 2, country_id: 1 },
+ { id: 13, u_id: 3, country_id: 2 }
+ ])
- await Post.insert({
- data: [
- { id: 11, p_id: 1, user_id: 1 },
- { id: 12, p_id: 2, user_id: 2 },
- { id: 13, p_id: 3, user_id: 3 }
- ]
- })
+ await Post.insert([
+ { id: 11, p_id: 1, user_id: 1 },
+ { id: 12, p_id: 2, user_id: 2 },
+ { id: 13, p_id: 3, user_id: 3 }
+ ])
- await Country.insert({
- data: [
- { id: 11, c_id: 1 },
- { id: 12, c_id: 2 }
- ]
- })
+ await Country.insert([
+ { id: 11, c_id: 1 },
+ { id: 12, c_id: 2 }
+ ])
const country = Country.query()
.with('posts')
diff --git a/test/feature/relations/HasOne.spec.ts b/test/feature/relations/HasOne.spec.ts
index 37506b4c..542035b4 100644
--- a/test/feature/relations/HasOne.spec.ts
+++ b/test/feature/relations/HasOne.spec.ts
@@ -28,10 +28,8 @@ describe('Features – Relations – Has One', () => {
const store = createStore([{ model: User }, { model: Phone }])
await User.insert({
- data: {
- id: 1,
- phone: { id: 1, user_id: 1 }
- }
+ id: 1,
+ phone: { id: 1, user_id: 1 }
})
const expected = createState({
@@ -49,9 +47,7 @@ describe('Features – Relations – Has One', () => {
it('can create data when the has one relation is empty', async () => {
const store = createStore([{ model: User }, { model: Phone }])
- await User.insert({
- data: { $id: '1', id: 1 }
- })
+ await User.insert({ $id: '1', id: 1 })
const expected = createState({
users: {
@@ -67,10 +63,8 @@ describe('Features – Relations – Has One', () => {
const store = createStore([{ model: User }, { model: Phone }])
await User.insert({
- data: {
- id: 1,
- phone: null
- }
+ id: 1,
+ phone: null
})
const expected = createState({
@@ -86,9 +80,7 @@ describe('Features – Relations – Has One', () => {
it('can create data when the has one relation is set to primitive value', async () => {
const store = createStore([{ model: User }, { model: Phone }])
- await User.insert({
- data: { id: 1, phone: 1 }
- })
+ await User.insert({ id: 1, phone: 1 })
const expected = createState({
users: {
@@ -103,18 +95,16 @@ describe('Features – Relations – Has One', () => {
it('it can create data containing mixed fields value', async () => {
const store = createStore([{ model: User }, { model: Phone }])
- await User.insert({
- data: [
- {
- id: 1,
- phone: { id: 1, user_id: 1 }
- },
- {
- id: 2,
- phone: null
- }
- ]
- })
+ await User.insert([
+ {
+ id: 1,
+ phone: { id: 1, user_id: 1 }
+ },
+ {
+ id: 2,
+ phone: null
+ }
+ ])
const expected = createState({
users: {
@@ -145,10 +135,8 @@ describe('Features – Relations – Has One', () => {
const store = createStore([{ model: User }, { model: Phone }])
await User.insert({
- data: {
- id: 1,
- phone: { id: 1 }
- }
+ id: 1,
+ phone: { id: 1 }
})
const expected = createState({
@@ -167,10 +155,8 @@ describe('Features – Relations – Has One', () => {
createStore([{ model: User }, { model: Phone }])
await User.insert({
- data: {
- id: 1,
- phone: { id: 1 }
- }
+ id: 1,
+ phone: { id: 1 }
})
const expected = {
@@ -194,9 +180,7 @@ describe('Features – Relations – Has One', () => {
it('can resolve the empty has one relation', async () => {
const store = createStore([{ model: User }, { model: Phone }])
- await User.insert({
- data: { id: 1 }
- })
+ await User.insert({ id: 1 })
const expected = { $id: '1', id: 1, phone: null }
diff --git a/test/feature/relations/HasOneThrough.spec.ts b/test/feature/relations/HasOneThrough.spec.ts
new file mode 100644
index 00000000..89831179
--- /dev/null
+++ b/test/feature/relations/HasOneThrough.spec.ts
@@ -0,0 +1,551 @@
+import { createStore, createState } from 'test/support/Helpers'
+import Model from '@/model/Model'
+
+describe('Features – Relations – Has One Through', () => {
+ it('can create entities containing the relation', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ // @Attribute()
+ id!: number
+
+ // @HasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ image!: Image
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ image: this.hasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ }
+ }
+ }
+
+ class Profile extends Model {
+ static entity = 'profiles'
+
+ // @Attribute()
+ id!: number
+
+ // @Attribute()
+ user_id!: number
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ user_id: this.attr(null)
+ }
+ }
+ }
+
+ class Image extends Model {
+ static entity = 'images'
+
+ // @Attribute()
+ id!: number
+
+ // @Attribute()
+ profile_id!: number
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ profile_id: this.attr(null)
+ }
+ }
+ }
+
+ const store = createStore([
+ { model: User },
+ { model: Profile },
+ { model: Image }
+ ])
+
+ await User.insert({
+ id: 1,
+ image: { id: 1 }
+ })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, image: null }
+ },
+ images: {
+ 1: { $id: '1', id: 1, profile_id: null }
+ },
+ profiles: {}
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can resolve the relation', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ // @Attribute()
+ id!: number
+
+ // @HasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ image!: Image
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ image: this.hasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ }
+ }
+ }
+
+ class Profile extends Model {
+ static entity = 'profiles'
+
+ // @Attribute()
+ id!: number
+
+ // @Attribute()
+ user_id!: number
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ user_id: this.attr(null)
+ }
+ }
+ }
+
+ class Image extends Model {
+ static entity = 'images'
+
+ // @Attribute()
+ id!: number
+
+ // @Attribute()
+ profile_id!: number
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ profile_id: this.attr(null)
+ }
+ }
+ }
+
+ createStore([{ model: User }, { model: Profile }, { model: Image }])
+
+ await User.insert([{ id: 1 }, { id: 2 }])
+
+ await Profile.insert([
+ { id: 3, user_id: 1 },
+ { id: 4, user_id: 2 }
+ ])
+
+ await Image.insert([
+ { id: 5, profile_id: 3 },
+ { id: 6, profile_id: 4 }
+ ])
+
+ const user = User.query()
+ .with('image')
+ .find(1) as User
+
+ expect(user).toBeInstanceOf(User)
+ expect(user.id).toBe(1)
+ expect(user.image).toBeInstanceOf(Image)
+ expect(user.image.profile_id).toBe(3)
+ expect(user.image.id).toBe(5)
+ })
+
+ it('can resolve the relation with string value id', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ // @Str('')
+ id!: number
+
+ // @HasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ image!: Image
+
+ static fields() {
+ return {
+ id: this.string(''),
+ image: this.hasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ }
+ }
+ }
+
+ class Profile extends Model {
+ static entity = 'profiles'
+
+ // @Str('')
+ id!: number
+
+ // @Str('')
+ user_id!: number
+
+ static fields() {
+ return {
+ id: this.string(''),
+ user_id: this.string('')
+ }
+ }
+ }
+
+ class Image extends Model {
+ static entity = 'images'
+
+ // @Str('')
+ id!: number
+
+ // @Str('')
+ profile_id!: number
+
+ static fields() {
+ return {
+ id: this.string(''),
+ profile_id: this.string('')
+ }
+ }
+ }
+
+ createStore([{ model: User }, { model: Profile }, { model: Image }])
+
+ await User.insert([{ id: 'string-id-1' }, { id: 'string-id-2' }])
+
+ await Profile.insert([
+ { id: 'string-id-3', user_id: 'string-id-1' },
+ { id: 'string-id-4', user_id: 'string-id-2' }
+ ])
+
+ await Image.insert([
+ { id: 'string-id-5', profile_id: 'string-id-3' },
+ { id: 'string-id-6', profile_id: 'string-id-4' }
+ ])
+
+ const user = User.query()
+ .with('image')
+ .find('string-id-1') as User
+
+ expect(user).toBeInstanceOf(User)
+ expect(user.id).toBe('string-id-1')
+ expect(user.image).toBeInstanceOf(Image)
+ expect(user.image.profile_id).toBe('string-id-3')
+ expect(user.image.id).toBe('string-id-5')
+ })
+
+ it('can resolve the relation when the intermediate entity is empty', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ // @Attribute()
+ id!: number
+
+ // @HasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ image!: Image
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ image: this.hasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ }
+ }
+ }
+
+ class Profile extends Model {
+ static entity = 'profiles'
+
+ // @Attribute()
+ id!: number
+
+ // @Attribute()
+ user_id!: number
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ user_id: this.attr(null)
+ }
+ }
+ }
+
+ class Image extends Model {
+ static entity = 'images'
+
+ // @Attribute()
+ id!: number
+
+ // @Attribute()
+ profile_id!: number
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ profile_id: this.attr(null)
+ }
+ }
+ }
+
+ createStore([{ model: User }, { model: Profile }, { model: Image }])
+
+ await User.insert([{ id: 1 }])
+
+ const user = User.query()
+ .with('image')
+ .find(1) as User
+
+ expect(user).toBeInstanceOf(User)
+ expect(user.id).toBe(1)
+ expect(user.image).toBeNull()
+ })
+
+ it('can resolve the relation when the target entity is empty', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ // @Attribute()
+ id!: number
+
+ // @HasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ image!: Image
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ image: this.hasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ }
+ }
+ }
+
+ class Profile extends Model {
+ static entity = 'profiles'
+
+ // @Attribute()
+ id!: number
+
+ // @Attribute()
+ user_id!: number
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ user_id: this.attr(null)
+ }
+ }
+ }
+
+ class Image extends Model {
+ static entity = 'images'
+
+ // @Attribute()
+ id!: number
+
+ // @Attribute()
+ profile_id!: number
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ profile_id: this.attr(null)
+ }
+ }
+ }
+
+ createStore([{ model: User }, { model: Profile }, { model: Image }])
+
+ await User.insert({ id: 1 })
+
+ await Profile.insert({
+ id: 1,
+ user_id: 1
+ })
+
+ const user = User.query()
+ .with('image')
+ .find(1) as User
+
+ expect(user).toBeInstanceOf(User)
+ expect(user.id).toBe(1)
+ expect(user.image).toBeNull()
+ })
+
+ it('can resolve the relation with custom primary keys', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static primaryKey = 'u_id'
+
+ // @Attribute()
+ u_id!: number
+
+ // @HasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ image!: Image
+
+ static fields() {
+ return {
+ u_id: this.attr(null),
+ image: this.hasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ }
+ }
+ }
+
+ class Profile extends Model {
+ static entity = 'profiles'
+
+ static primaryKey = 'p_id'
+
+ // @Attribute()
+ p_id!: number
+
+ // @Attribute()
+ user_id!: number
+
+ static fields() {
+ return {
+ p_id: this.attr(null),
+ user_id: this.attr(null)
+ }
+ }
+ }
+
+ class Image extends Model {
+ static entity = 'images'
+
+ static primaryKey = 'i_id'
+
+ // @Attribute()
+ i_id!: number
+
+ // @Attribute()
+ profile_id!: number
+
+ static fields() {
+ return {
+ i_id: this.attr(null),
+ profile_id: this.attr(null)
+ }
+ }
+ }
+
+ createStore([{ model: User }, { model: Profile }, { model: Image }])
+
+ await User.insert([{ u_id: 1 }, { u_id: 2 }])
+
+ await Profile.insert([
+ { p_id: 3, user_id: 1 },
+ { p_id: 4, user_id: 2 }
+ ])
+
+ await Image.insert([
+ { i_id: 5, profile_id: 3 },
+ { i_id: 6, profile_id: 4 }
+ ])
+
+ const user = User.query()
+ .with('image')
+ .find(1) as User
+
+ expect(user).toBeInstanceOf(User)
+ expect(user.u_id).toBe(1)
+ expect(user.image).toBeInstanceOf(Image)
+ expect(user.image.profile_id).toBe(3)
+ expect(user.image.i_id).toBe(5)
+ })
+
+ it('can resolve the relation with custom local keys', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ // @Attribute()
+ id!: number
+
+ // @Attribute()
+ u_id!: number
+
+ // @HasOneThrough(Image, Profile, 'user_id', 'profile_id', 'u_id', 'p_id')
+ image!: Image
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ u_id: this.attr(null),
+ image: this.hasOneThrough(
+ Image,
+ Profile,
+ 'user_id',
+ 'profile_id',
+ 'u_id',
+ 'p_id'
+ )
+ }
+ }
+ }
+
+ class Profile extends Model {
+ static entity = 'profiles'
+
+ // @Attribute()
+ id!: number
+
+ // @Attribute()
+ p_id!: number
+
+ // @Attribute()
+ user_id!: number
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ p_id: this.attr(null),
+ user_id: this.attr(null)
+ }
+ }
+ }
+
+ class Image extends Model {
+ static entity = 'images'
+
+ // @Attribute()
+ id!: number
+
+ // @Attribute()
+ i_id!: number
+
+ // @Attribute()
+ profile_id!: number
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ i_id: this.attr(null),
+ profile_id: this.attr(null)
+ }
+ }
+ }
+
+ createStore([{ model: User }, { model: Profile }, { model: Image }])
+
+ await User.insert([
+ { id: 11, u_id: 1 },
+ { id: 12, u_id: 2 }
+ ])
+
+ await Profile.insert([
+ { id: 13, p_id: 3, user_id: 1 },
+ { id: 14, p_id: 4, user_id: 2 }
+ ])
+
+ await Image.insert([
+ { id: 15, i_id: 5, profile_id: 3 },
+ { id: 16, i_id: 6, profile_id: 4 }
+ ])
+
+ const user = User.query()
+ .with('image')
+ .find(11) as User
+
+ expect(user).toBeInstanceOf(User)
+ expect(user.id).toBe(11)
+ expect(user.image).toBeInstanceOf(Image)
+ expect(user.image.profile_id).toBe(3)
+ expect(user.image.id).toBe(15)
+ })
+})
diff --git a/test/feature/relations/MorphMany_Persist.spec.ts b/test/feature/relations/MorphMany_Persist.spec.ts
index b2a0297c..2e7ce1df 100644
--- a/test/feature/relations/MorphMany_Persist.spec.ts
+++ b/test/feature/relations/MorphMany_Persist.spec.ts
@@ -53,23 +53,21 @@ describe('Feature – Relations – Morph Many – Persist', () => {
])
await Post.create({
- data: {
- id: 1,
- comments: [
- {
- id: 2,
- body: 'comment1',
- commentable_id: 1,
- commentable_type: 'posts'
- },
- {
- id: 3,
- body: 'comment2',
- commentable_id: 1,
- commentable_type: 'posts'
- }
- ]
- }
+ id: 1,
+ comments: [
+ {
+ id: 2,
+ body: 'comment1',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ },
+ {
+ id: 3,
+ body: 'comment2',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ }
+ ]
})
createState({
@@ -156,38 +154,36 @@ describe('Feature – Relations – Morph Many – Persist', () => {
{ model: Comment }
])
- await Post.create({
- data: [
- {
- id: 1,
- comments: [
- {
- id: 2,
- body: 'comment1',
- commentable_id: 1,
- commentable_type: 'posts'
- },
- {
- id: 3,
- body: 'comment2',
- commentable_id: 1,
- commentable_type: 'posts'
- }
- ]
- },
- {
- id: 2,
- comments: [
- {
- id: 4,
- body: 'comment3',
- commentable_id: 2,
- commentable_type: 'posts'
- }
- ]
- }
- ]
- })
+ await Post.create([
+ {
+ id: 1,
+ comments: [
+ {
+ id: 2,
+ body: 'comment1',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ },
+ {
+ id: 3,
+ body: 'comment2',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ }
+ ]
+ },
+ {
+ id: 2,
+ comments: [
+ {
+ id: 4,
+ body: 'comment3',
+ commentable_id: 2,
+ commentable_type: 'posts'
+ }
+ ]
+ }
+ ])
expect(store.state.entities.posts.data['1'].id).toBe(1)
expect(store.state.entities.posts.data['2'].id).toBe(2)
@@ -256,13 +252,11 @@ describe('Feature – Relations – Morph Many – Persist', () => {
])
await Post.create({
- data: {
- id: 1,
- comments: [
- { id: 2, body: 'comment1' },
- { id: 3, body: 'comment2' }
- ]
- }
+ id: 1,
+ comments: [
+ { id: 2, body: 'comment1' },
+ { id: 3, body: 'comment2' }
+ ]
})
createState({
@@ -350,13 +344,11 @@ describe('Feature – Relations – Morph Many – Persist', () => {
])
await Post.create({
- data: {
- id: 1,
- comments: [
- { id: 2, body: 'comment1', commentable_id: 1 },
- { id: 3, body: 'comment2', commentable_id: 2 }
- ]
- }
+ id: 1,
+ comments: [
+ { id: 2, body: 'comment1', commentable_id: 1 },
+ { id: 3, body: 'comment2', commentable_id: 2 }
+ ]
})
expect(store.state.entities.posts.data['1'].id).toBe(1)
@@ -420,12 +412,10 @@ describe('Feature – Relations – Morph Many – Persist', () => {
{ model: Comment }
])
- await Comment.create({
- data: [
- { id: 2, body: 'comment1' },
- { id: 3, body: 'comment2' }
- ]
- })
+ await Comment.create([
+ { id: 2, body: 'comment1' },
+ { id: 3, body: 'comment2' }
+ ])
createState({
posts: {},
@@ -520,19 +510,17 @@ describe('Feature – Relations – Morph Many – Persist', () => {
])
await User.create({
- data: {
- id: 1,
- posts: [
- {
- id: 5,
- user_id: 1,
- comments: [
- { id: 2, body: 'comment1', commentable_id: 1 },
- { id: 3, body: 'comment2', commentable_id: 2 }
- ]
- }
- ]
- }
+ id: 1,
+ posts: [
+ {
+ id: 5,
+ user_id: 1,
+ comments: [
+ { id: 2, body: 'comment1', commentable_id: 1 },
+ { id: 3, body: 'comment2', commentable_id: 2 }
+ ]
+ }
+ ]
})
createState({
@@ -606,9 +594,7 @@ describe('Feature – Relations – Morph Many – Persist', () => {
const store = createStore([{ model: Post }, { model: Comment }])
- await store.dispatch('entities/posts/create', {
- data: { id: 1, comments: null }
- })
+ await Post.create({ id: 1, comments: null })
const expected = createState({
posts: {
@@ -670,24 +656,17 @@ describe('Feature – Relations – Morph Many – Persist', () => {
{ model: Comment }
])
- await store.dispatch('entities/posts/create', {
- data: {
- id: 1,
- comments: [
- {
- id: 1,
- body: 'comment1',
- commentabe_id: 1,
- commentable_type: 'posts'
- },
- {
- id: 2,
- body: 'comment2',
- commentabe_id: 1,
- commentable_type: 'posts'
- }
- ]
- }
+ await Post.create({
+ id: 1,
+ comments: [
+ {
+ id: 1,
+ body: 'comment1',
+ commentabe_id: 1,
+ commentable_type: 'posts'
+ },
+ { id: 2, body: 'comment2', commentabe_id: 1, commentable_type: 'posts' }
+ ]
})
const post = store.getters['entities/posts/query']()
@@ -700,7 +679,7 @@ describe('Feature – Relations – Morph Many – Persist', () => {
expect(post.comments[1]).toBeInstanceOf(Comment)
})
- it('generates id or type for the moprh relation', async () => {
+ it('generates id or type for the morph relation', async () => {
class Post extends Model {
static entity = 'posts'
@@ -750,14 +729,12 @@ describe('Feature – Relations – Morph Many – Persist', () => {
{ model: Comment }
])
- await store.dispatch('entities/posts/create', {
- data: {
- id: 1,
- comments: [
- { id: 1, body: 'comment1' },
- { id: 2, body: 'comment2' }
- ]
- }
+ await Post.create({
+ id: 1,
+ comments: [
+ { id: 1, body: 'comment1' },
+ { id: 2, body: 'comment2' }
+ ]
})
const post = store.getters['entities/posts/query']()
diff --git a/test/feature/relations/MorphMany_Retrieve.spec.ts b/test/feature/relations/MorphMany_Retrieve.spec.ts
index 28b6d475..88d585ab 100644
--- a/test/feature/relations/MorphMany_Retrieve.spec.ts
+++ b/test/feature/relations/MorphMany_Retrieve.spec.ts
@@ -73,42 +73,36 @@ describe('Feature – Relations – Morph Many – Retrieve', () => {
createStore([{ model: Post }, { model: Video }, { model: Comment }])
- await Post.create({
- data: [{ id: 1 }, { id: 5 }, { id: 6 }]
- })
-
- await Video.create({
- data: { id: 3 }
- })
-
- await Comment.create({
- data: [
- {
- id: '1',
- body: 'comment1',
- commentable_id: 1,
- commentable_type: 'posts'
- },
- {
- id: '2',
- body: 'comment2',
- commentable_id: 3,
- commentable_type: 'videos'
- },
- {
- id: '3',
- body: 'comment3',
- commentable_id: 1,
- commentable_type: 'posts'
- },
- {
- id: '4',
- body: 'comment4',
- commentable_id: 5,
- commentable_type: 'posts'
- }
- ]
- })
+ await Post.create([{ id: 1 }, { id: 5 }, { id: 6 }])
+
+ await Video.create({ id: 3 })
+
+ await Comment.create([
+ {
+ id: '1',
+ body: 'comment1',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ },
+ {
+ id: '2',
+ body: 'comment2',
+ commentable_id: 3,
+ commentable_type: 'videos'
+ },
+ {
+ id: '3',
+ body: 'comment3',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ },
+ {
+ id: '4',
+ body: 'comment4',
+ commentable_id: 5,
+ commentable_type: 'posts'
+ }
+ ])
const post = Post.query()
.with('comments')
@@ -197,42 +191,36 @@ describe('Feature – Relations – Morph Many – Retrieve', () => {
createStore([{ model: Post }, { model: Video }, { model: Comment }])
- await Post.create({
- data: [{ post_id: 1 }, { post_id: 5 }]
- })
-
- await Video.create({
- data: { id: 3 }
- })
-
- await Comment.create({
- data: [
- {
- id: '1',
- body: 'comment1',
- commentable_id: 1,
- commentable_type: 'posts'
- },
- {
- id: '2',
- body: 'comment2',
- commentable_id: 3,
- commentable_type: 'videos'
- },
- {
- id: '3',
- body: 'comment3',
- commentable_id: 1,
- commentable_type: 'posts'
- },
- {
- id: '4',
- body: 'comment4',
- commentable_id: 5,
- commentable_type: 'posts'
- }
- ]
- })
+ await Post.create([{ post_id: 1 }, { post_id: 5 }])
+
+ await Video.create({ id: 3 })
+
+ await Comment.create([
+ {
+ id: '1',
+ body: 'comment1',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ },
+ {
+ id: '2',
+ body: 'comment2',
+ commentable_id: 3,
+ commentable_type: 'videos'
+ },
+ {
+ id: '3',
+ body: 'comment3',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ },
+ {
+ id: '4',
+ body: 'comment4',
+ commentable_id: 5,
+ commentable_type: 'posts'
+ }
+ ])
const post = Post.query()
.with('comments')
@@ -318,45 +306,39 @@ describe('Feature – Relations – Morph Many – Retrieve', () => {
createStore([{ model: Post }, { model: Video }, { model: Comment }])
- await Post.create({
- data: [
- { id: 2, post_id: 1 },
- { id: 3, post_id: 5 }
- ]
- })
-
- await Video.create({
- data: [{ id: 3 }, { id: 4 }]
- })
-
- await Comment.create({
- data: [
- {
- id: '1',
- body: 'comment1',
- commentable_id: 1,
- commentable_type: 'posts'
- },
- {
- id: '2',
- body: 'comment2',
- commentable_id: 3,
- commentable_type: 'videos'
- },
- {
- id: '3',
- body: 'comment3',
- commentable_id: 1,
- commentable_type: 'posts'
- },
- {
- id: '4',
- body: 'comment4',
- commentable_id: 5,
- commentable_type: 'posts'
- }
- ]
- })
+ await Post.create([
+ { id: 2, post_id: 1 },
+ { id: 3, post_id: 5 }
+ ])
+
+ await Video.create([{ id: 3 }, { id: 4 }])
+
+ await Comment.create([
+ {
+ id: '1',
+ body: 'comment1',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ },
+ {
+ id: '2',
+ body: 'comment2',
+ commentable_id: 3,
+ commentable_type: 'videos'
+ },
+ {
+ id: '3',
+ body: 'comment3',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ },
+ {
+ id: '4',
+ body: 'comment4',
+ commentable_id: 5,
+ commentable_type: 'posts'
+ }
+ ])
const post = Post.query()
.with('comments')
@@ -439,13 +421,9 @@ describe('Feature – Relations – Morph Many – Retrieve', () => {
createStore([{ model: Post }, { model: Video }, { model: Comment }])
- await Post.insert({
- data: { id: 1, comments: [{ id: 1 }, { id: 3 }, { id: 2 }] }
- })
+ await Post.insert({ id: 1, comments: [{ id: 1 }, { id: 3 }, { id: 2 }] })
- await Video.insert({
- data: { id: 1, comments: [{ id: 4 }, { id: 6 }, { id: 5 }] }
- })
+ await Video.insert({ id: 1, comments: [{ id: 4 }, { id: 6 }, { id: 5 }] })
const post = Post.query()
.with('comments', (query) => {
diff --git a/test/feature/relations/MorphOne_Persist.spec.ts b/test/feature/relations/MorphOne_Persist.spec.ts
index da640e12..c828ff07 100644
--- a/test/feature/relations/MorphOne_Persist.spec.ts
+++ b/test/feature/relations/MorphOne_Persist.spec.ts
@@ -30,15 +30,13 @@ describe('Features – Relations – Morph One – Persist', () => {
const store = createStore([{ model: Post }, { model: Comment }])
- await store.dispatch('entities/posts/create', {
- data: {
+ await Post.create({
+ id: 1,
+ comment: {
id: 1,
- comment: {
- id: 1,
- body: 'The Body',
- commentable_type: 'posts',
- commentable_id: 1
- }
+ body: 'The Body',
+ commentable_type: 'posts',
+ commentable_id: 1
}
})
@@ -104,10 +102,8 @@ describe('Features – Relations – Morph One – Persist', () => {
])
await Post.create({
- data: {
- id: 1,
- comment: { id: 2, body: 'comment1' }
- }
+ id: 1,
+ comment: { id: 2, body: 'comment1' }
})
expect(store.state.entities.posts.data['1'].id).toBe(1)
@@ -161,10 +157,8 @@ describe('Features – Relations – Morph One – Persist', () => {
])
await Post.create({
- data: {
- id: 1,
- comment: { id: 2, body: 'comment1', commentable_id: 1 }
- }
+ id: 1,
+ comment: { id: 2, body: 'comment1', commentable_id: 1 }
})
createState({
@@ -233,12 +227,10 @@ describe('Features – Relations – Morph One – Persist', () => {
{ model: Comment }
])
- await Comment.create({
- data: [
- { id: 2, body: 'comment1' },
- { id: 3, body: 'comment2' }
- ]
- })
+ await Comment.create([
+ { id: 2, body: 'comment1' },
+ { id: 3, body: 'comment2' }
+ ])
expect(store.state.entities.comments.data['2'].id).toBe(2)
expect(store.state.entities.comments.data['2'].commentable_id).toBe(null)
@@ -304,16 +296,14 @@ describe('Features – Relations – Morph One – Persist', () => {
])
await User.create({
- data: {
- id: 1,
- posts: [
- {
- id: 5,
- user_id: 1,
- comment: { id: 2, body: 'comment1', commentable_id: 1 }
- }
- ]
- }
+ id: 1,
+ posts: [
+ {
+ id: 5,
+ user_id: 1,
+ comment: { id: 2, body: 'comment1', commentable_id: 1 }
+ }
+ ]
})
createState({
@@ -372,23 +362,21 @@ describe('Features – Relations – Morph One – Persist', () => {
const store = createStore([{ model: Post }, { model: Comment }])
- await store.dispatch('entities/posts/create', {
- data: [
- {
+ await Post.create([
+ {
+ id: 1,
+ comment: {
id: 1,
- comment: {
- id: 1,
- body: 'The Body',
- commentable_type: 'posts',
- commentable_id: 1
- }
- },
- {
- id: 2,
- comment: null
+ body: 'The Body',
+ commentable_type: 'posts',
+ commentable_id: 1
}
- ]
- })
+ },
+ {
+ id: 2,
+ comment: null
+ }
+ ])
const expected = createState({
posts: {
@@ -452,15 +440,13 @@ describe('Features – Relations – Morph One – Persist', () => {
{ model: Comment }
])
- await store.dispatch('entities/posts/create', {
- data: {
+ await Post.create({
+ id: 1,
+ comment: {
id: 1,
- comment: {
- id: 1,
- body: 'comment1',
- commentabe_id: 1,
- commentable_type: 'posts'
- }
+ body: 'comment1',
+ commentabe_id: 1,
+ commentable_type: 'posts'
}
})
@@ -472,7 +458,7 @@ describe('Features – Relations – Morph One – Persist', () => {
expect(post.comment).toBeInstanceOf(Comment)
})
- it('generates id or type for the moprh relation', async () => {
+ it('generates id or type for the morph relation', async () => {
class Post extends Model {
static entity = 'posts'
@@ -514,11 +500,9 @@ describe('Features – Relations – Morph One – Persist', () => {
{ model: Comment }
])
- await store.dispatch('entities/posts/create', {
- data: {
- id: 1,
- comment: { id: 1, body: 'comment1' }
- }
+ await Post.create({
+ id: 1,
+ comment: { id: 1, body: 'comment1' }
})
const post = store.getters['entities/posts/query']()
diff --git a/test/feature/relations/MorphOne_Retrieve.spec.ts b/test/feature/relations/MorphOne_Retrieve.spec.ts
index f0eb9a51..b1eeb6e1 100644
--- a/test/feature/relations/MorphOne_Retrieve.spec.ts
+++ b/test/feature/relations/MorphOne_Retrieve.spec.ts
@@ -65,46 +65,40 @@ describe('Feature – Relations – Morph One – Retrieve', () => {
createStore([{ model: Post }, { model: Video }, { model: Comment }])
- await Post.create({
- data: [{ id: 1 }, { id: 5 }]
- })
-
- await Video.create({
- data: { id: 3 }
- })
-
- await Comment.create({
- data: [
- {
- $id: '1',
- id: '1',
- body: 'comment1',
- commentable_id: 1,
- commentable_type: 'posts'
- },
- {
- $id: '2',
- id: '2',
- body: 'comment2',
- commentable_id: 3,
- commentable_type: 'videos'
- },
- {
- $id: '3',
- id: '3',
- body: 'comment3',
- commentable_id: 2,
- commentable_type: 'posts'
- },
- {
- $id: '4',
- id: '4',
- body: 'comment4',
- commentable_id: 5,
- commentable_type: 'posts'
- }
- ]
- })
+ await Post.create([{ id: 1 }, { id: 5 }])
+
+ await Video.create({ id: 3 })
+
+ await Comment.create([
+ {
+ $id: '1',
+ id: 1,
+ body: 'comment1',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ },
+ {
+ $id: '2',
+ id: 2,
+ body: 'comment2',
+ commentable_id: 3,
+ commentable_type: 'videos'
+ },
+ {
+ $id: '3',
+ id: 3,
+ body: 'comment3',
+ commentable_id: 2,
+ commentable_type: 'posts'
+ },
+ {
+ $id: '4',
+ id: 4,
+ body: 'comment4',
+ commentable_id: 5,
+ commentable_type: 'posts'
+ }
+ ])
const post = Post.query()
.with('comment')
@@ -175,13 +169,9 @@ describe('Feature – Relations – Morph One – Retrieve', () => {
createStore([{ model: Post }, { model: Video }, { model: Comment }])
- await Post.create({
- data: [{ id: 1 }, { id: 5 }]
- })
+ await Post.create([{ id: 1 }, { id: 5 }])
- await Video.create({
- data: { id: 3 }
- })
+ await Video.create({ id: 3 })
const post = Post.query()
.with('comments')
@@ -254,46 +244,40 @@ describe('Feature – Relations – Morph One – Retrieve', () => {
createStore([{ model: Post }, { model: Video }, { model: Comment }])
- await Post.create({
- data: [{ post_id: 1 }, { post_id: 5 }]
- })
-
- await Video.create({
- data: { id: 3 }
- })
-
- await Comment.create({
- data: [
- {
- $id: '1',
- id: '1',
- body: 'comment1',
- commentable_id: 1,
- commentable_type: 'posts'
- },
- {
- $id: '2',
- id: '2',
- body: 'comment2',
- commentable_id: 3,
- commentable_type: 'videos'
- },
- {
- $id: '3',
- id: '3',
- body: 'comment3',
- commentable_id: 2,
- commentable_type: 'posts'
- },
- {
- $id: '4',
- id: '4',
- body: 'comment4',
- commentable_id: 5,
- commentable_type: 'posts'
- }
- ]
- })
+ await Post.create([{ post_id: 1 }, { post_id: 5 }])
+
+ await Video.create({ id: 3 })
+
+ await Comment.create([
+ {
+ $id: '1',
+ id: 1,
+ body: 'comment1',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ },
+ {
+ $id: '2',
+ id: 2,
+ body: 'comment2',
+ commentable_id: 3,
+ commentable_type: 'videos'
+ },
+ {
+ $id: '3',
+ id: 3,
+ body: 'comment3',
+ commentable_id: 2,
+ commentable_type: 'posts'
+ },
+ {
+ $id: '4',
+ id: 4,
+ body: 'comment4',
+ commentable_id: 5,
+ commentable_type: 'posts'
+ }
+ ])
const post = Post.query()
.with('comment')
@@ -373,49 +357,43 @@ describe('Feature – Relations – Morph One – Retrieve', () => {
createStore([{ model: Post }, { model: Video }, { model: Comment }])
- await Post.create({
- data: [
- { id: 2, post_id: 1 },
- { id: 3, post_id: 5 }
- ]
- })
-
- await Video.create({
- data: { id: 3 }
- })
-
- await Comment.create({
- data: [
- {
- $id: '1',
- id: '1',
- body: 'comment1',
- commentable_id: 1,
- commentable_type: 'posts'
- },
- {
- $id: '2',
- id: '2',
- body: 'comment2',
- commentable_id: 3,
- commentable_type: 'videos'
- },
- {
- $id: '3',
- id: '3',
- body: 'comment3',
- commentable_id: 2,
- commentable_type: 'posts'
- },
- {
- $id: '4',
- id: '4',
- body: 'comment4',
- commentable_id: 5,
- commentable_type: 'posts'
- }
- ]
- })
+ await Post.create([
+ { id: 2, post_id: 1 },
+ { id: 3, post_id: 5 }
+ ])
+
+ await Video.create({ id: 3 })
+
+ await Comment.create([
+ {
+ $id: '1',
+ id: 1,
+ body: 'comment1',
+ commentable_id: 1,
+ commentable_type: 'posts'
+ },
+ {
+ $id: '2',
+ id: 2,
+ body: 'comment2',
+ commentable_id: 3,
+ commentable_type: 'videos'
+ },
+ {
+ $id: '3',
+ id: 3,
+ body: 'comment3',
+ commentable_id: 2,
+ commentable_type: 'posts'
+ },
+ {
+ $id: '4',
+ id: 4,
+ body: 'comment4',
+ commentable_id: 5,
+ commentable_type: 'posts'
+ }
+ ])
const post = Post.query()
.with('comment')
diff --git a/test/feature/relations/MorphTo.spec.ts b/test/feature/relations/MorphTo.spec.ts
index 9164f4f9..d9d75090 100644
--- a/test/feature/relations/MorphTo.spec.ts
+++ b/test/feature/relations/MorphTo.spec.ts
@@ -1,3 +1,4 @@
+/* tslint:disable:variable-name */
import { createStore, createState } from 'test/support/Helpers'
import Model from '@/model/Model'
@@ -6,6 +7,12 @@ describe('Features – Relations – Morph To', () => {
class Post extends Model {
static entity = 'posts'
+ // @Attribute
+ id!: number
+
+ // @MorphOne(Comment, 'commentable_id', 'commentable_type')
+ comment!: Comment
+
static fields() {
return {
id: this.attr(null),
@@ -17,6 +24,21 @@ describe('Features – Relations – Morph To', () => {
class Comment extends Model {
static entity = 'comments'
+ // @Attribute
+ id!: number
+
+ // @Attribute('')
+ body!: string
+
+ // @Attribute
+ commentable_id!: number
+
+ // @Attribute
+ commentable_type!: string
+
+ // @MorphTo('commentable_id', 'commentable_type')
+ commentable!: Post
+
static fields() {
return {
id: this.attr(null),
@@ -30,14 +52,12 @@ describe('Features – Relations – Morph To', () => {
const store = createStore([{ model: Post }, { model: Comment }])
- await store.dispatch('entities/comments/create', {
- data: {
- id: 1,
- body: 'The Body',
- commentable_type: 'posts',
- commentable_id: 1,
- commentable: { id: 1 }
- }
+ await Comment.create({
+ id: 1,
+ body: 'The Body',
+ commentable_type: 'posts',
+ commentable_id: 1,
+ commentable: { id: 1 }
})
const expected = createState({
@@ -63,6 +83,12 @@ describe('Features – Relations – Morph To', () => {
class Post extends Model {
static entity = 'posts'
+ // @Attribute
+ id!: number
+
+ // @MorphMany(Comment, 'commentable_id', 'commentable_type')
+ comments!: Comment[]
+
static fields() {
return {
id: this.attr(null),
@@ -78,6 +104,12 @@ describe('Features – Relations – Morph To', () => {
class Video extends Model {
static entity = 'videos'
+ // @Attribute
+ id!: number
+
+ // @MorphMany(Comment, 'commentable_id', 'commentable_type')
+ comments!: Comment[]
+
static fields() {
return {
id: this.attr(null),
@@ -93,6 +125,21 @@ describe('Features – Relations – Morph To', () => {
class Comment extends Model {
static entity = 'comments'
+ // @Attribute
+ id!: number
+
+ // @Attribute('')
+ body!: string
+
+ // @Attribute
+ commentable_id!: number
+
+ // @Attribute
+ commentable_type!: string
+
+ // @MorphTo('commentable_id', 'commentable_type')
+ commentable!: Post | Video
+
static fields() {
return {
id: this.attr(null),
@@ -104,39 +151,33 @@ describe('Features – Relations – Morph To', () => {
}
}
- const store = createStore([
- { model: Post },
- { model: Video },
- { model: Comment }
- ])
-
- await store.dispatch('entities/posts/insert', {
- data: {
- id: 1,
- comments: [
- { id: 1, body: 'comment1' },
- { id: 2, body: 'comment2' }
- ]
- }
+ createStore([{ model: Post }, { model: Video }, { model: Comment }])
+
+ await Post.insert({
+ id: 1,
+ comments: [
+ { id: 1, body: 'comment1' },
+ { id: 2, body: 'comment2' }
+ ]
})
- await store.dispatch('entities/videos/insert', {
- data: {
- id: 1,
- comments: [
- { id: 3, body: 'comment3' },
- { id: 4, body: 'comment4' }
- ]
- }
+ await Video.insert({
+ id: 1,
+ comments: [
+ { id: 3, body: 'comment3' },
+ { id: 4, body: 'comment4' }
+ ]
})
- const comments = store.getters['entities/comments/query']()
+ const comments = Comment.query()
.with('commentable')
.get()
expect(comments.length).toBe(4)
+
expect(comments[0]).toBeInstanceOf(Comment)
expect(comments[0].commentable).toBeInstanceOf(Post)
+
expect(comments[2]).toBeInstanceOf(Comment)
expect(comments[2].commentable).toBeInstanceOf(Video)
})
@@ -145,6 +186,12 @@ describe('Features – Relations – Morph To', () => {
class Post extends Model {
static entity = 'posts'
+ // @Attribute
+ id!: number
+
+ // @MorphMany(Comment, 'commentable_id', 'commentable_type')
+ comments!: Comment[]
+
static fields() {
return {
id: this.attr(null),
@@ -160,6 +207,12 @@ describe('Features – Relations – Morph To', () => {
class Video extends Model {
static entity = 'videos'
+ // @Attribute
+ id!: number
+
+ // @MorphMany(Comment, 'commentable_id', 'commentable_type')
+ comments!: Comment[]
+
static fields() {
return {
id: this.attr(null),
@@ -175,6 +228,21 @@ describe('Features – Relations – Morph To', () => {
class Comment extends Model {
static entity = 'comments'
+ // @Attribute
+ id!: number
+
+ // @Attribute('')
+ body!: string
+
+ // @Attribute
+ commentable_id!: number
+
+ // @Attribute
+ commentable_type!: string
+
+ // @MorphTo('commentable_id', 'commentable_type')
+ commentable!: Post | Video
+
static fields() {
return {
id: this.attr(null),
@@ -186,22 +254,16 @@ describe('Features – Relations – Morph To', () => {
}
}
- const store = createStore([
- { model: Post },
- { model: Video },
- { model: Comment }
- ])
+ createStore([{ model: Post }, { model: Video }, { model: Comment }])
- await store.dispatch('entities/comments/insert', {
- data: {
- id: 1,
- body: 'Body 01'
- }
+ await Comment.insert({
+ id: 1,
+ body: 'Body 01'
})
- const comment = store.getters['entities/comments/query']()
+ const comment = Comment.query()
.with('commentable')
- .find(1)
+ .find(1) as Comment
expect(comment.commentable).toBe(null)
})
diff --git a/test/feature/relations/MorphToMany_Persist.spec.ts b/test/feature/relations/MorphToMany_Persist.spec.ts
index 2c94e609..aab33f32 100644
--- a/test/feature/relations/MorphToMany_Persist.spec.ts
+++ b/test/feature/relations/MorphToMany_Persist.spec.ts
@@ -1,3 +1,4 @@
+/* tslint:disable:variable-name */
import { createStore, createState } from 'test/support/Helpers'
import Model from '@/model/Model'
@@ -69,13 +70,11 @@ describe('Features – Relations – Morph To Many – Persist', () => {
])
await Post.create({
- data: {
- id: 1,
- tags: [
- { id: 2, name: 'news', pivot: { public: true } },
- { id: 3, name: 'cast' }
- ]
- }
+ id: 1,
+ tags: [
+ { id: 2, name: 'news', pivot: { public: true } },
+ { id: 3, name: 'cast' }
+ ]
})
expect(store.state.entities.posts.data['1'].id).toBe(1)
@@ -159,9 +158,7 @@ describe('Features – Relations – Morph To Many – Persist', () => {
{ model: Taggable }
])
- await store.dispatch('entities/posts/create', {
- data: { id: 1 }
- })
+ await Post.create({ id: 1 })
const expected = {
1: { $id: '1', id: 1, tags: [] }
@@ -236,13 +233,11 @@ describe('Features – Relations – Morph To Many – Persist', () => {
])
await Post.create({
- data: {
- id: 1,
- tags: [
- { id: 2, name: 'news' },
- { id: 3, name: 'cast' }
- ]
- }
+ id: 1,
+ tags: [
+ { id: 2, name: 'news' },
+ { id: 3, name: 'cast' }
+ ]
})
const expected = createState({
@@ -341,13 +336,11 @@ describe('Features – Relations – Morph To Many – Persist', () => {
])
await Post.create({
- data: {
- title: 'Post title.',
- tags: [
- { id: 2, name: 'news' },
- { id: 3, name: 'cast' }
- ]
- }
+ title: 'Post title.',
+ tags: [
+ { id: 2, name: 'news' },
+ { id: 3, name: 'cast' }
+ ]
})
const expected = createState({
@@ -447,13 +440,11 @@ describe('Features – Relations – Morph To Many – Persist', () => {
])
await Post.create({
- data: {
- id: 1,
- tags: [
- { id: 2, name: 'news', tag_pivot: { public: true } },
- { id: 3, name: 'cast' }
- ]
- }
+ id: 1,
+ tags: [
+ { id: 2, name: 'news', tag_pivot: { public: true } },
+ { id: 3, name: 'cast' }
+ ]
})
expect(store.state.entities.taggables.data['1_2_posts'].public).toBe(true)
@@ -464,6 +455,12 @@ describe('Features – Relations – Morph To Many – Persist', () => {
class Post extends Model {
static entity = 'posts'
+ // @Attribute
+ id!: number
+
+ // @MorphToMany(Tag, Taggable, 'tag_id', 'taggable_id', 'taggable_type')
+ tags!: Tag[]
+
static fields() {
return {
id: this.attr(null),
@@ -481,6 +478,12 @@ describe('Features – Relations – Morph To Many – Persist', () => {
class Video extends Model {
static entity = 'videos'
+ // @Attribute
+ id!: number
+
+ // @MorphToMany(Tag, Taggable, 'tag_id', 'taggable_id', 'taggable_type')
+ tags!: Tag[]
+
static fields() {
return {
id: this.attr(null),
@@ -498,6 +501,12 @@ describe('Features – Relations – Morph To Many – Persist', () => {
class Tag extends Model {
static entity = 'tag'
+ // @Attribute
+ id!: number
+
+ // @Attribute('')
+ name!: string
+
static fields() {
return {
id: this.attr(null),
@@ -509,6 +518,18 @@ describe('Features – Relations – Morph To Many – Persist', () => {
class Taggable extends Model {
static entity = 'taggables'
+ // @Attribute
+ id!: number
+
+ // @Attribute
+ tag_id!: number
+
+ // @Attribute
+ taggable_id!: number
+
+ // @Attribute
+ taggable_type!: string
+
static fields() {
return {
id: this.attr(null),
@@ -519,35 +540,33 @@ describe('Features – Relations – Morph To Many – Persist', () => {
}
}
- const store = createStore([
+ createStore([
{ model: Post },
{ model: Video },
{ model: Tag },
{ model: Taggable }
])
- await store.dispatch('entities/posts/create', {
- data: [
- {
- id: 1,
- tags: [
- { id: 1, name: 'news' },
- { id: 2, name: 'cast' }
- ]
- },
- {
- id: 2,
- tags: [
- { id: 1, name: 'news' },
- { id: 2, name: 'cast' }
- ]
- }
- ]
- })
+ await Post.create([
+ {
+ id: 1,
+ tags: [
+ { id: 1, name: 'news' },
+ { id: 2, name: 'cast' }
+ ]
+ },
+ {
+ id: 2,
+ tags: [
+ { id: 1, name: 'news' },
+ { id: 2, name: 'cast' }
+ ]
+ }
+ ])
- const post = store.getters['entities/posts/query']()
+ const post = Post.query()
.with('tags')
- .find(1)
+ .find(1) as Post
expect(post).toBeInstanceOf(Post)
expect(post.tags.length).toBe(2)
diff --git a/test/feature/relations/MorphToMany_Retrieve.spec.ts b/test/feature/relations/MorphToMany_Retrieve.spec.ts
index bbf74db2..a957d14f 100644
--- a/test/feature/relations/MorphToMany_Retrieve.spec.ts
+++ b/test/feature/relations/MorphToMany_Retrieve.spec.ts
@@ -106,46 +106,38 @@ describe('Feature – Relations – Morph To Many – Retrieve', () => {
{ model: Taggable }
])
- await Post.create({
- data: [{ id: 1 }, { id: 5 }, { id: 6 }]
- })
+ await Post.create([{ id: 1 }, { id: 5 }, { id: 6 }])
- await Video.create({
- data: { id: 3 }
- })
+ await Video.create({ id: 3 })
- await Tag.create({
- data: [
- { id: 1, name: 'news' },
- { id: 2, name: 'cast' }
- ]
- })
+ await Tag.create([
+ { id: 1, name: 'news' },
+ { id: 2, name: 'cast' }
+ ])
- await Taggable.create({
- data: [
- {
- id: 1,
- tag_id: 1,
- taggable_id: 1,
- taggable_type: 'posts',
- is_public: true
- },
- {
- id: 2,
- tag_id: 2,
- taggable_id: 3,
- taggable_type: 'videos',
- is_public: true
- },
- {
- id: 3,
- tag_id: 2,
- taggable_id: 1,
- taggable_type: 'posts',
- is_public: false
- }
- ]
- })
+ await Taggable.create([
+ {
+ id: 1,
+ tag_id: 1,
+ taggable_id: 1,
+ taggable_type: 'posts',
+ is_public: true
+ },
+ {
+ id: 2,
+ tag_id: 2,
+ taggable_id: 3,
+ taggable_type: 'videos',
+ is_public: true
+ },
+ {
+ id: 3,
+ tag_id: 2,
+ taggable_id: 1,
+ taggable_type: 'posts',
+ is_public: false
+ }
+ ])
const post = Post.query()
.with('tags')
@@ -262,28 +254,20 @@ describe('Feature – Relations – Morph To Many – Retrieve', () => {
{ model: Taggable }
])
- await Post.create({
- data: [{ post_id: 1 }, { post_id: 5 }]
- })
+ await Post.create([{ post_id: 1 }, { post_id: 5 }])
- await Video.create({
- data: { id: 3 }
- })
+ await Video.create({ id: 3 })
- await Tag.create({
- data: [
- { id: 1, name: 'news' },
- { id: 2, name: 'cast' }
- ]
- })
+ await Tag.create([
+ { id: 1, name: 'news' },
+ { id: 2, name: 'cast' }
+ ])
- await Taggable.create({
- data: [
- { id: 1, tag_id: 1, taggable_id: 1, taggable_type: 'posts' },
- { id: 2, tag_id: 2, taggable_id: 3, taggable_type: 'videos' },
- { id: 3, tag_id: 2, taggable_id: 1, taggable_type: 'posts' }
- ]
- })
+ await Taggable.create([
+ { id: 1, tag_id: 1, taggable_id: 1, taggable_type: 'posts' },
+ { id: 2, tag_id: 2, taggable_id: 3, taggable_type: 'videos' },
+ { id: 3, tag_id: 2, taggable_id: 1, taggable_type: 'posts' }
+ ])
const post = Post.query()
.with('tags')
@@ -395,31 +379,23 @@ describe('Feature – Relations – Morph To Many – Retrieve', () => {
{ model: Taggable }
])
- await Post.create({
- data: [
- { id: 1, post_id: 10 },
- { id: 5, post_id: 11 }
- ]
- })
+ await Post.create([
+ { id: 1, post_id: 10 },
+ { id: 5, post_id: 11 }
+ ])
- await Video.create({
- data: { id: 3 }
- })
+ await Video.create({ id: 3 })
- await Tag.create({
- data: [
- { id: 1, name: 'news' },
- { id: 2, name: 'cast' }
- ]
- })
+ await Tag.create([
+ { id: 1, name: 'news' },
+ { id: 2, name: 'cast' }
+ ])
- await Taggable.create({
- data: [
- { id: 1, tag_id: 1, taggable_id: 10, taggable_type: 'posts' },
- { id: 2, tag_id: 2, taggable_id: 3, taggable_type: 'videos' },
- { id: 3, tag_id: 2, taggable_id: 10, taggable_type: 'posts' }
- ]
- })
+ await Taggable.create([
+ { id: 1, tag_id: 1, taggable_id: 10, taggable_type: 'posts' },
+ { id: 2, tag_id: 2, taggable_id: 3, taggable_type: 'videos' },
+ { id: 3, tag_id: 2, taggable_id: 10, taggable_type: 'posts' }
+ ])
const post = Post.query()
.with('tags')
@@ -536,31 +512,23 @@ describe('Feature – Relations – Morph To Many – Retrieve', () => {
{ model: Taggable }
])
- await Post.create({
- data: [
- { id: 1, post_id: 10 },
- { id: 5, post_id: 11 }
- ]
- })
+ await Post.create([
+ { id: 1, post_id: 10 },
+ { id: 5, post_id: 11 }
+ ])
- await Video.create({
- data: { id: 3 }
- })
+ await Video.create({ id: 3 })
- await Tag.create({
- data: [
- { id: 1, tag_id: 100, name: 'news' },
- { id: 2, tag_id: 101, name: 'cast' }
- ]
- })
+ await Tag.create([
+ { id: 1, tag_id: 100, name: 'news' },
+ { id: 2, tag_id: 101, name: 'cast' }
+ ])
- await Taggable.create({
- data: [
- { id: 1, tag_id: 100, taggable_id: 10, taggable_type: 'posts' },
- { id: 2, tag_id: 100, taggable_id: 3, taggable_type: 'videos' },
- { id: 3, tag_id: 101, taggable_id: 10, taggable_type: 'posts' }
- ]
- })
+ await Taggable.create([
+ { id: 1, tag_id: 100, taggable_id: 10, taggable_type: 'posts' },
+ { id: 2, tag_id: 100, taggable_id: 3, taggable_type: 'videos' },
+ { id: 3, tag_id: 101, taggable_id: 10, taggable_type: 'posts' }
+ ])
const post = Post.query()
.with('tags')
@@ -664,17 +632,13 @@ describe('Feature – Relations – Morph To Many – Retrieve', () => {
])
await Post.insert({
- data: {
- id: 1,
- tags: [{ id: 1 }, { id: 3 }, { id: 2 }]
- }
+ id: 1,
+ tags: [{ id: 1 }, { id: 3 }, { id: 2 }]
})
await Video.insert({
- data: {
- id: 1,
- tags: [{ id: 1 }, { id: 3 }, { id: 2 }]
- }
+ id: 1,
+ tags: [{ id: 1 }, { id: 3 }, { id: 2 }]
})
const post = Post.query()
diff --git a/test/feature/relations/MorphedByMany_Persist.spec.ts b/test/feature/relations/MorphedByMany_Persist.spec.ts
index 91c8cf20..7f8a08f8 100644
--- a/test/feature/relations/MorphedByMany_Persist.spec.ts
+++ b/test/feature/relations/MorphedByMany_Persist.spec.ts
@@ -70,12 +70,10 @@ describe('Feature – Relations – Morphed By Many – Persist', () => {
])
await Tag.create({
- data: {
- id: 1,
- name: 'news',
- posts: [{ id: 1, pivot: { public: true } }, { id: 2 }],
- videos: [{ id: 3, pivot: { public: true } }, { id: 4 }]
- }
+ id: 1,
+ name: 'news',
+ posts: [{ id: 1, pivot: { public: true } }, { id: 2 }],
+ videos: [{ id: 3, pivot: { public: true } }, { id: 4 }]
})
expect(store.state.entities.posts.data['1'].id).toBe(1)
@@ -174,22 +172,20 @@ describe('Feature – Relations – Morphed By Many – Persist', () => {
{ model: Taggable }
])
- await Tag.create({
- data: [
- {
- id: 1,
- name: 'news',
- posts: [{ id: 1 }, { id: 2 }],
- videos: [{ id: 3 }, { id: 4 }]
- },
- {
- id: 2,
- name: 'cast',
- posts: [{ id: 2 }, { id: 3 }],
- videos: [{ id: 3 }, { id: 5 }]
- }
- ]
- })
+ await Tag.create([
+ {
+ id: 1,
+ name: 'news',
+ posts: [{ id: 1 }, { id: 2 }],
+ videos: [{ id: 3 }, { id: 4 }]
+ },
+ {
+ id: 2,
+ name: 'cast',
+ posts: [{ id: 2 }, { id: 3 }],
+ videos: [{ id: 3 }, { id: 5 }]
+ }
+ ])
expect(store.state.entities.posts.data['1'].id).toBe(1)
expect(store.state.entities.posts.data['2'].id).toBe(2)
@@ -319,12 +315,10 @@ describe('Feature – Relations – Morphed By Many – Persist', () => {
])
await Tag.create({
- data: {
- id: 1,
- name: 'news',
- posts: [{ id: 1, tag: { public: true } }, { id: 2 }],
- videos: [{ id: 3, tag: { public: true } }, { id: 4 }]
- }
+ id: 1,
+ name: 'news',
+ posts: [{ id: 1, tag: { public: true } }, { id: 2 }],
+ videos: [{ id: 3, tag: { public: true } }, { id: 4 }]
})
expect(store.state.entities.posts.data['1'].id).toBe(1)
diff --git a/test/feature/relations/MorphedByMany_Retrieve.spec.ts b/test/feature/relations/MorphedByMany_Retrieve.spec.ts
index 3bf66a92..613ffcb6 100644
--- a/test/feature/relations/MorphedByMany_Retrieve.spec.ts
+++ b/test/feature/relations/MorphedByMany_Retrieve.spec.ts
@@ -109,53 +109,45 @@ describe('Feature – Relations – Morphed By Many – Retrieve', () => {
{ model: Taggable }
])
- await Post.create({
- data: [{ id: 1 }, { id: 5 }]
- })
+ await Post.create([{ id: 1 }, { id: 5 }])
- await Video.create({
- data: [{ id: 3 }, { id: 4 }, { id: 5 }]
- })
+ await Video.create([{ id: 3 }, { id: 4 }, { id: 5 }])
- await Tag.create({
- data: [
- { id: 1, name: 'news' },
- { id: 3, name: 'without references' }
- ]
- })
+ await Tag.create([
+ { id: 1, name: 'news' },
+ { id: 3, name: 'without references' }
+ ])
- await Taggable.create({
- data: [
- {
- id: 1,
- tag_id: 1,
- taggable_id: 1,
- taggable_type: 'posts',
- is_public: false
- },
- {
- id: 2,
- tag_id: 2,
- taggable_id: 3,
- taggable_type: 'videos',
- is_public: true
- },
- {
- id: 3,
- tag_id: 1,
- taggable_id: 5,
- taggable_type: 'posts',
- is_public: true
- },
- {
- id: 4,
- tag_id: 1,
- taggable_id: 4,
- taggable_type: 'videos',
- is_public: false
- }
- ]
- })
+ await Taggable.create([
+ {
+ id: 1,
+ tag_id: 1,
+ taggable_id: 1,
+ taggable_type: 'posts',
+ is_public: false
+ },
+ {
+ id: 2,
+ tag_id: 2,
+ taggable_id: 3,
+ taggable_type: 'videos',
+ is_public: true
+ },
+ {
+ id: 3,
+ tag_id: 1,
+ taggable_id: 5,
+ taggable_type: 'posts',
+ is_public: true
+ },
+ {
+ id: 4,
+ tag_id: 1,
+ taggable_id: 4,
+ taggable_type: 'videos',
+ is_public: false
+ }
+ ])
const tag = Tag.query()
.with('posts')
@@ -277,26 +269,18 @@ describe('Feature – Relations – Morphed By Many – Retrieve', () => {
{ model: Taggable }
])
- await Post.create({
- data: [{ post_id: 1 }, { post_id: 5 }]
- })
+ await Post.create([{ post_id: 1 }, { post_id: 5 }])
- await Video.create({
- data: [{ id: 3 }, { id: 4 }, { id: 5 }]
- })
+ await Video.create([{ id: 3 }, { id: 4 }, { id: 5 }])
- await Tag.create({
- data: { tag_id: 1, name: 'news' }
- })
+ await Tag.create({ tag_id: 1, name: 'news' })
- await Taggable.create({
- data: [
- { id: 1, tag_id: 1, taggable_id: 1, taggable_type: 'posts' },
- { id: 2, tag_id: 2, taggable_id: 3, taggable_type: 'videos' },
- { id: 3, tag_id: 1, taggable_id: 5, taggable_type: 'posts' },
- { id: 4, tag_id: 1, taggable_id: 4, taggable_type: 'videos' }
- ]
- })
+ await Taggable.create([
+ { id: 1, tag_id: 1, taggable_id: 1, taggable_type: 'posts' },
+ { id: 2, tag_id: 2, taggable_id: 3, taggable_type: 'videos' },
+ { id: 3, tag_id: 1, taggable_id: 5, taggable_type: 'posts' },
+ { id: 4, tag_id: 1, taggable_id: 4, taggable_type: 'videos' }
+ ])
const tag = Tag.query()
.with('posts')
@@ -414,29 +398,21 @@ describe('Feature – Relations – Morphed By Many – Retrieve', () => {
{ model: Taggable }
])
- await Post.create({
- data: [
- { id: 1, post_id: 100 },
- { id: 5, post_id: 105 }
- ]
- })
+ await Post.create([
+ { id: 1, post_id: 100 },
+ { id: 5, post_id: 105 }
+ ])
- await Video.create({
- data: [{ id: 3 }, { id: 4 }, { id: 5 }]
- })
+ await Video.create([{ id: 3 }, { id: 4 }, { id: 5 }])
- await Tag.create({
- data: { id: 1, tag_id: 200, name: 'news' }
- })
+ await Tag.create({ id: 1, tag_id: 200, name: 'news' })
- await Taggable.create({
- data: [
- { id: 1, tag_id: 200, taggable_id: 100, taggable_type: 'posts' },
- { id: 2, tag_id: 2, taggable_id: 3, taggable_type: 'videos' },
- { id: 3, tag_id: 200, taggable_id: 105, taggable_type: 'posts' },
- { id: 4, tag_id: 200, taggable_id: 4, taggable_type: 'videos' }
- ]
- })
+ await Taggable.create([
+ { id: 1, tag_id: 200, taggable_id: 100, taggable_type: 'posts' },
+ { id: 2, tag_id: 2, taggable_id: 3, taggable_type: 'videos' },
+ { id: 3, tag_id: 200, taggable_id: 105, taggable_type: 'posts' },
+ { id: 4, tag_id: 200, taggable_id: 4, taggable_type: 'videos' }
+ ])
const tag = Tag.query()
.with('posts')
@@ -540,11 +516,9 @@ describe('Feature – Relations – Morphed By Many – Retrieve', () => {
])
await Tag.create({
- data: {
- id: 1,
- posts: [{ id: 1 }, { id: 3 }, { id: 2 }],
- videos: [{ id: 1 }, { id: 3 }, { id: 2 }]
- }
+ id: 1,
+ posts: [{ id: 1 }, { id: 3 }, { id: 2 }],
+ videos: [{ id: 1 }, { id: 3 }, { id: 2 }]
})
const tag = Tag.query()
diff --git a/test/feature/relations/Persist_NestedMorph.spec.ts b/test/feature/relations/Persist_NestedMorph.spec.ts
index f531cdc3..4c9b0061 100644
--- a/test/feature/relations/Persist_NestedMorph.spec.ts
+++ b/test/feature/relations/Persist_NestedMorph.spec.ts
@@ -44,19 +44,17 @@ describe('Features – Relations – Persist – Nested Morph', () => {
{ model: Comment }
])
- await store.dispatch('entities/users/create', {
- data: {
- id: 1,
- comments: [
- {
- id: 1,
- user_id: 1,
- commentable_id: 1,
- commentable_type: 'posts',
- commentable: { id: 1 }
- }
- ]
- }
+ await User.create({
+ id: 1,
+ comments: [
+ {
+ id: 1,
+ user_id: 1,
+ commentable_id: 1,
+ commentable_type: 'posts',
+ commentable: { id: 1 }
+ }
+ ]
})
const expected = createState({
diff --git a/test/feature/relations/Retrieve_Constraint.spec.ts b/test/feature/relations/Retrieve_Constraint.spec.ts
index ee7ebed7..6379b717 100644
--- a/test/feature/relations/Retrieve_Constraint.spec.ts
+++ b/test/feature/relations/Retrieve_Constraint.spec.ts
@@ -29,17 +29,13 @@ describe('Feature – Relations – Retrieve – Constraint', () => {
it('can resolve relation constraint', async () => {
const store = createStore([{ model: User }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: [{ id: 1 }, { id: 2 }, { id: 3 }]
- })
-
- await store.dispatch('entities/posts/create', {
- data: [
- { id: 1, user_id: 1 },
- { id: 2, user_id: 1 },
- { id: 3, user_id: 2 }
- ]
- })
+ await User.create([{ id: 1 }, { id: 2 }, { id: 3 }])
+
+ await Post.create([
+ { id: 1, user_id: 1 },
+ { id: 2, user_id: 1 },
+ { id: 3, user_id: 2 }
+ ])
const expected = {
$id: '1',
diff --git a/test/feature/relations/Retrieve_DeepLoad.spec.ts b/test/feature/relations/Retrieve_DeepLoad.spec.ts
index 50739cc3..bb8f4d3d 100644
--- a/test/feature/relations/Retrieve_DeepLoad.spec.ts
+++ b/test/feature/relations/Retrieve_DeepLoad.spec.ts
@@ -48,29 +48,23 @@ describe('Relations – Deep Load', () => {
}
}
- const store = createStore([
- { model: User },
- { model: Post },
- { model: Comment }
- ])
+ createStore([{ model: User }, { model: Post }, { model: Comment }])
- await store.dispatch('entities/posts/create', {
- data: {
- id: 1,
- comments: [
- {
+ await Post.create({
+ id: 1,
+ comments: [
+ {
+ id: 1,
+ user_id: 1,
+ user: {
id: 1,
- user_id: 1,
- user: {
- id: 1,
- name: 'John Doe'
- }
+ name: 'John Doe'
}
- ]
- }
+ }
+ ]
})
- const post = store.getters['entities/posts/query']()
+ const post = Post.query()
.withAll()
.first()
diff --git a/test/feature/relations/Retrieve_Has.spec.ts b/test/feature/relations/Retrieve_Has.spec.ts
index e13e7c24..42567652 100644
--- a/test/feature/relations/Retrieve_Has.spec.ts
+++ b/test/feature/relations/Retrieve_Has.spec.ts
@@ -27,26 +27,22 @@ describe('Feature – Relations – Retrieve – Has', () => {
}
it('can query data depending on relationship existence', async () => {
- const store = createStore([{ model: User }, { model: Post }])
+ createStore([{ model: User }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: [{ id: 1 }, { id: 2 }, { id: 3 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }, { id: 3 }])
- await store.dispatch('entities/posts/create', {
- data: [
- { id: 1, user_id: 1 },
- { id: 2, user_id: 2 },
- { id: 3, user_id: 2 }
- ]
- })
+ await Post.create([
+ { id: 1, user_id: 1 },
+ { id: 2, user_id: 2 },
+ { id: 3, user_id: 2 }
+ ])
const expected = [
{ $id: '1', id: 1, posts: [] },
{ $id: '2', id: 2, posts: [] }
]
- const users = store.getters['entities/users/query']()
+ const users = User.query()
.has('posts')
.get()
@@ -54,23 +50,19 @@ describe('Feature – Relations – Retrieve – Has', () => {
})
it('can query data depending on relationship absence', async () => {
- const store = createStore([{ model: User }, { model: Post }])
+ createStore([{ model: User }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: [{ id: 1 }, { id: 2 }, { id: 3 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }, { id: 3 }])
- await store.dispatch('entities/posts/create', {
- data: [
- { id: 1, user_id: 1 },
- { id: 2, user_id: 2 },
- { id: 3, user_id: 2 }
- ]
- })
+ await Post.create([
+ { id: 1, user_id: 1 },
+ { id: 2, user_id: 2 },
+ { id: 3, user_id: 2 }
+ ])
const expected = [{ $id: '3', id: 3, posts: [] }]
- const users = store.getters['entities/users/query']()
+ const users = User.query()
.hasNot('posts')
.get()
@@ -101,37 +93,31 @@ describe('Feature – Relations – Retrieve – Has', () => {
}
}
- const store = createStore([
- { model: User },
- { model: Phone },
- { model: Post }
- ])
+ createStore([{ model: User }, { model: Phone }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: [{ id: 1 }, { id: 2 }, { id: 3 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }, { id: 3 }])
- await store.dispatch('entities/posts/create', {
- data: [
- { id: 1, user_id: 1 },
- { id: 2, user_id: 2 },
- { id: 3, user_id: 2 }
- ]
- })
+ await Post.create([
+ { id: 1, user_id: 1 },
+ { id: 2, user_id: 2 },
+ { id: 3, user_id: 2 }
+ ])
- await store.dispatch('entities/phones/create', {
- data: [{ id: 1, user_id: 2 }]
- })
+ await Phone.create([{ id: 1, user_id: 2 }])
const expected1 = [{ $id: '1', id: 1, phone: null, posts: [] }]
const expected2 = [{ $id: '2', id: 2, phone: null, posts: [] }]
- const users1 = store.getters['entities/users/query']()
- .whereHas('posts', (query: Query) => query.where('id', 1))
+ const users1 = User.query()
+ .whereHas('posts', (query: Query) => {
+ query.where('id', 1)
+ })
.get()
- const users2 = store.getters['entities/users/query']()
- .whereHas('phone', (query: Query) => query.where('id', 1))
+ const users2 = User.query()
+ .whereHas('phone', (query: Query) => {
+ query.where('id', 1)
+ })
.get()
expect(users1).toEqual(expected1)
@@ -139,50 +125,44 @@ describe('Feature – Relations – Retrieve – Has', () => {
})
it('can query data depending on relationship absence with constraint', async () => {
- const store = createStore([{ model: User }, { model: Post }])
+ createStore([{ model: User }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: [{ id: 1 }, { id: 2 }, { id: 3 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }, { id: 3 }])
- await store.dispatch('entities/posts/create', {
- data: [
- { id: 1, user_id: 1 },
- { id: 2, user_id: 2 },
- { id: 3, user_id: 2 }
- ]
- })
+ await Post.create([
+ { id: 1, user_id: 1 },
+ { id: 2, user_id: 2 },
+ { id: 3, user_id: 2 }
+ ])
const expected = [
{ $id: '2', id: 2, posts: [] },
{ $id: '3', id: 3, posts: [] }
]
- const users = store.getters['entities/users/query']()
- .whereHasNot('posts', (query: Query) => query.where('id', 1))
+ const users = User.query()
+ .whereHasNot('posts', (query: Query) => {
+ query.where('id', 1)
+ })
.get()
expect(users).toEqual(expected)
})
it('can query data depending on relationship existence with number', async () => {
- const store = createStore([{ model: User }, { model: Post }])
+ createStore([{ model: User }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: [{ id: 1 }, { id: 2 }, { id: 3 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }, { id: 3 }])
- await store.dispatch('entities/posts/create', {
- data: [
- { id: 1, user_id: 1 },
- { id: 2, user_id: 2 },
- { id: 3, user_id: 2 }
- ]
- })
+ await Post.create([
+ { id: 1, user_id: 1 },
+ { id: 2, user_id: 2 },
+ { id: 3, user_id: 2 }
+ ])
const expected = [{ $id: '2', id: 2, posts: [] }]
- const users = store.getters['entities/users/query']()
+ const users = User.query()
.has('posts', 2)
.get()
@@ -190,26 +170,22 @@ describe('Feature – Relations – Retrieve – Has', () => {
})
it('can query data depending on relationship absence with number', async () => {
- const store = createStore([{ model: User }, { model: Post }])
+ createStore([{ model: User }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: [{ id: 1 }, { id: 2 }, { id: 3 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }, { id: 3 }])
- await store.dispatch('entities/posts/create', {
- data: [
- { id: 1, user_id: 1 },
- { id: 2, user_id: 2 },
- { id: 3, user_id: 2 }
- ]
- })
+ await Post.create([
+ { id: 1, user_id: 1 },
+ { id: 2, user_id: 2 },
+ { id: 3, user_id: 2 }
+ ])
const expected = [
{ $id: '1', id: 1, posts: [] },
{ $id: '3', id: 3, posts: [] }
]
- const users = store.getters['entities/users/query']()
+ const users = User.query()
.hasNot('posts', 2)
.get()
@@ -240,30 +216,20 @@ describe('Feature – Relations – Retrieve – Has', () => {
}
}
- const store = createStore([
- { model: User },
- { model: Phone },
- { model: Post }
- ])
+ createStore([{ model: User }, { model: Phone }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: [{ id: 1 }, { id: 2 }, { id: 3 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }, { id: 3 }])
- await store.dispatch('entities/posts/create', {
- data: [
- { id: 1, user_id: 1 },
- { id: 2, user_id: 2 },
- { id: 3, user_id: 2 }
- ]
- })
+ await Post.create([
+ { id: 1, user_id: 1 },
+ { id: 2, user_id: 2 },
+ { id: 3, user_id: 2 }
+ ])
- await store.dispatch('entities/phones/create', {
- data: [{ id: 1, user_id: 1 }]
- })
+ await Phone.create([{ id: 1, user_id: 1 }])
expect(
- store.getters['entities/users/query']()
+ User.query()
.has('posts', 1)
.get()
).toEqual([
@@ -271,17 +237,17 @@ describe('Feature – Relations – Retrieve – Has', () => {
{ $id: '2', id: 2, phone: null, posts: [] }
])
expect(
- store.getters['entities/users/query']()
+ User.query()
.has('posts', '=', 1)
.get()
).toEqual([{ $id: '1', id: 1, phone: null, posts: [] }])
expect(
- store.getters['entities/users/query']()
+ User.query()
.has('posts', '>', 1)
.get()
).toEqual([{ $id: '2', id: 2, phone: null, posts: [] }])
expect(
- store.getters['entities/users/query']()
+ User.query()
.has('posts', '>=', 1)
.get()
).toEqual([
@@ -289,12 +255,12 @@ describe('Feature – Relations – Retrieve – Has', () => {
{ $id: '2', id: 2, phone: null, posts: [] }
])
expect(
- store.getters['entities/users/query']()
+ User.query()
.has('posts', '<', 2)
.get()
).toEqual([{ $id: '1', id: 1, phone: null, posts: [] }])
expect(
- store.getters['entities/users/query']()
+ User.query()
.has('posts', '<=', 2)
.get()
).toEqual([
@@ -302,35 +268,31 @@ describe('Feature – Relations – Retrieve – Has', () => {
{ $id: '2', id: 2, phone: null, posts: [] }
])
expect(
- store.getters['entities/users/query']()
+ User.query()
.has('posts', 'unknown', 1)
.get()
).toEqual([{ $id: '1', id: 1, phone: null, posts: [] }])
expect(
- store.getters['entities/users/query']()
+ User.query()
.has('phone', '=', 1)
.get()
).toEqual([{ $id: '1', id: 1, phone: null, posts: [] }])
})
it('can query data depending on relationship absence with string conditions', async () => {
- const store = createStore([{ model: User }, { model: Post }])
+ createStore([{ model: User }, { model: Post }])
- await store.dispatch('entities/users/create', {
- data: [{ id: 1 }, { id: 2 }, { id: 3 }]
- })
+ await User.create([{ id: 1 }, { id: 2 }, { id: 3 }])
- await store.dispatch('entities/posts/create', {
- data: [
- { id: 1, user_id: 1 },
- { id: 2, user_id: 2 },
- { id: 3, user_id: 2 }
- ]
- })
+ await Post.create([
+ { id: 1, user_id: 1 },
+ { id: 2, user_id: 2 },
+ { id: 3, user_id: 2 }
+ ])
expect(
- store.getters['entities/users/query']()
+ User.query()
.hasNot('posts', '>', 1)
.get()
).toEqual([
@@ -338,12 +300,12 @@ describe('Feature – Relations – Retrieve – Has', () => {
{ $id: '3', id: 3, posts: [] }
])
expect(
- store.getters['entities/users/query']()
+ User.query()
.hasNot('posts', '>=', 1)
.get()
).toEqual([{ $id: '3', id: 3, posts: [] }])
expect(
- store.getters['entities/users/query']()
+ User.query()
.hasNot('posts', '<', 2)
.get()
).toEqual([
@@ -351,7 +313,7 @@ describe('Feature – Relations – Retrieve – Has', () => {
{ $id: '3', id: 3, posts: [] }
])
expect(
- store.getters['entities/users/query']()
+ User.query()
.hasNot('posts', '<=', 2)
.get()
).toEqual([{ $id: '3', id: 3, posts: [] }])
diff --git a/test/feature/relations/Retrieve_SelfRelation.spec.ts b/test/feature/relations/Retrieve_SelfRelation.spec.ts
index 96265437..a5ed0e5d 100644
--- a/test/feature/relations/Retrieve_SelfRelation.spec.ts
+++ b/test/feature/relations/Retrieve_SelfRelation.spec.ts
@@ -27,13 +27,8 @@ describe('Feature – Relations – Retrieve – Self Relation', () => {
createStore([{ model: Post }])
- await Post.insert({
- data: { id: 1, parent_id: null }
- })
-
- await Post.insert({
- data: { id: 2, parent_id: 1 }
- })
+ await Post.insert({ id: 1, parent_id: null })
+ await Post.insert({ id: 2, parent_id: 1 })
const post = Post.query()
.with('parent')
diff --git a/test/feature/relations/Retrieve_StringDefine.spec.ts b/test/feature/relations/Retrieve_StringDefine.spec.ts
index abd14d1a..9f810713 100644
--- a/test/feature/relations/Retrieve_StringDefine.spec.ts
+++ b/test/feature/relations/Retrieve_StringDefine.spec.ts
@@ -40,13 +40,9 @@ describe('Feature – Relations - String Define', () => {
createStore([{ model: User }, { model: Post }])
- await User.create({
- data: { id: 1 }
- })
+ await User.create({ id: 1 })
- await Post.create({
- data: { id: 2, user_id: 1 }
- })
+ await Post.create({ id: 2, user_id: 1 })
const user = User.query()
.with('posts')
diff --git a/test/feature/relations/Retrieve_With.spec.ts b/test/feature/relations/Retrieve_With.spec.ts
index 0ed91f83..14c615a9 100644
--- a/test/feature/relations/Retrieve_With.spec.ts
+++ b/test/feature/relations/Retrieve_With.spec.ts
@@ -62,14 +62,12 @@ describe('Feature – Relations – Retrieve – With', () => {
createStore([{ model: User }, { model: Phone }, { model: Post }])
await User.create({
- data: {
- id: 1,
- phone: { id: 2, user_id: 1 },
- posts: [
- { id: 3, user_id: 1 },
- { id: 4, user_id: 1 }
- ]
- }
+ id: 1,
+ phone: { id: 2, user_id: 1 },
+ posts: [
+ { id: 3, user_id: 1 },
+ { id: 4, user_id: 1 }
+ ]
})
const user1 = User.query()
@@ -152,22 +150,20 @@ describe('Feature – Relations – Retrieve – With', () => {
createStore([{ model: User }, { model: Phone }, { model: Post }])
await User.create({
- data: {
- id: 1,
- phone: { id: 2, user_id: 1 },
- posts: [
- {
- id: 3,
- user_id: 1,
- user: { id: 1 }
- },
- {
- id: 4,
- user_id: 1,
- user: { id: 1 }
- }
- ]
- }
+ id: 1,
+ phone: { id: 2, user_id: 1 },
+ posts: [
+ {
+ id: 3,
+ user_id: 1,
+ user: { id: 1 }
+ },
+ {
+ id: 4,
+ user_id: 1,
+ user: { id: 1 }
+ }
+ ]
})
const user1 = User.query()
@@ -247,34 +243,28 @@ describe('Feature – Relations – Retrieve – With', () => {
}
}
- const store = createStore([
- { model: User },
- { model: Post },
- { model: Comment }
- ])
+ createStore([{ model: User }, { model: Post }, { model: Comment }])
- await store.dispatch('entities/users/create', {
- data: {
- id: 1,
- posts: [
- {
- id: 1,
- user_id: 1,
- comments: [
- { id: 1, post_id: 1 },
- { id: 2, post_id: 1 }
- ]
- },
- {
- id: 2,
- user_id: 1,
- comments: [
- { id: 3, post_id: 2 },
- { id: 4, post_id: 2 }
- ]
- }
- ]
- }
+ await User.create({
+ id: 1,
+ posts: [
+ {
+ id: 1,
+ user_id: 1,
+ comments: [
+ { id: 1, post_id: 1 },
+ { id: 2, post_id: 1 }
+ ]
+ },
+ {
+ id: 2,
+ user_id: 1,
+ comments: [
+ { id: 3, post_id: 2 },
+ { id: 4, post_id: 2 }
+ ]
+ }
+ ]
})
const expected = {
@@ -365,32 +355,26 @@ describe('Feature – Relations – Retrieve – With', () => {
}
}
- const store = createStore([
- { model: User },
- { model: Post },
- { model: Comment }
- ])
+ createStore([{ model: User }, { model: Post }, { model: Comment }])
- await store.dispatch('entities/users/create', {
- data: [
- {
- id: 1,
- posts: [
- {
- id: 1,
- user_id: 1,
- comments: [
- { id: 1, post_id: 1 },
- { id: 2, post_id: 1 }
- ]
- }
- ]
- },
- {
- id: 2
- }
- ]
- })
+ await User.create([
+ {
+ id: 1,
+ posts: [
+ {
+ id: 1,
+ user_id: 1,
+ comments: [
+ { id: 1, post_id: 1 },
+ { id: 2, post_id: 1 }
+ ]
+ }
+ ]
+ },
+ {
+ id: 2
+ }
+ ])
const expected = [
{
@@ -415,9 +399,9 @@ describe('Feature – Relations – Retrieve – With', () => {
}
]
- const users = (await store.getters['entities/users/query']()
+ const users = User.query()
.with('posts.comments')
- .findIn([1, 2])) as User[]
+ .findIn([1, 2])
expect(users).toEqual(expected)
})
@@ -499,35 +483,33 @@ describe('Feature – Relations – Retrieve – With', () => {
}
}
- const store = createStore([
+ createStore([
{ model: User },
{ model: Post },
{ model: Comment },
{ model: Like }
])
- await store.dispatch('entities/users/create', {
- data: {
- id: 1,
- posts: [
- {
- id: 1,
- user_id: 1,
- comments: [
- { id: 1, post_id: 1, likes: [{ id: 1, comment_id: 1 }] },
- { id: 2, post_id: 1, likes: [{ id: 2, comment_id: 2 }] }
- ]
- },
- {
- id: 2,
- user_id: 1,
- comments: [
- { id: 3, post_id: 2, likes: [{ id: 3, comment_id: 3 }] },
- { id: 4, post_id: 2, likes: [{ id: 4, comment_id: 4 }] }
- ]
- }
- ]
- }
+ await User.create({
+ id: 1,
+ posts: [
+ {
+ id: 1,
+ user_id: 1,
+ comments: [
+ { id: 1, post_id: 1, likes: [{ id: 1, comment_id: 1 }] },
+ { id: 2, post_id: 1, likes: [{ id: 2, comment_id: 2 }] }
+ ]
+ },
+ {
+ id: 2,
+ user_id: 1,
+ comments: [
+ { id: 3, post_id: 2, likes: [{ id: 3, comment_id: 3 }] },
+ { id: 4, post_id: 2, likes: [{ id: 4, comment_id: 4 }] }
+ ]
+ }
+ ]
})
const expected = {
@@ -575,9 +557,9 @@ describe('Feature – Relations – Retrieve – With', () => {
]
}
- const users = store.getters['entities/users/query']()
+ const users = User.query()
.with('posts.comments.likes')
- .find(1) as User[]
+ .find(1)
expect(users).toEqual(expected)
})
@@ -659,43 +641,41 @@ describe('Feature – Relations – Retrieve – With', () => {
}
}
- const store = createStore([
+ createStore([
{ model: User },
{ model: Post },
{ model: Comment },
{ model: Like }
])
- await store.dispatch('entities/users/create', {
- data: {
- id: 1,
- posts: [
- {
- id: 1,
- user_id: 1,
- comments: [
- { id: 1, post_id: 1 },
- { id: 2, post_id: 1 }
- ],
- likes: [
- { id: 1, post_id: 1 },
- { id: 2, post_id: 1 }
- ]
- },
- {
- id: 2,
- user_id: 1,
- comments: [
- { id: 3, post_id: 2 },
- { id: 4, post_id: 2 }
- ],
- likes: [
- { id: 3, post_id: 2 },
- { id: 4, post_id: 2 }
- ]
- }
- ]
- }
+ await User.create({
+ id: 1,
+ posts: [
+ {
+ id: 1,
+ user_id: 1,
+ comments: [
+ { id: 1, post_id: 1 },
+ { id: 2, post_id: 1 }
+ ],
+ likes: [
+ { id: 1, post_id: 1 },
+ { id: 2, post_id: 1 }
+ ]
+ },
+ {
+ id: 2,
+ user_id: 1,
+ comments: [
+ { id: 3, post_id: 2 },
+ { id: 4, post_id: 2 }
+ ],
+ likes: [
+ { id: 3, post_id: 2 },
+ { id: 4, post_id: 2 }
+ ]
+ }
+ ]
})
const expected = {
@@ -733,10 +713,10 @@ describe('Feature – Relations – Retrieve – With', () => {
const user1 = User.query()
.with('posts.comments|likes')
- .find(1) as User
+ .find(1)
const user2 = User.query()
.with('posts.*')
- .find(1) as User
+ .find(1)
expect(user1).toEqual(expected)
expect(user2).toEqual(expected)
@@ -789,43 +769,41 @@ describe('Feature – Relations – Retrieve – With', () => {
}
}
- const store = createStore([
+ createStore([
{ model: User },
{ model: Post },
{ model: Comment },
{ model: Like }
])
- await store.dispatch('entities/users/create', {
- data: {
- id: 1,
- posts: [
- {
- id: 1,
- user_id: 1,
- comments: [
- { id: 1, post_id: 1 },
- { id: 2, post_id: 1 }
- ],
- likes: [
- { id: 1, post_id: 1 },
- { id: 2, post_id: 1 }
- ]
- },
- {
- id: 2,
- user_id: 1,
- comments: [
- { id: 3, post_id: 2 },
- { id: 4, post_id: 2 }
- ],
- likes: [
- { id: 3, post_id: 2 },
- { id: 4, post_id: 2 }
- ]
- }
- ]
- }
+ await User.create({
+ id: 1,
+ posts: [
+ {
+ id: 1,
+ user_id: 1,
+ comments: [
+ { id: 1, post_id: 1 },
+ { id: 2, post_id: 1 }
+ ],
+ likes: [
+ { id: 1, post_id: 1 },
+ { id: 2, post_id: 1 }
+ ]
+ },
+ {
+ id: 2,
+ user_id: 1,
+ comments: [
+ { id: 3, post_id: 2 },
+ { id: 4, post_id: 2 }
+ ],
+ likes: [
+ { id: 3, post_id: 2 },
+ { id: 4, post_id: 2 }
+ ]
+ }
+ ]
})
const expected = {
@@ -861,10 +839,10 @@ describe('Feature – Relations – Retrieve – With', () => {
]
}
- const user1 = store.getters['entities/users/query']()
+ const user1 = User.query()
.with(['posts.comments', 'posts.likes'])
.find(1)
- const user2 = store.getters['entities/users/query']()
+ const user2 = User.query()
.with(['posts.*'])
.find(1)
@@ -910,10 +888,8 @@ describe('Feature – Relations – Retrieve – With', () => {
createStore([{ model: User }, { model: Post }])
await User.create({
- data: {
- id: 1,
- posts: [{ id: 1, user_id: 1 }]
- }
+ id: 1,
+ posts: [{ id: 1, user_id: 1 }]
})
const user = User.query()
@@ -960,17 +936,9 @@ describe('Feature – Relations – Retrieve – With', () => {
createStore([{ model: User }, { model: Post }])
- await User.create({
- data: {
- id: 1
- }
- })
- await Post.create({
- data: {
- id: 1,
- user_id: null
- }
- })
+ await User.create({ id: 1 })
+
+ await Post.create({ id: 1, user_id: null })
const post = Post.query()
.with('*')
diff --git a/test/feature/relations/Retrieve_With_NestedConstraints.spec.ts b/test/feature/relations/Retrieve_With_NestedConstraints.spec.ts
index 83d8cb7d..9a9a25d6 100644
--- a/test/feature/relations/Retrieve_With_NestedConstraints.spec.ts
+++ b/test/feature/relations/Retrieve_With_NestedConstraints.spec.ts
@@ -67,17 +67,15 @@ describe('Feature – Relations – With – Nested Constraints', () => {
})
it('can apply `with` constraints to nested relations', async () => {
- await User.create({ data: { id: 1 } })
+ await User.create({ id: 1 })
- await Post.create({ data: { id: 1, user_id: 1 } })
+ await Post.create({ id: 1, user_id: 1 })
- await Comment.create({
- data: [
- { id: 1, post_id: 1, title: 'Title01' },
- { id: 2, post_id: 1, title: 'Title01' },
- { id: 3, post_id: 1, title: 'Title02' }
- ]
- })
+ await Comment.create([
+ { id: 1, post_id: 1, title: 'Title01' },
+ { id: 2, post_id: 1, title: 'Title01' },
+ { id: 3, post_id: 1, title: 'Title02' }
+ ])
const user = User.query()
.with('posts.comments', (query) => {
diff --git a/test/feature/repositories/Crud_Delete.spec.ts b/test/feature/repositories/Crud_Delete.spec.ts
new file mode 100644
index 00000000..82d80a08
--- /dev/null
+++ b/test/feature/repositories/Crud_Delete.spec.ts
@@ -0,0 +1,74 @@
+import { createStore, createState } from 'test/support/Helpers'
+import { Model } from '@/index'
+
+describe('Feature - Repositories - CRUD Delete', () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null)
+ }
+ }
+
+ id!: any
+ }
+
+ it('can delete a record by specifying the id', async () => {
+ const store = createStore([User])
+
+ const user = store.$repo(User)
+
+ await user.insert({
+ data: [{ id: 1 }, { id: 2 }]
+ })
+
+ await user.delete(1)
+
+ const expected = createState({
+ users: {
+ 2: { $id: '2', id: 2 }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can delete records by specifying a closure', async () => {
+ const store = createStore([User])
+
+ const user = store.$repo(User)
+
+ await user.insert({
+ data: [{ id: 1 }, { id: 2 }]
+ })
+
+ await user.delete((user) => user.id === 2)
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1 }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+
+ it('can delete all records in the entity', async () => {
+ const store = createStore([User])
+
+ const user = store.$repo(User)
+
+ await user.insert({
+ data: [{ id: 1 }, { id: 2 }]
+ })
+
+ await user.deleteAll()
+
+ const expected = createState({
+ users: {}
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+})
diff --git a/test/feature/repositories/Crud_InsertOrUpdate.spec.ts b/test/feature/repositories/Crud_InsertOrUpdate.spec.ts
new file mode 100644
index 00000000..5d9caabd
--- /dev/null
+++ b/test/feature/repositories/Crud_InsertOrUpdate.spec.ts
@@ -0,0 +1,39 @@
+import { createStore, createState } from 'test/support/Helpers'
+import { Model } from '@/index'
+
+describe('Feature - Repositories - CRUD Insert or Update', () => {
+ it('can insert or update given record to the store', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ name: this.string('')
+ }
+ }
+ }
+
+ const store = createStore([User])
+
+ await store.$repo(User).insert({
+ data: { id: 1, name: 'John Doe' }
+ })
+
+ await store.$repo(User).insertOrUpdate({
+ data: [
+ { id: 1, name: 'Jane Doe' },
+ { id: 2, name: 'Johnny Doe' }
+ ]
+ })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'Jane Doe' },
+ 2: { $id: '2', id: 2, name: 'Johnny Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+})
diff --git a/test/feature/repositories/Crud_Retrieve.spec.ts b/test/feature/repositories/Crud_Retrieve.spec.ts
new file mode 100644
index 00000000..8aead697
--- /dev/null
+++ b/test/feature/repositories/Crud_Retrieve.spec.ts
@@ -0,0 +1,195 @@
+import { createStore } from 'test/support/Helpers'
+import { Model, Query } from '@/index'
+
+describe('Feature - Repositories - CRUD Retrieve', () => {
+ it('can get a new query instance', () => {
+ class User extends Model {
+ static entity = 'users'
+ }
+
+ const store = createStore([User])
+
+ const query = store.$repo(User).query()
+
+ expect(query).toBeInstanceOf(Query)
+ })
+
+ it('can get all records', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null)
+ }
+ }
+
+ id!: number
+ }
+
+ const store = createStore([User])
+
+ const userRepo = store.$repo(User)
+
+ await userRepo.insert({
+ data: [{ id: 1 }, { id: 2 }]
+ })
+
+ const users = userRepo.all()
+
+ expect(users.length).toBe(2)
+ expect(users[0].id).toBe(1)
+ expect(users[1].id).toBe(2)
+ })
+
+ it('can find a single record', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null)
+ }
+ }
+
+ id!: number
+ }
+
+ const store = createStore([User])
+
+ const userRepo = store.$repo(User)
+
+ await userRepo.insert({
+ data: [{ id: 1 }, { id: 2 }]
+ })
+
+ const user = userRepo.find(2) as User
+
+ expect(user.id).toBe(2)
+ })
+
+ it('can find a single record with composite primary key', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static primaryKey = ['idA', 'idB']
+
+ static fields() {
+ return {
+ idA: this.attr(null),
+ idB: this.attr(null)
+ }
+ }
+
+ idA!: number
+ idB!: number
+ }
+
+ const store = createStore([User])
+
+ const userRepo = store.$repo(User)
+
+ await userRepo.insert({
+ data: [
+ { idA: 1, idB: 2 },
+ { idA: 3, idB: 4 }
+ ]
+ })
+
+ const user = userRepo.find([1, 2]) as User
+
+ expect(user.idA).toBe(1)
+ expect(user.idB).toBe(2)
+ })
+
+ it('can find records by ids', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null)
+ }
+ }
+
+ id!: number
+ }
+
+ const store = createStore([User])
+
+ const userRepo = store.$repo(User)
+
+ await userRepo.insert({
+ data: [{ id: 1 }, { id: 2 }, { id: 3 }]
+ })
+
+ const users = userRepo.findIn([1, 3])
+
+ expect(users[0].id).toBe(1)
+ expect(users[1].id).toBe(3)
+ })
+
+ it('can find records by ids with composite primary key', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static primaryKey = ['idA', 'idB']
+
+ static fields() {
+ return {
+ idA: this.attr(null),
+ idB: this.attr(null)
+ }
+ }
+
+ idA!: number
+ idB!: number
+ }
+
+ const store = createStore([User])
+
+ const userRepo = store.$repo(User)
+
+ await userRepo.insert({
+ data: [
+ { idA: 1, idB: 2 },
+ { idA: 3, idB: 4 },
+ { idA: 5, idB: 6 }
+ ]
+ })
+
+ const users = userRepo.findIn([
+ [1, 2],
+ [5, 6]
+ ])
+
+ expect(users[0].idA).toBe(1)
+ expect(users[0].idB).toBe(2)
+ expect(users[1].idA).toBe(5)
+ expect(users[1].idB).toBe(6)
+ })
+
+ it('can check whether record exists', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null)
+ }
+ }
+ }
+
+ const store = createStore([User])
+
+ const userRepo = store.$repo(User)
+
+ expect(userRepo.exists()).toBe(false)
+
+ await userRepo.insert({
+ data: { id: 1 }
+ })
+
+ expect(userRepo.exists()).toBe(true)
+ })
+})
diff --git a/test/feature/repositories/Crud_Update.spec.ts b/test/feature/repositories/Crud_Update.spec.ts
new file mode 100644
index 00000000..5a7d9e86
--- /dev/null
+++ b/test/feature/repositories/Crud_Update.spec.ts
@@ -0,0 +1,35 @@
+import { createStore, createState } from 'test/support/Helpers'
+import { Model } from '@/index'
+
+describe('Feature - Repositories - CRUD Update', () => {
+ it('can update record by including primary key in the data', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ name: this.string('')
+ }
+ }
+ }
+
+ const store = createStore([User])
+
+ const user = store.$repo(User)
+
+ await user.insert({
+ data: { id: 1, name: 'John Doe' }
+ })
+
+ await user.update({ id: 1, name: 'Jane Doe' })
+
+ const expected = createState({
+ users: {
+ 1: { $id: '1', id: 1, name: 'Jane Doe' }
+ }
+ })
+
+ expect(store.state.entities).toEqual(expected)
+ })
+})
diff --git a/test/feature/repositories/Repository.spec.ts b/test/feature/repositories/Repository.spec.ts
new file mode 100644
index 00000000..ca500171
--- /dev/null
+++ b/test/feature/repositories/Repository.spec.ts
@@ -0,0 +1,100 @@
+import { createStore } from 'test/support/Helpers'
+import Repository from '@/repository/Repository'
+import Model from '@/model/Model'
+
+describe('Feature - Repository', () => {
+ it('can retrieve a new repository instance from the store', () => {
+ class User extends Model {
+ static entity = 'users'
+ }
+
+ const store = createStore([User])
+
+ const user = store.$repo(User)
+
+ expect(user).toBeInstanceOf(Repository)
+ })
+
+ it('can instantiate a new model instance without a record', () => {
+ class User extends Model {
+ static entity = 'users'
+ }
+
+ const store = createStore([User])
+
+ const userRepo = store.$repo(User)
+
+ expect(userRepo.make()).toBeInstanceOf(User)
+ })
+
+ it('can instantiate a new model instance with a record', () => {
+ class User extends Model {
+ static entity = 'users'
+
+ // @Attribute('')
+ name!: string
+
+ // @Attribute('')
+ email!: string
+
+ static fields() {
+ return {
+ id: this.attr(''),
+ name: this.attr('')
+ }
+ }
+ }
+
+ const store = createStore([User])
+
+ const userRepo = store.$repo(User)
+
+ const user = userRepo.make({ name: 'John Doe' })
+
+ expect(user).toBeInstanceOf(User)
+ expect(user.name).toBe('John Doe')
+ })
+
+ it('injects store instance to the instantiated model.', () => {
+ class User extends Model {
+ static entity = 'users'
+ }
+
+ const store = createStore([User])
+
+ const user = store.$repo(User).make()
+
+ expect(user.$store()).toBe(store)
+ })
+
+ it('can retrieve a custom repository', () => {
+ class User extends Model {
+ static entity = 'users'
+ }
+
+ class UserRepository extends Repository {
+ model = User
+ foo = 'bar'
+ }
+
+ const store = createStore([User])
+
+ const userRepo = store.$repo(UserRepository)
+
+ expect(userRepo.foo).toBe('bar')
+ })
+
+ it('throws if the custom repository has no assigned model', () => {
+ class User extends Model {
+ static entity = 'users'
+ }
+
+ class UserRepository extends Repository {
+ foo = 'bar'
+ }
+
+ const store = createStore([User])
+
+ expect(() => store.$repo(UserRepository)).toThrow()
+ })
+})
diff --git a/test/performance/Retrieve_HasOneThrough.spec.ts b/test/performance/Retrieve_HasOneThrough.spec.ts
new file mode 100644
index 00000000..22879cad
--- /dev/null
+++ b/test/performance/Retrieve_HasOneThrough.spec.ts
@@ -0,0 +1,76 @@
+import { createStore } from 'test/support/Helpers'
+import Model from '@/model/Model'
+
+describe('Performance – Retrieve – Has One Through', () => {
+ it('should retrieve relation in time', async () => {
+ class User extends Model {
+ static entity = 'users'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ image: this.hasOneThrough(Image, Profile, 'user_id', 'profile_id')
+ }
+ }
+ }
+
+ class Profile extends Model {
+ static entity = 'profiles'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ user_id: this.attr(null)
+ }
+ }
+ }
+
+ class Image extends Model {
+ static entity = 'images'
+
+ static fields() {
+ return {
+ id: this.attr(null),
+ profile_id: this.attr(null)
+ }
+ }
+ }
+
+ const users = []
+ const profiles = []
+ const images = []
+
+ for (let i = 1; i <= 1000; i++) {
+ users.push({ id: i })
+ }
+
+ for (let i = 1; i <= 1000; i++) {
+ profiles.push({ id: i, user_id: i })
+ }
+
+ for (let i = 1; i <= 1000; i++) {
+ images.push({ id: i, profile_id: i })
+ }
+
+ const store = createStore([
+ { model: User },
+ { model: Profile },
+ { model: Image }
+ ])
+
+ await store.dispatch('entities/users/create', { data: users })
+ await store.dispatch('entities/profiles/create', { data: profiles })
+ await store.dispatch('entities/images/create', { data: images })
+
+ const start = +new Date()
+
+ store.getters['entities/users/query']()
+ .with('image')
+ .get()
+
+ const end = +new Date()
+
+ expect(end - start).toBeLessThan(300)
+ console.info('\x1b[2m%s\x1b[0m', ` -- The test took ${end - start}ms`)
+ })
+})
diff --git a/test/support/Helpers.ts b/test/support/Helpers.ts
index 39550e4a..c0995207 100644
--- a/test/support/Helpers.ts
+++ b/test/support/Helpers.ts
@@ -2,6 +2,7 @@ import Vue from 'vue'
import Vuex, { Store, Module } from 'vuex'
import VuexORM from '@/index'
import { mapValues } from '@/support/Utils'
+import { Record, Collection } from '@/data'
import Database from '@/database/Database'
import Model from '@/model/Model'
import State from '@/modules/contracts/State'
@@ -51,13 +52,24 @@ export function createStoreFromDatabase(database: Database): Store {
})
}
+/**
+ * Fill store data with the given state.
+ */
+export function fillState(store: Store, state: Record): void {
+ for (const entity in state) {
+ store.commit('entities/$mutate', {
+ entity,
+ callback(s: State) {
+ s.data = state[entity]
+ }
+ })
+ }
+}
+
/**
* Create a new entity state.
*/
-export function createState(
- state: Record,
- namespace: string = 'entities'
-) {
+export function createState(state: Record, namespace: string = 'entities') {
return {
$name: namespace,
...mapValues(state, (data, name) => ({
@@ -68,8 +80,18 @@ export function createState(
}
}
+/**
+ * Serialize given collection by invoking `$getAttributes` on each model
+ * in the given collection.
+ */
+export function serializeCollection(collection: Collection): Record[] {
+ return collection.map((m) => m.$getAttributes())
+}
+
export default {
createStore,
createStoreFromDatabase,
- createState
+ fillState,
+ createState,
+ serializeCollection
}
diff --git a/test/unit/VuexORM.spec.ts b/test/unit/VuexORM.spec.ts
new file mode 100644
index 00000000..9df31009
--- /dev/null
+++ b/test/unit/VuexORM.spec.ts
@@ -0,0 +1,76 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import VuexORM from '@/index'
+import Database from '@/database/Database'
+import Model from '@/model/Model'
+
+describe('unit/VuexORM', () => {
+ Vue.use(Vuex)
+
+ it('installs Vuex ORM to the store', () => {
+ class User extends Model {
+ static entity = 'users'
+ }
+ class Post extends Model {
+ static entity = 'posts'
+ }
+
+ const database = new Database()
+
+ database.register(User)
+ database.register(Post)
+
+ const store = new Vuex.Store({
+ plugins: [VuexORM.install(database)]
+ })
+
+ const expected = {
+ entities: {
+ $name: 'entities',
+ users: {
+ $name: 'users',
+ $connection: 'entities',
+ data: {}
+ },
+ posts: {
+ $name: 'posts',
+ $connection: 'entities',
+ data: {}
+ }
+ }
+ }
+
+ expect(store.state).toEqual(expected)
+ })
+
+ it('can customize the namespace', () => {
+ class User extends Model {
+ static entity = 'users'
+ }
+
+ const database = new Database()
+
+ database.register(User)
+
+ const store = new Vuex.Store({
+ plugins: [
+ VuexORM.install(database, {
+ namespace: 'database'
+ })
+ ]
+ })
+
+ const expected = {
+ database: {
+ $name: 'database',
+ users: {
+ $name: 'users',
+ $connection: 'database',
+ data: {}
+ }
+ }
+ }
+
+ expect(store.state).toEqual(expected)
+ })
+})
diff --git a/test/unit/database/Database.spec.ts b/test/unit/database/Database.spec.ts
index ee8e3896..f2d79514 100644
--- a/test/unit/database/Database.spec.ts
+++ b/test/unit/database/Database.spec.ts
@@ -3,42 +3,6 @@ import Database from '@/database/Database'
import Model from '@/model/Model'
describe('Unit – Database', () => {
- it('registers itself to the store instance', () => {
- const database = new Database()
-
- const store = createStoreFromDatabase(database)
-
- expect(store.$db()).toBe(database)
- })
-
- it('binds the store instance to the registered models.', () => {
- class User extends Model {
- static entity = 'users'
- }
-
- const database1 = new Database()
- const database2 = new Database()
-
- database1.register(User)
- database2.register(User)
-
- const store1 = createStoreFromDatabase(database1)
- const store2 = createStoreFromDatabase(database2)
-
- expect(
- store1
- .$db()
- .model('users')
- .database()
- ).toBe(database1)
- expect(
- store2
- .$db()
- .model('users')
- .database()
- ).toBe(database2)
- })
-
it('can fetch all models', () => {
class User extends Model {
static entity = 'users'
@@ -54,8 +18,8 @@ describe('Unit – Database', () => {
const models = database.models()
- expect(models.users.prototype).toBeInstanceOf(User)
- expect(models.posts.prototype).toBeInstanceOf(Post)
+ expect(models.users).toBe(User)
+ expect(models.posts).toBe(Post)
})
it('can fetch a model by string', () => {
@@ -73,7 +37,7 @@ describe('Unit – Database', () => {
const model = database.model('users')
- expect(model.prototype).toBeInstanceOf(User)
+ expect(model).toBe(User)
})
it('can fetch a model by type', () => {
@@ -91,7 +55,7 @@ describe('Unit – Database', () => {
const model = database.model(User)
- expect(model.prototype).toBeInstanceOf(User)
+ expect(model).toBe(User)
})
it('throws when a model is not found', () => {
@@ -130,9 +94,9 @@ describe('Unit – Database', () => {
const models = database.baseModels()
- expect(models.users.prototype).toBeInstanceOf(User)
- expect(models.guests.prototype).toBeInstanceOf(User)
- expect(models.admins.prototype).toBeInstanceOf(User)
+ expect(models.users).toBe(User)
+ expect(models.guests).toBe(User)
+ expect(models.admins).toBe(User)
})
it('can fetch a base model by string', () => {
@@ -154,7 +118,7 @@ describe('Unit – Database', () => {
const model = database.baseModel('guests')
- expect(model.prototype).toBeInstanceOf(User)
+ expect(model).toBe(User)
})
it('can fetch a base model by type', () => {
@@ -176,7 +140,7 @@ describe('Unit – Database', () => {
const model = database.baseModel(Guest)
- expect(model.prototype).toBeInstanceOf(User)
+ expect(model).toBe(User)
})
it('throws when a base model is not found', () => {
@@ -255,4 +219,46 @@ describe('Unit – Database', () => {
expect(() => database.module('posts')).toThrowError('[Vuex ORM]')
})
+
+ it('binds the store instance to the registered models.', () => {
+ jest.spyOn(global.console, 'warn').mockImplementation()
+
+ class User extends Model {
+ static entity = 'users'
+ }
+
+ const database1 = new Database()
+ const database2 = new Database()
+
+ database1.register(User)
+ database2.register(User)
+
+ const store1 = createStoreFromDatabase(database1)
+ const store2 = createStoreFromDatabase(database2)
+
+ expect(
+ store1
+ .$db()
+ .model('users')
+ .database()
+ ).toBe(database1)
+ expect(
+ store2
+ .$db()
+ .model('users')
+ .database()
+ ).toBe(database2)
+ })
+
+ it('warns when using the deprecated `$db` method', () => {
+ const warn = jest.spyOn(global.console, 'warn').mockImplementation()
+
+ const database = new Database()
+
+ const store = createStoreFromDatabase(database)
+
+ store.$db()
+
+ expect(warn).toBeCalled()
+ })
})
diff --git a/test/unit/query/Query.spec.ts b/test/unit/query/Query.spec.ts
index d5e4dfd3..9f61c7f8 100644
--- a/test/unit/query/Query.spec.ts
+++ b/test/unit/query/Query.spec.ts
@@ -16,8 +16,8 @@ describe('Unit – Query', () => {
const models = new Query(store, 'users').getModels()
- expect(models.users.prototype).toBeInstanceOf(User)
- expect(models.posts.prototype).toBeInstanceOf(Post)
+ expect(models.users).toBe(User)
+ expect(models.posts).toBe(Post)
})
it('can retrieve a specific model from the database instance', () => {
@@ -25,7 +25,7 @@ describe('Unit – Query', () => {
const model = new Query(store, 'users').getModel('posts')
- expect(model.prototype).toBeInstanceOf(Post)
+ expect(model).toBe(Post)
})
it('retrieves the model corresponds to the entity of Query if the name is not specified', () => {
@@ -33,6 +33,6 @@ describe('Unit – Query', () => {
const model = new Query(store, 'users').getModel()
- expect(model.prototype).toBeInstanceOf(User)
+ expect(model).toBe(User)
})
})
diff --git a/test/unit/support/Utils.spec.ts b/test/unit/support/Utils.spec.ts
index 20b01811..ee9121ee 100644
--- a/test/unit/support/Utils.spec.ts
+++ b/test/unit/support/Utils.spec.ts
@@ -102,82 +102,37 @@ describe('Unit - Utils', () => {
})
describe('#cloneDeep', () => {
- it('should clone arrays', async () => {
- const a = [{ a: 0 }, { b: 1 }]
-
- expect(Utils.cloneDeep(a)).toEqual(a)
-
- const b = [1, 2, 3]
-
- expect(b).toEqual(b)
-
- const c = [{ a: 0 }, { b: 1 }]
- const d = Utils.cloneDeep(c)
-
- expect(d).toEqual(c)
- expect(d[0]).toEqual(c[0])
-
- const e = [0, 'a', {}, [{}]]
-
- expect(Utils.cloneDeep(e)).toEqual(e)
- })
-
- it('should deeply clone an array', async () => {
- const a = [[{ a: 'b' }], [{ a: 'b' }]]
- const b = Utils.cloneDeep(a)
-
- expect(b).not.toBe(a)
- expect(b[0]).not.toBe(a[0])
- expect(b[1]).not.toBe(a[1])
- expect(b).toEqual(a)
- })
-
- it('should deeply clone arrays by reference', async () => {
- const a: any = { a: 'b' }
- const b = [a]
- const c = Utils.cloneDeep(b)
-
- a.c = 'd'
-
- expect(c).not.toEqual(b)
- })
-
- it('should clone objects', () => {
- const a = { a: 1, b: 2, c: 3 }
-
- expect(Utils.cloneDeep(a)).toEqual(a)
- expect(Utils.cloneDeep(a)).not.toBe(a)
- })
-
- it('should deeply clone objects', async () => {
- const a = {
- a: { a: 1, b: 2, c: 3 },
- b: { a: 1, b: 2, c: 3 },
- c: { a: 1, b: 2, c: 3 }
+ it('can create a deep clone of an object', () => {
+ const data = {
+ id: 1,
+ nested: [
+ {
+ id: 1,
+ deep: [
+ {
+ id: 1,
+ deeper: {
+ id: 2
+ }
+ },
+ {
+ id: 2,
+ deeper: {
+ id: 3
+ }
+ }
+ ]
+ }
+ ]
}
- expect(Utils.cloneDeep(a)).toEqual(a)
- expect(Utils.cloneDeep(a)).not.toBe(a)
- expect(Utils.cloneDeep(a).a).toEqual(a.a)
- expect(Utils.cloneDeep(a).a).not.toBe(a.a)
- expect(Utils.cloneDeep(a).b).toEqual(a.b)
- expect(Utils.cloneDeep(a).b).not.toBe(a.b)
- expect(Utils.cloneDeep(a).c).toEqual(a.c)
- expect(Utils.cloneDeep(a).c).not.toBe(a.c)
- })
+ const clone = Utils.cloneDeep(data)
- it('should deeply clone objects by reference', async () => {
- const a: Record = { a: 'b' }
- const b = Utils.cloneDeep(a)
+ expect(clone).toStrictEqual(data)
- b.c = 'd'
-
- expect(b).not.toEqual(a)
- })
+ delete data.nested[0].deep[0].deeper.id
- it('should return primitives', async () => {
- expect(Utils.cloneDeep('foo')).toEqual('foo')
- expect(Utils.cloneDeep(0)).toEqual(0)
+ expect(clone).not.toStrictEqual(data)
})
})
})