mirror of
https://github.com/directus/directus.git
synced 2026-01-29 22:57:55 -05:00
Add relational o2m write
This commit is contained in:
@@ -12,7 +12,8 @@ export enum Action {
|
||||
}
|
||||
|
||||
export const createActivity = async (data: Record<string, any>, query?: Query) => {
|
||||
return await ItemsService.createItem('directus_activity', data, query);
|
||||
const primaryKey = await ItemsService.createItem('directus_activity', data);
|
||||
return await ItemsService.readItem('directus_activity', primaryKey, query);
|
||||
};
|
||||
|
||||
export const readActivities = async (query?: Query) => {
|
||||
|
||||
@@ -2,7 +2,8 @@ import { Query } from '../types/query';
|
||||
import * as ItemsService from './items';
|
||||
|
||||
export const createFolder = async (data: Record<string, any>, query: Query) => {
|
||||
return await ItemsService.createItem('directus_folders', data, query);
|
||||
const primaryKey = await ItemsService.createItem('directus_folders', data);
|
||||
return await ItemsService.readItem('directus_folders', primaryKey, query);
|
||||
};
|
||||
|
||||
export const readFolders = async (query: Query) => {
|
||||
@@ -18,7 +19,8 @@ export const updateFolder = async (
|
||||
data: Record<string, any>,
|
||||
query: Query
|
||||
) => {
|
||||
return await ItemsService.updateItem('directus_folders', pk, data, query);
|
||||
const primaryKey = await ItemsService.updateItem('directus_folders', pk, data);
|
||||
return await ItemsService.readItem('directus_folders', primaryKey, query);
|
||||
};
|
||||
|
||||
export const deleteFolder = async (pk: string | number) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Query } from '../types/query';
|
||||
import runAST from '../database/run-ast';
|
||||
import getAST from '../utils/get-ast';
|
||||
import * as PayloadService from './payload';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
export const createItem = async (collection: string, data: Record<string, any>) => {
|
||||
let payload = await PayloadService.processValues('create', collection, data);
|
||||
@@ -10,11 +11,24 @@ export const createItem = async (collection: string, data: Record<string, any>)
|
||||
payload = await PayloadService.processM2O(collection, payload);
|
||||
|
||||
const primaryKeyField = await schemaInspector.primary(collection);
|
||||
const result = await database(collection).insert(payload).returning(primaryKeyField);
|
||||
|
||||
// payload process o2m
|
||||
// Only insert the values that actually save to an existing column. This ensures we ignore aliases etc
|
||||
const columns = await schemaInspector.columns(collection);
|
||||
const primaryKeys = await database(collection)
|
||||
.insert(
|
||||
pick(
|
||||
payload,
|
||||
columns.map(({ column }) => column)
|
||||
)
|
||||
)
|
||||
.returning(primaryKeyField);
|
||||
|
||||
return result[0]; // pk
|
||||
// This allows the o2m values to be populated correctly
|
||||
payload[primaryKeyField] = primaryKeys[0];
|
||||
|
||||
await PayloadService.processO2M(collection, payload);
|
||||
|
||||
return primaryKeys[0];
|
||||
};
|
||||
|
||||
export const readItems = async <T = Record<string, any>>(
|
||||
@@ -55,14 +69,25 @@ export const updateItem = async (
|
||||
pk: number | string,
|
||||
data: Record<string, any>
|
||||
) => {
|
||||
const payload = await PayloadService.processValues('create', collection, data);
|
||||
let payload = await PayloadService.processValues('create', collection, data);
|
||||
|
||||
payload = await PayloadService.processM2O(collection, payload);
|
||||
|
||||
const primaryKeyField = await schemaInspector.primary(collection);
|
||||
const result = await database(collection)
|
||||
.update(payload)
|
||||
|
||||
// Only insert the values that actually save to an existing column. This ensures we ignore aliases etc
|
||||
const columns = await schemaInspector.columns(collection);
|
||||
const primaryKeys = await database(collection)
|
||||
.update(
|
||||
pick(
|
||||
payload,
|
||||
columns.map(({ column }) => column)
|
||||
)
|
||||
)
|
||||
.where({ [primaryKeyField]: pk })
|
||||
.returning(primaryKeyField);
|
||||
|
||||
return result[0]; // pk
|
||||
return primaryKeys[0];
|
||||
};
|
||||
|
||||
export const deleteItem = async (collection: string, pk: number | string) => {
|
||||
|
||||
@@ -119,10 +119,9 @@ async function processField(
|
||||
/**
|
||||
* Recursively checks for nested relational items, and saves them bottom up, to ensure we have IDs etc ready
|
||||
*/
|
||||
export const processM2O = async (
|
||||
collection: string,
|
||||
payload: Record<string, any> | Record<string, any>[]
|
||||
) => {
|
||||
export const processM2O = async (collection: string, payload: Record<string, any>) => {
|
||||
const payloadClone = clone(payload);
|
||||
|
||||
const relations = await database
|
||||
.select<Relation[]>('*')
|
||||
.from('directus_relations')
|
||||
@@ -131,15 +130,15 @@ export const processM2O = async (
|
||||
// Only process related records that are actually in the payload
|
||||
const relationsToProcess = relations.filter((relation) => {
|
||||
return (
|
||||
payload.hasOwnProperty(relation.field_many) &&
|
||||
typeof payload[relation.field_many] === 'object'
|
||||
payloadClone.hasOwnProperty(relation.field_many) &&
|
||||
typeof payloadClone[relation.field_many] === 'object'
|
||||
);
|
||||
});
|
||||
|
||||
// Save all nested m2o records
|
||||
await Promise.all(
|
||||
relationsToProcess.map(async (relation) => {
|
||||
const relatedRecord = payload[relation.field_many];
|
||||
const relatedRecord = payloadClone[relation.field_many];
|
||||
const hasPrimaryKey = relatedRecord.hasOwnProperty(relation.primary_one);
|
||||
|
||||
let relatedPrimaryKey: string | number;
|
||||
@@ -159,9 +158,64 @@ export const processM2O = async (
|
||||
}
|
||||
|
||||
// Overwrite the nested object with just the primary key, so the parent level can be saved correctly
|
||||
payload[relation.field_many] = relatedPrimaryKey;
|
||||
payloadClone[relation.field_many] = relatedPrimaryKey;
|
||||
})
|
||||
);
|
||||
|
||||
return payload;
|
||||
return payloadClone;
|
||||
};
|
||||
|
||||
export const processO2M = async (collection: string, payload: Record<string, any>) => {
|
||||
const payloadClone = clone(payload);
|
||||
|
||||
const relations = await database
|
||||
.select<Relation[]>('*')
|
||||
.from('directus_relations')
|
||||
.where({ collection_one: collection });
|
||||
|
||||
// Only process related records that are actually in the payload
|
||||
const relationsToProcess = relations.filter((relation) => {
|
||||
return (
|
||||
payloadClone.hasOwnProperty(relation.field_one) &&
|
||||
Array.isArray(payloadClone[relation.field_one])
|
||||
);
|
||||
});
|
||||
|
||||
// Save all nested o2m records
|
||||
await Promise.all(
|
||||
relationsToProcess.map(async (relation) => {
|
||||
const relatedRecords = payloadClone[relation.field_one];
|
||||
|
||||
await Promise.all(
|
||||
relatedRecords.map(async (relatedRecord: any, index: number) => {
|
||||
relatedRecord[relation.field_many] = payloadClone[relation.primary_one];
|
||||
|
||||
const hasPrimaryKey = relatedRecord.hasOwnProperty(relation.primary_many);
|
||||
|
||||
let relatedPrimaryKey: string | number;
|
||||
|
||||
if (hasPrimaryKey) {
|
||||
relatedPrimaryKey = relatedRecord[relation.primary_many];
|
||||
|
||||
await ItemsService.updateItem(
|
||||
relation.collection_many,
|
||||
relatedPrimaryKey,
|
||||
relatedRecord
|
||||
);
|
||||
} else {
|
||||
relatedPrimaryKey = await ItemsService.createItem(
|
||||
relation.collection_many,
|
||||
relatedRecord
|
||||
);
|
||||
}
|
||||
|
||||
relatedRecord[relation.primary_many] = relatedPrimaryKey;
|
||||
|
||||
payloadClone[relation.field_one][index] = relatedRecord;
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
return payloadClone;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
export type Relation = {
|
||||
id: number;
|
||||
|
||||
collection_many: string;
|
||||
field_many: string;
|
||||
primary_many: string;
|
||||
|
||||
collection_one: string;
|
||||
field_one: string;
|
||||
primary_one: string;
|
||||
|
||||
Reference in New Issue
Block a user