# Inserting & Updating
You may insert new data or update existing data through various Model methods. All data created through Vuex ORM gets persisted to Vuex Store.
# Inserts
To create new data, you can use the insert
, create
, and new
methods. They all insert new records to the Vuex Store but behave in a slightly different manner.
The insert
method will simply insert new records. You should pass an object containing records in data
key to the method.
<script>
import User from '@/models/User'
export default {
created () {
User.insert({
data: { id: 1, name: 'John' }
})
}
}
</script>
You may also pass an array of records.
User.insert({
data: [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
{ id: 3, name: 'Johnny' }
]
})
After the insert, inside Vuex Store would look something like this.
{
entities: {
users: {
data: {
1: { id: 1, name: 'John' },
2: { id: 2, name: 'Jane' },
3: { id: 3, name: 'Johnny' }
}
}
}
}
The create
method would work almost identical to insert
. The argument is also the same.
User.create({
data: { id: 1, name: 'John' }
})
The difference between the insert
and create
method is whether to keep existing data or not. The create
method is going to replace all existing data in the store and replace with the given data, while insert
will create new data and leave existing data as is.
// Let's say this is the initial State.
{
entities: {
users: {
data: {
1: { id: 1, name: 'John' }
}
}
}
}
// `insert` is going to insert a new record, and keep existing data.
User.insert({
data: { id: 2, name: 'Jane' }
})
// State after `insert`.
{
entities: {
users: {
data: {
1: { id: 1, name: 'John' },
2: { id: 2, name: 'Jane' }
}
}
}
}
// `create` is going to replace all existing data with new data.
User.create({
data: { id: 3, name: 'Johnny' }
})
// State after `create`.
{
entities: {
users: {
data: {
3: { id: 3, name: 'Johnny' }
}
}
}
}
When performing insert
, if there is existing data with the same ID, the record will get overwritten.
// Initial State.
{
entities: {
users: {
data: {
1: { id: 1, name: 'John' }
}
}
}
}
// `insert` is going to overwrite the record with the same ID.
User.insert({
data: { id: 1, name: 'Jane' }
})
// State after `insert`.
{
entities: {
users: {
data: {
1: { id: 1, name: 'Jane' }
}
}
}
}
Finally, with the new
method, you can create a new record with all fields filled by default values. Let's say you have the following Model defined.
class User extends Model {
static entity = 'users'
static fields () {
return {
id: this.uid(),
name: this.string('Default Name')
}
}
}
By calling new
method on the User Model would create a new record of:
// Create new data with default values.
User.new()
// State after `new`
{
entities: {
users: {
data: {
1: { id: 1, name: 'Default Name' }
}
}
}
}
# Default Values
If you pass an object that has missing fields defined in the model, that field will be automatically generated with the default value defined in the model.
// When you have this model.
class User extends Model {
static entity = 'users'
static fields () {
return {
id: this.attr(null),
name: this.attr(''),
active: this.attr(false)
}
}
}
// If you insert object that misses some fields...
User.create({
data: { id: 1, name: 'John Doe ' }
})
// Those missing fields will be present with its default value.
{
entities: {
users: {
data: {
1: {
id: 1,
name: 'John Doe',
active: false
}
}
}
}
}
Note that records should always be created with the primary key value provided. Leaving the primary key empty (''
, undefined
or null
) will result in index keys _no_key_X
and a high probability of inconsistent behavior. This also means that a primary key of type uid
is recommended when using the new
method. See here to learn more about primary key and index key.
# Inserting Relationships
If you pass data with relationships to the insert
or create
method, those relationships will be normalized and inserted to the store.
For "Single Relationship", such as "Has One" and "Belongs To", the data should contain an object to its relational field. Let's say you have the following Model definition, where Post Model "Belongs To" User Model.
class User extends Model {
static entity = 'users'
static fields () {
return {
id: this.attr(null),
name: this.attr('')
}
}
}
class Post extends Model {
static entity = 'posts'
static fields () {
return {
id: this.attr(null),
user_id: this.attr(null),
title: this.attr(''),
author: this.belongsTo(User, 'user_id')
}
}
}
You may insert Post data with its related User like below example.
// Create Post data with its related User.
Post.insert({
data: {
id: 1,
user_id: 1,
title: 'Post title.',
author: {
id: 1,
name: 'John Doe '
}
}
})
// State after `insert`.
{
entities: {
posts: {
data: {
1: { id: 1, user_id: 1, title: 'Post title' }
}
},
users: {
data: {
1: { id: 1, name: 'John Doe' }
}
}
}
}
For "Many Relationship", for example, "Has Many", the data should contain an array of records to its relational field. Let's say this time you have the following Model definition, where User Model "Has Many" Post Model.
class User extends Model {
static entity = 'users'
static fields () {
return {
id: this.attr(null),
name: this.attr(''),
posts: this.hasMany(Post, 'user_id')
}
}
}
class Post extends Model {
static entity = 'posts'
static fields () {
return {
id: this.attr(null),
user_id: this.attr(null),
title: this.attr('')
}
}
}
Now you may insert User data with many Post data as same as "Single Relationship".
// Create User data with its related Post data.
User.insert({
data: {
id: 1,
name: 'John Doe ',
posts: [
{ id: 1, user_id: 1, title: 'Post title 1' },
{ id: 2, user_id: 1, title: 'Post title 2' }
]
}
})
// State after `insert`.
{
entities: {
posts: {
data: {
1: { id: 1, user_id: 1, title: 'Post title 1' }
2: { id: 2, user_id: 1, title: 'Post title 2' }
}
},
users: {
data: {
1: { id: 1, name: 'John Doe' }
}
}
}
}
The insertion method works for all relationship types including complex ones such as "Belongs To Many" or "Morph Many". Because in the end, all relationships are either "Single" or "Many" from the data tree structure point of view. Vuex ORM will try its best to normalize the data and store each relationship separately into the store.
Though there are few things that you should know about when inserting relationship in a slightly complex manner.
# Inserting Many To Many Relationships
Since 0.36.0+, when inserting many-to-many relationships such as belongsToMany
or morphToMany
, any data nested under the pivot
attribute will be inserted into intermediate models.
Let's take a look at an example. Here we have User
belonging to many Role
through RoleUser
.
class User extends Model {
static entity = 'users'
static fields () {
return {
id: this.attr(null),
roles: this.belongsToMany(Role, RoleUser, 'user_id', 'role_id')
}
}
}
class Role extends Model {
static entity = 'roles'
static fields () {
return {
id: this.attr(null)
}
}
}
class RoleUser extends Model {
static entity = 'role_user'
static primaryKey = ['role_id', 'user_id']
static fields () {
return {
id: this.attr(null),
role_id: this.attr(null),
user_id: this.attr(null),
level: this.number(1)
}
}
}
Having these structures, you may include intermediate data (RoleUser
data) under pivot
attribute.
// Create user data with nested roles and its intermediate data.
User.insert({
data: {
id: 1,
roles: [
{
id: 1,
pivot: { id: 1, level: 2 }
},
{
id: 2,
pivot: { id: 2, level: 3 }
}
]
}
})
// State after `insert`.
{
entities: {
users: {
1: { id: 1 }
},
roles: {
1: { id: 1 },
2: { id: 2 }
},
role_user: {
1: { id: 1, user_id: 1, role_id: 1, level: 2 },
2: { id: 2, user_id: 1, role_id: 2, level: 3 }
}
}
}
Note that you may customize the name of pivot
attributes by using the as
method when defining the relationship.
class User extends Model {
static entity = 'users'
static fields () {
return {
id: this.attr(null),
roles: this.belongsToMany(
Role,
RoleUser,
'user_id',
'role_id'
).as('permission')
}
}
}
In this case, you must pass intermediate data to the key that matches the customized name.
User.insert({
data: {
id: 1,
roles: [{
id: 1,
permission: { id: 1, level: 2 }
}]
}
})
# Generating Missing Foreign Keys
If data is missing its foreign keys, Vuex ORM will automatically generate them during the inserting process. For example, let's say you have the following Model definition.
class User extends Model {
static entity = 'users'
static fields () {
return {
id: this.attr(null),
name: this.attr(''),
posts: this.hasMany(Post, 'user_id')
}
}
}
class Post extends Model {
static entity = 'posts'
static fields () {
return {
id: this.attr(null),
user_id: this.attr(null),
title: this.attr('')
}
}
}
And if you try to insert User with Post without user_id
key, Vuex ORM will generate user_id
value automatically.
// Create User data with its related Post data with `user_id` missing.
User.insert({
data: {
id: 1,
name: 'John Doe ',
posts: [
{ id: 1, title: 'Post title 1' },
{ id: 2, title: 'Post title 2' }
]
}
})
// State after `insert`. See `user_id` is automatically generated.
{
entities: {
posts: {
data: {
1: { id: 1, user_id: 1, title: 'Post title 1' }
2: { id: 2, user_id: 1, title: 'Post title 2' }
}
},
users: {
data: {
1: { id: 1, name: 'John Doe' }
}
}
}
}
# Generating Pivot Records
For relationships that require a "Pivot Entity" such as "Belongs To Many", Vuex ORM will create missing pivot records as well. Again, let's say you have the following Model definition.
class User extends Model {
static entity = 'users'
static fields () {
return {
id: this.attr(null),
name: this.attr(''),
roles: this.belongsToMany(Role, RoleUser, 'user_id', 'role_id')
}
}
}
class Role extends Model {
static entity = 'roles'
static fields () {
return {
id: this.attr(null),
name: this.string('')
}
}
}
class RoleUser extends Model {
static entity = 'role_user'
static primaryKey = ['role_id', 'user_id']
static fields () {
return {
role_id: this.attr(null),
user_id: this.attr(null)
}
}
}
When inserting data, you insert User data and its related Role data but without RoleUser data. Still, in this case, Vuex ORM will generate required intermediate pivot records.
// Insert User data with Role data.
User.insert({
data: {
id: 1,
name: 'John Doe',
roles: [
{ id: 1, name: 'admin' },
{ id: 2, name: 'operator' }
]
}
})
// State after `insert`. See there's a intermediate `role_user` records.
{
entities: {
users: {
1: { id: 1, name: 'John Doe' }
},
roles: {
1: { id: 1, name: 'admin' },
2: { id: 2, name: 'operator' }
},
role_user: {
'[1,1]': { role_id: 1, user_id: 1 },
'[2,1]': { role_id: 2, user_id: 1 }
}
}
}
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.
class Country extends Model {
static entity = 'countries'
static fields () {
return {
id: this.attr(null),
users: this.hasMany(User, 'country_id'),
posts: this.hasManyThrough(Post, User, 'country_id', 'user_id')
}
}
}
class User extends Model {
static entity = 'users'
static fields () {
return {
id: this.attr(null),
country_id: this.attr(null),
posts: this.hasMany(Post, 'user_id')
}
}
}
class Post extends Model {
static entity = 'posts'
static fields () {
return {
id: this.attr(null),
user_id: this.attr(null)
}
}
}
And then you try to save the following data.
Country.create({
data: {
id: 1,
posts: [
{ id: 1 },
{ id: 2 }
]
}
})
Vuex ORM will normalize the data and save them to the store as below.
{
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.
Country.create({
data: {
id: 1,
users: [
{
id: 1,
posts: [
{ id: 1 }
]
},
{
id: 2,
posts: [
{ id: 2 }
]
}
]
}
})
# 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.
User.create({
data: { id: 1, name: 'John Doe' }
}).then((entities) => {
console.log(entities)
})
/*
{
users: [
{ id: 1, name: 'John Doe' }
]
}
*/
If you prefer to use async / await, then you can compose inserts like this.
const entities = await User.create({
data: { id: 1, name: 'John Doe' }
})
/*
{
users: [
{ id: 1, name: 'John Doe' }
]
}
*/
Note that the return value will be in normalized shape. For example, when you insert data with relationships, those relationships are detached from each other.
const entities = await User.create({
data: {
id: 1,
name: 'John Doe',
posts: [
{ id: 1, user_id: 1, title: 'Post title 1' },
{ id: 2, user_id: 1, title: 'Post title 2' }
]
}
})
/*
{
users: [
{ id: 1, name: 'John Doe' }
],
posts: [
{ id: 1, user_id: 1, title: 'Post title 1' },
{ id: 2, user_id: 1, title: 'Post title 2' }
]
}
*/
The new
method will also return the newly created record, but it'll return only one record since it's obvious that there's no relational data.
const user = await User.new()
// { id: 1, name: 'Default Name' }
# Updates
To update existing data, you can do so with the update
method. Following example is some simple Vue Component handling data update with form data. The update
method takes where
condition and data
as payload.
<template>
<div>
<label>Name</label>
<input :value="user.name" @input="updateName">
</div>
</template>
<script>
import User from '@/models/User'
export default {
computed: {
user () {
return User.find(1)
}
},
methods: {
updateName (e) {
User.update({
where: 1,
data: {
name: e.target.value
}
})
}
}
}
</script>
where
condition can be a Number
or a String
representing the value of the primary key of the Model. data
is the data you want to update. Let's see some example here to understand how it works.
// Initial State.
{
entities: {
users: {
1: { id: 1, name: 'John', age: 20 },
2: { id: 2, name: 'Jane', age: 30 }
}
}
}
// Update data.
User.update({
where: 2,
data: { age: 24 }
})
// State after `update`
{
entities: {
users: {
1: { id: 1, name: 'John', age: 20 },
2: { id: 2, name: 'Jane', age: 24 }
}
}
}
You may also pass Function
to the where
field to determine the target data. The Function
takes the record as the argument and must return Boolean
. This approach also lets you update multiple records at once.
// Initial State.
{
entities: {
users: {
1: { id: 1, name: 'John', age: 20, active: false },
2: { id: 2, name: 'Jane', age: 20, active: false },
3: { id: 3, name: 'Johnny', age: 30, active: false }
}
}
}
// Update via function.
User.update({
where: (user) => {
return user.age === 20
},
data: { active: true }
})
// State after `update`.
{
entities: {
users: {
1: { id: 1, name: 'John', age: 20, active: true },
2: { id: 2, name: 'Jane', age: 20, active: true },
3: { id: 3, name: 'Johnny', age: 30, active: false }
}
}
}
data
also could be a Function
. The Function
will receive the target record as an argument. Then you can modify any properties. This is especially useful when you want to interact with the field that contains Array
or Object
.
// Initial State.
{
entities: {
users: {
1: { id: 1, name: 'John', age: 20, keywords: ['sales'] },
2: { id: 2, name: 'Jane', age: 30, keywords: ['marketing'] }
}
}
}
// Update data via function.
User.update({
where: 2,
data (user) {
user.name = 'Jane Doe'
user.keywords.push('pr')
}
})
// State after `update`.
{
entities: {
users: {
1: { id: 1, name: 'John', age: 20, keywords: ['sales'] },
2: { id: 2, name: 'Jane Doe', age: 30, keywords: ['marketing', 'PR'] }
}
}
}
You may also pass the whole data object as a single argument. In this case, the primary key must exist in the data object.
// Initial State.
{
entities: {
users: {
1: { id: 1, name: 'John', age: 20 },
2: { id: 2, name: 'Jane', age: 30 }
}
}
}
// Update by passing data object.
User.update({
id: 2, // <- Primary key must exist.
age: 24
})
// State after `update`.
{
entities: {
users: {
1: { id: 1, name: 'John', age: 20 },
2: { id: 2, name: 'Jane', age: 24 }
}
}
}
It is also possible to pass an array of objects to the data
property when those objects contain the primary key.
// Initial State.
{
entities: {
users: {
1: { id: 1, name: 'John', age: 20 },
2: { id: 2, name: 'Jane', age: 30 },
3: { id: 3, name: 'Harry', age: 37 }
}
}
}
// Update by passing an array of objects.
User.update({
data: [
{
id: 2, // <- Primary key must exist.
age: 24
},
{
id: 3,
age: 40
}
]
})
// State after `update`.
{
entities: {
users: {
1: { id: 1, name: 'John', age: 20 },
2: { id: 2, name: 'Jane', age: 24 },
3: { id: 3, name: 'Harry', age: 40 }
}
}
}
# Updating Relationships
Updating relationships works the same as insert methods such as insert
and create
. Any data you pass to data
field will be normalized and updated accordingly in Vuex Store. Note that you can't specify where
field for relationships, so don't forget to include the primary key for the relationships.
// Initial State.
{
entities: {
users: {
1: { id: 1, name: 'John', age: 20 }
},
posts: {
1: { id: 1, user_id: 1, title: 'Post title 1' },
2: { id: 2, user_id: 1, title: 'Post title 2' }
}
}
}
// Update data with relationship.
User.update({
where: 1,
data: {
name: 'John Doe',
posts: [
{ id: 1, title: 'Edited post title' }
]
}
})
// State after `update`.
{
entities: {
users: {
1: { id: 1, name: 'John Doe', age: 20 }
},
posts: {
1: { id: 1, user_id: 1, title: 'Edited post title' },
2: { id: 2, user_id: 1, title: 'Post title 2' }
}
}
}
# Get Updated Data
Same as insert
or create
, the update
method also returns updated data as a Promise. However, the data structure that will be returned is different depending on the use of the update
method.
If you pass the whole object to the update
method, it will return all updated entities.
const user = await User.update({ id: 1, age: 24 })
/*
{
users: [{ id: 1, name: 'John Doe', age: 24 }]
}
*/
When specifying id value in the where
property, it will return a single item that was updated.
const user = await User.update({
where: 1,
data: { age: 24 }
})
// { id: 1, name: 'John Doe', age: 24 },
When updating records by specifying a closure to the where
property, the returned data will always be an array containing all updated data.
const user = await User.update({
where: record => record.age === 30,
data: { age: 24 }
})
/*
[
{ id: 1, name: 'John Doe', age: 24 },
{ id: 2, name: 'Jane Doe', age: 24 }
]
*/
# Insert or Update
The insertOrUpdate
method will insert new data if any records do not exist in the store, and update the data of records that do exist.
// Initial State.
{
entities: {
users: {
data: {
1: { id: 1, name: 'John', title: 'Sales' }
}
}
}
}
// `insertOrUpdate` is going to add new data and update data.
User.insertOrUpdate({
data: [
{ id: 1, name: 'Peter' },
{ id: 2, name: 'Hank' }
]
})
// State after `insertOrUpdate`.
{
entities: {
users: {
data: {
1: { id: 1, name: 'Peter', title: 'Sales' },
2: { id: 2, name: 'Hank', title: null }
}
}
}
}
See how the title
of the User ID of 1 is not affected. If we were using the insert
method, both User ID 1 and 2 will have the title
value set to null
(the default value of the field).
The insertOrUpdate
is also very useful when inserting dynamically embedded relationships. For example, if an API supports dynamic embedding of relationships and doesn't always return all relationships, the relationships would be emptied when missing on insert
. This is because insertion methods, insert
, create
and update
, will be applied for all nested relationships.
Let's see what we mean by this in the example. What happen if we were to insert
User with Posts.
// Initial State.
{
entities: {
users: {
data: {
1: { id: 1, name: 'John' }
}
},
posts: {
data: {
1: { id: 1, user_id: 1, title: 'Post title 1', body: 'Very long body...' }
}
}
}
}
// We would destroy post's body field if we use `insert`.
User.insert({
id: 1,
name: 'John',
posts: [
{ id: 1, user_id: 1, title: 'Post title 1' },
{ id: 2, user_id: 1, title: 'Post title 2' }
]
})
// State after `insert`.
{
entities: {
users: {
data: {
1: { id: 1, name: 'John' }
}
},
posts: {
data: {
1: { id: 1, user_id: 1, title: 'Post title 1', body: null },
2: { id: 1, user_id: 2, title: 'Post title 2', body: null }
}
}
}
}
See body
of Post ID of 1 got removed. This is because Post ID 1 got "inserted". This case, we want to "update" the posts. For these cases you can use the insertOrUpdate
method:
// Initial State.
{
entities: {
users: {
data: {
1: { id: 1, name: 'John' }
}
},
posts: {
data: {
1: { id: 1, user_id: 1, title: 'Post title 1', body: 'Very long body...' }
}
}
}
}
// Using `insertOrUpdate` will update any existing records while inserting new records that don't exist.
User.insertOrUpdate({
id: 1,
name: 'John',
posts: [
{ id: 1, user_id: 1, title: 'Post title 1' },
{ id: 2, user_id: 1, title: 'Post title 2' }
]
})
// State after `insertOrUpdate`.
{
entities: {
users: {
data: {
1: { id: 1, name: 'John' }
}
},
posts: {
data: {
1: { id: 1, user_id: 1, title: 'Post title 1', body: 'Very long body...' },
2: { id: 1, user_id: 2, title: 'Post title 2', body: null }
}
}
}
}
# Insert Method for Relationships
As described at "Insert or Update" section, when you insert data with related records included, all of the related records will be inserted using the base method that was called. For example, if you call the create
method, all of the related records get "created" into the store.
Sometimes you might want the option to choose how Vuex ORM inserts data into the store depending on the relationship. For example, you might want to "create" users but "insert" posts. You may do so by passing additional options to the method.
// `create` users but `insert` posts.
User.create({
data: [...],
insert: ['posts']
})
// `insert` users but `create` posts and `comments`.
User.insert({
data: [...],
create: ['posts', 'comments']
})
The available options are:
create
insert
update
insertOrUpdate
Note that the value passed to those create
or insert
options should be the entity name of the Model, not the name of the field that defines the relationship in the model.
# Save Method
The $save
method at Model allows you to insert or update the record to the Store.
const user = new User()
user.name = 'John Doe'
user.$save()
Note that $save
method will only save the model itself. It wouldn't save any relationships attached to the Model.
Be careful when using the $save
method, that you might like the syntax due to its consistency with other ORMs such as Laravel Eloquent, remember you should not mutate the record inside the store. Simply put, never mutate the record you fetched by the getters.
const user = User.find(1)
// Never do this!
user.name = 'John Doe'
The perfect place to use $save
method is when you need to create a fresh new record inside Vue Component like below example.
export default {
data () {
return {
user: new User()
}
},
methods: {
updateName (e) {
this.user.name = e.target.value
},
save () {
this.user.$save()
}
}
}
# Dispatching Actions from Root Module
You can dispatch action from root module as well. With this syntax, you must specify the entity manually by passing entity
field.
store.dispatch('entities/create', { entity: 'users', data: { /* ... */ } })
// Above is equivalent to this.
User.create({ data: { /* ... */ } })
Note that since the root module needs entity
, you can't dispatch update
action through root by passing the condition directly.
// This would not work.
store.dispatch('entities/update', { id: 2, age: 24 })
// you must have entity field.
store.dispatch('entities/update', {
entity: 'users',
where: 2,
data: { age: 24 }
})