60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
import invariant from "invariant";
|
|
import type Model from "../base/Model";
|
|
|
|
type RelationOptions = {
|
|
/** Whether this relation is required */
|
|
required?: boolean;
|
|
};
|
|
|
|
/**
|
|
* A decorator that records this key as a relation field on the model.
|
|
* Properties decorated with @Relation will merge and read their data from
|
|
* the associated store.
|
|
*
|
|
* @param classResolver A function that returns the class of the relation
|
|
* @param options Optional options for the relation definition
|
|
*/
|
|
export default function Relation<T extends typeof Model>(
|
|
classResolver: () => T,
|
|
options?: RelationOptions
|
|
) {
|
|
return function (target: any, propertyKey: string) {
|
|
const idKey = `${String(propertyKey)}Id`;
|
|
const relationClass = classResolver();
|
|
const relationClassName = relationClass.name;
|
|
|
|
Object.defineProperty(target, propertyKey, {
|
|
get() {
|
|
const id: string | undefined = this[idKey];
|
|
|
|
if (!id) {
|
|
return undefined;
|
|
}
|
|
|
|
const store =
|
|
this.store.rootStore[`${relationClassName.toLowerCase()}s`];
|
|
invariant(store, `Store for ${relationClassName} not found`);
|
|
|
|
return store.get(id);
|
|
},
|
|
set(newValue: Model | Partial<Model> | undefined) {
|
|
this[idKey] = newValue ? newValue.id : undefined;
|
|
|
|
if (newValue) {
|
|
const store =
|
|
this.store.rootStore[`${relationClassName.toLowerCase()}s`];
|
|
invariant(store, `Store for ${relationClassName} not found`);
|
|
|
|
store.add(newValue);
|
|
} else if (options?.required) {
|
|
throw new Error(
|
|
`Cannot set required ${String(propertyKey)} to undefined`
|
|
);
|
|
}
|
|
},
|
|
enumerable: true,
|
|
configurable: true,
|
|
});
|
|
};
|
|
}
|