fix: update association value

This commit is contained in:
Chareice 2021-12-08 13:00:09 +08:00
parent f09991d932
commit 90846686ac
6 changed files with 123 additions and 55 deletions

View File

@ -35,44 +35,6 @@ describe('database', () => {
expect(imageCollection.fields.has('url2')).toBeTruthy();
});
test('hasMany with inverse belongsTo relation', async () => {
const db = mockDatabase();
const UserCollection = db.collection({
name: 'users',
fields: [
{ type: 'string', name: 'name' },
{ type: 'hasMany', name: 'posts' },
],
});
const PostCollection = db.collection({
name: 'posts',
fields: [{ type: 'string', name: 'title' }],
});
expect(UserCollection.model.associations.posts).toBeDefined();
expect(PostCollection.model.associations.user).toBeDefined();
});
test('belongsTo with inverse hasMany relation', async () => {
const db = mockDatabase();
const UserCollection = db.collection({
name: 'users',
fields: [{ type: 'string', name: 'name' }],
});
const PostCollection = db.collection({
name: 'posts',
fields: [
{ type: 'string', name: 'title' },
{ type: 'belongsTo', name: 'user' },
],
});
expect(PostCollection.model.associations.user).toBeDefined();
expect(UserCollection.model.associations.posts).toBeDefined();
});
test('get collection', async () => {
const db = mockDatabase();
expect(db.getCollection('test')).toBeUndefined();

View File

@ -17,6 +17,7 @@ describe('option parser', () => {
fields: [
{ type: 'string', name: 'name' },
{ type: 'integer', name: 'age' },
{ type: 'hasMany', name: 'posts' },
],
});

View File

@ -1,8 +1,10 @@
import { mockDatabase } from '../index';
import { BelongsToManyRepository } from '../../relation-repository/belongs-to-many-repository';
import Database from '../../database';
describe('belongs to many', () => {
let db;
let db: Database;
let User;
let Post;
let Tag;
let PostTag;
@ -14,11 +16,29 @@ describe('belongs to many', () => {
fields: [{ type: 'string', name: 'tagged_at' }],
});
User = db.collection({
name: 'users',
fields: [
{
type: 'string',
name: 'name',
},
{
type: 'hasMany',
name: 'posts',
},
],
});
Post = db.collection({
name: 'posts',
fields: [
{ type: 'belongsToMany', name: 'tags', through: 'posts_tags' },
{ type: 'string', name: 'title' },
{
type: 'belongsTo',
name: 'user',
},
],
});
@ -248,6 +268,50 @@ describe('belongs to many', () => {
expect(p2Tag.posts_tags.tagged_at).toBeNull();
});
test('update association values', async () => {
const u1 = await User.repository.create({
values: {
name: 'u1',
},
});
const p1 = await Post.repository.create({
values: {
title: 'p1',
tags: [{ name: 't1' }, { name: 't2' }],
user: u1.id,
},
});
const tag = await Tag.repository.findOne();
const tagPostsRepository = new BelongsToManyRepository(Tag, 'posts', tag.id);
await tagPostsRepository.update({
values: {
user: {
id: u1.get('id'),
name: 'u0',
},
},
});
await u1.reload();
expect(u1.get('name')).toEqual('u1');
await tagPostsRepository.update({
values: {
user: {
id: u1.get('id'),
name: 'u0',
},
},
updateAssociationValues: ['user'],
});
await u1.reload();
expect(u1.get('name')).toEqual('u0');
});
test('add', async () => {
let t1 = await Tag.repository.create({
values: { name: 't1' },

View File

@ -21,12 +21,16 @@ describe('repository.find', () => {
name: 'posts',
fields: [
{ type: 'string', name: 'name' },
{ type: 'belongsTo', name: 'user' },
{ type: 'hasMany', name: 'comments' },
],
});
Comment = db.collection({
name: 'comments',
fields: [{ type: 'string', name: 'name' }],
fields: [
{ type: 'string', name: 'name' },
{ type: 'belongsTo', name: 'post' },
],
});
await db.sync();
await User.repository.createMany({

View File

@ -15,6 +15,7 @@ describe('repository find', () => {
fields: [
{ type: 'string', name: 'name' },
{ type: 'integer', name: 'age' },
{ type: 'hasMany', name: 'posts' },
],
});

View File

@ -9,6 +9,8 @@ import {
BelongsTo,
BelongsToMany,
HasMany,
Transactionable,
Hookable,
} from 'sequelize';
import { UpdateGuard } from './update-guard';
import { TransactionAble } from './repository';
@ -21,6 +23,10 @@ function isStringOrNumber(value: any) {
return typeof value === 'string' || typeof value === 'number';
}
function getKeysByPrefix(keys: string[], prefix: string) {
return keys.filter((key) => key.startsWith(`${prefix}.`)).map((key) => key.substring(prefix.length + 1));
}
export function modelAssociations(instance: Model) {
return (<typeof Model>instance.constructor).associations;
}
@ -57,6 +63,11 @@ interface UpdateOptions extends TransactionAble {
sourceModel?: Model;
}
interface UpdateAssociationOptions extends Transactionable, Hookable {
updateAssociationValues?: string[];
sourceModel?: Model;
}
export async function updateModelByValues(instance: Model, values: UpdateValue, options?: UpdateOptions) {
if (!options?.sanitized) {
const guard = new UpdateGuard();
@ -105,7 +116,7 @@ export async function updateThroughTableValue(
* @param values
* @param options
*/
export async function updateAssociations(instance: Model, values: any, options: any = {}) {
export async function updateAssociations(instance: Model, values: any, options: UpdateAssociationOptions = {}) {
// if no values set, return
if (!values) {
return;
@ -119,8 +130,10 @@ export async function updateAssociations(instance: Model, values: any, options:
transaction = await instance.sequelize.transaction();
}
const keys = Object.keys(values);
for (const key of Object.keys(modelAssociations(instance))) {
if (values[key]) {
if (keys.includes(key)) {
await updateAssociation(instance, key, values[key], {
...options,
transaction,
@ -159,7 +172,12 @@ export async function updateAssociations(instance: Model, values: any, options:
* @param value
* @param options
*/
export async function updateAssociation(instance: Model, key: string, value: any, options: any = {}) {
export async function updateAssociation(
instance: Model,
key: string,
value: any,
options: UpdateAssociationOptions = {},
) {
const association = modelAssociationByKey(instance, key);
if (!association) {
@ -183,7 +201,12 @@ export async function updateAssociation(instance: Model, key: string, value: any
* @param value
* @param options
*/
export async function updateSingleAssociation(model: Model, key: string, value: any, options: any = {}) {
export async function updateSingleAssociation(
model: Model,
key: string,
value: any,
options: UpdateAssociationOptions = {},
) {
const association = <HasOne | BelongsTo>modelAssociationByKey(model, key);
if (!association) {
@ -194,7 +217,8 @@ export async function updateSingleAssociation(model: Model, key: string, value:
return false;
}
const { transaction = await model.sequelize.transaction() } = options;
const { updateAssociationValues = [], transaction = await model.sequelize.transaction() } = options;
const keys = getKeysByPrefix(updateAssociationValues, key);
try {
// set method of association
@ -220,6 +244,7 @@ export async function updateSingleAssociation(model: Model, key: string, value:
}
return true;
}
if (value instanceof Model) {
await model[setAccessor](value);
model.setDataValue(key, value);
@ -248,9 +273,15 @@ export async function updateSingleAssociation(model: Model, key: string, value:
},
transaction,
});
if (instance) {
await model[setAccessor](instance);
await updateAssociations(instance, value, { transaction, ...options });
if (updateAssociationValues.includes(key)) {
await instance.update(value, { ...options, transaction });
}
await updateAssociations(instance, value, { ...options, transaction, updateAssociationValues: keys });
model.setDataValue(key, instance);
if (!options.transaction) {
await transaction.commit();
@ -259,12 +290,8 @@ export async function updateSingleAssociation(model: Model, key: string, value:
}
}
if (value[dataKey] === null) {
return await removeAssociation();
}
const instance = await model[createAccessor](value, { transaction });
await updateAssociations(instance, value, { transaction, ...options });
await updateAssociations(instance, value, { ...options, transaction, updateAssociationValues: keys });
model.setDataValue(key, instance);
// @ts-ignore
if (association.targetKey) {
@ -288,7 +315,12 @@ export async function updateSingleAssociation(model: Model, key: string, value:
* @param value
* @param options
*/
export async function updateMultipleAssociation(model: Model, key: string, value: any, options: any = {}) {
export async function updateMultipleAssociation(
model: Model,
key: string,
value: any,
options: UpdateAssociationOptions = {},
) {
const association = <BelongsToMany | HasMany>modelAssociationByKey(model, key);
if (!association) {
@ -299,7 +331,8 @@ export async function updateMultipleAssociation(model: Model, key: string, value
return false;
}
const { transaction = await model.sequelize.transaction() } = options;
const { updateAssociationValues = [], transaction = await model.sequelize.transaction() } = options;
const keys = getKeysByPrefix(updateAssociationValues, key);
try {
const setAccessor = association.accessors.set;
@ -359,7 +392,7 @@ export async function updateMultipleAssociation(model: Model, key: string, value
if (isUndefinedOrNull(item[pk])) {
// create new record
const instance = await model[createAccessor](item, accessorOptions);
await updateAssociations(instance, item, { transaction, ...options });
await updateAssociations(instance, item, { ...options, transaction, updateAssociationValues: keys });
list3.push(instance);
} else {
// set & update record
@ -369,7 +402,10 @@ export async function updateMultipleAssociation(model: Model, key: string, value
const addAccessor = association.accessors.add;
await model[addAccessor](item[pk], accessorOptions);
await updateAssociations(instance, item, { transaction, ...options });
if (updateAssociationValues.includes(key)) {
await instance.update(item, { ...options, transaction });
}
await updateAssociations(instance, item, { ...options, transaction, updateAssociationValues: keys });
list3.push(instance);
}
}