tachybase_todo/packages/plugins/collection-manager/src/hooks/afterCreateForForeignKeyField.ts
2022-11-21 14:58:07 +08:00

174 lines
4.4 KiB
TypeScript

import Database from '@nocobase/database';
export function afterCreateForForeignKeyField(db: Database) {
function generateFkOptions(collectionName: string, foreignKey: string) {
const collection = db.getCollection(collectionName);
if (!collection) {
throw new Error('collection not found');
}
const M = collection.model;
const attr = M.rawAttributes[foreignKey];
if (!attr) {
throw new Error(`${collectionName}.${foreignKey} does not exists`);
}
return attribute2field(attr);
}
// Foreign key types are only integer and string
function attribute2field(attribute: any) {
let type = 'bigInt';
if (attribute.type.constructor.name === 'INTEGER') {
type = 'integer';
} else if (attribute.type.constructor.name === 'STRING') {
type = 'string';
}
const name = attribute.fieldName;
const data = {
interface: 'integer',
name,
type,
uiSchema: {
type: 'number',
title: name,
'x-component': 'InputNumber',
'x-read-pretty': true,
},
};
if (type === 'string') {
data['interface'] = 'input';
data['uiSchema'] = {
type: 'string',
title: name,
'x-component': 'Input',
'x-read-pretty': true,
};
}
return data;
}
async function createFieldIfNotExists({ values, transaction }) {
const { collectionName, name } = values;
if (!collectionName || !name) {
throw new Error(`field options invalid`);
}
const r = db.getRepository('fields');
const instance = await r.findOne({
filter: {
collectionName,
name,
},
transaction,
});
if (instance) {
if (instance.type !== values.type) {
throw new Error(`fk type invalid`);
}
instance.set('sort', 1);
instance.set('isForeignKey', true);
await instance.save({ transaction });
} else {
const creatInstance = await r.create({
values: {
isForeignKey: true,
...values,
},
transaction,
});
// SortField#setSortValue instance._previousDataValues[scopeKey] judgment cause create set sort:1 invalid, need update
creatInstance.set('sort', 1);
await creatInstance.save({ transaction });
}
// update ID sort:0
await r.update({
filter: {
collectionName,
options: {
primaryKey: true,
},
},
values: {
sort: 0,
},
transaction,
});
}
return async (model, { transaction, context }) => {
// skip if no app context
if (!context) {
return;
}
const { type, interface: interfaceType, collectionName, target, through, foreignKey, otherKey } = model.get();
// foreign key in target collection
if (['oho', 'o2m'].includes(interfaceType)) {
const values = generateFkOptions(target, foreignKey);
await createFieldIfNotExists({
values: {
collectionName: target,
...values,
},
transaction,
});
}
// foreign key in source collection
else if (['obo', 'm2o'].includes(interfaceType)) {
const values = generateFkOptions(collectionName, foreignKey);
await createFieldIfNotExists({
values: { collectionName, ...values },
transaction,
});
}
// foreign key in through collection
else if (['linkTo', 'm2m'].includes(interfaceType)) {
if (type !== 'belongsToMany') {
return;
}
const r = db.getRepository('collections');
const instance = await r.findOne({
filter: {
name: through,
},
transaction,
});
if (!instance) {
await r.create({
values: {
name: through,
title: through,
timestamps: false,
autoGenId: false,
hidden: true,
autoCreate: true,
isThrough: true,
},
transaction,
});
}
const opts1 = generateFkOptions(through, foreignKey);
const opts2 = generateFkOptions(through, otherKey);
await createFieldIfNotExists({
values: {
collectionName: through,
...opts1,
},
transaction,
});
await createFieldIfNotExists({
values: {
collectionName: through,
...opts2,
},
transaction,
});
}
};
}