UNPKG

18.7 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var tslib_1 = require("tslib");
4var EntityMetadata_1 = require("./EntityMetadata");
5/**
6 * Contains all information about some entity's relation.
7 */
8var RelationMetadata = /** @class */ (function () {
9 // ---------------------------------------------------------------------
10 // Constructor
11 // ---------------------------------------------------------------------
12 function RelationMetadata(options) {
13 /**
14 * Indicates if this is a parent (can be only many-to-one relation) relation in the tree tables.
15 */
16 this.isTreeParent = false;
17 /**
18 * Indicates if this is a children (can be only one-to-many relation) relation in the tree tables.
19 */
20 this.isTreeChildren = false;
21 /**
22 * Indicates if this relation's column is a primary key.
23 * Can be used only for many-to-one and owner one-to-one relations.
24 */
25 this.isPrimary = false;
26 /**
27 * Indicates if this relation is lazily loaded.
28 */
29 this.isLazy = false;
30 /**
31 * Indicates if this relation is eagerly loaded.
32 */
33 this.isEager = false;
34 /**
35 * Indicates if persistence is enabled for the relation.
36 * By default its enabled, but if you want to avoid any changes in the relation to be reflected in the database you can disable it.
37 * If its disabled you can only change a relation from inverse side of a relation or using relation query builder functionality.
38 * This is useful for performance optimization since its disabling avoid multiple extra queries during entity save.
39 */
40 this.persistenceEnabled = true;
41 /**
42 * If set to true then related objects are allowed to be inserted to the database.
43 */
44 this.isCascadeInsert = false;
45 /**
46 * If set to true then related objects are allowed to be updated in the database.
47 */
48 this.isCascadeUpdate = false;
49 /**
50 * If set to true then related objects are allowed to be remove from the database.
51 */
52 this.isCascadeRemove = false;
53 /**
54 * Indicates if relation column value can be nullable or not.
55 */
56 this.isNullable = true;
57 /**
58 * Indicates if this side is an owner of this relation.
59 */
60 this.isOwning = false;
61 /**
62 * Checks if this relation's type is "one-to-one".
63 */
64 this.isOneToOne = false;
65 /**
66 * Checks if this relation is owner side of the "one-to-one" relation.
67 * Owner side means this side of relation has a join column in the table.
68 */
69 this.isOneToOneOwner = false;
70 /**
71 * Checks if this relation has a join column (e.g. is it many-to-one or one-to-one owner side).
72 */
73 this.isWithJoinColumn = false;
74 /**
75 * Checks if this relation is NOT owner side of the "one-to-one" relation.
76 * NOT owner side means this side of relation does not have a join column in the table.
77 */
78 this.isOneToOneNotOwner = false;
79 /**
80 * Checks if this relation's type is "one-to-many".
81 */
82 this.isOneToMany = false;
83 /**
84 * Checks if this relation's type is "many-to-one".
85 */
86 this.isManyToOne = false;
87 /**
88 * Checks if this relation's type is "many-to-many".
89 */
90 this.isManyToMany = false;
91 /**
92 * Checks if this relation's type is "many-to-many", and is owner side of the relationship.
93 * Owner side means this side of relation has a join table.
94 */
95 this.isManyToManyOwner = false;
96 /**
97 * Checks if this relation's type is "many-to-many", and is NOT owner side of the relationship.
98 * Not owner side means this side of relation does not have a join table.
99 */
100 this.isManyToManyNotOwner = false;
101 /**
102 * Foreign keys created for this relation.
103 */
104 this.foreignKeys = [];
105 /**
106 * Join table columns.
107 * Join columns can be obtained only from owner side of the relation.
108 * From non-owner side of the relation join columns will be empty.
109 * If this relation is a many-to-one/one-to-one then it takes join columns from the current entity.
110 * If this relation is many-to-many then it takes all owner join columns from the junction entity.
111 */
112 this.joinColumns = [];
113 /**
114 * Inverse join table columns.
115 * Inverse join columns are supported only for many-to-many relations
116 * and can be obtained only from owner side of the relation.
117 * From non-owner side of the relation join columns will be undefined.
118 */
119 this.inverseJoinColumns = [];
120 this.entityMetadata = options.entityMetadata;
121 this.embeddedMetadata = options.embeddedMetadata;
122 var args = options.args;
123 this.target = args.target;
124 this.propertyName = args.propertyName;
125 this.relationType = args.relationType;
126 if (args.inverseSideProperty)
127 this.givenInverseSidePropertyFactory = args.inverseSideProperty;
128 this.isLazy = args.isLazy || false;
129 this.isCascadeInsert = args.options.cascade === true || (args.options.cascade instanceof Array && args.options.cascade.indexOf("insert") !== -1);
130 this.isCascadeUpdate = args.options.cascade === true || (args.options.cascade instanceof Array && args.options.cascade.indexOf("update") !== -1);
131 this.isCascadeRemove = args.options.cascade === true || (args.options.cascade instanceof Array && args.options.cascade.indexOf("remove") !== -1);
132 this.isPrimary = args.options.primary || false;
133 this.isNullable = args.options.nullable === false || this.isPrimary ? false : true;
134 this.onDelete = args.options.onDelete;
135 this.onUpdate = args.options.onUpdate;
136 this.deferrable = args.options.deferrable;
137 this.isEager = args.options.eager || false;
138 this.persistenceEnabled = args.options.persistence === false ? false : true;
139 this.isTreeParent = args.isTreeParent || false;
140 this.isTreeChildren = args.isTreeChildren || false;
141 this.type = args.type instanceof Function ? args.type() : args.type;
142 this.isOneToOne = this.relationType === "one-to-one";
143 this.isOneToMany = this.relationType === "one-to-many";
144 this.isManyToOne = this.relationType === "many-to-one";
145 this.isManyToMany = this.relationType === "many-to-many";
146 this.isOneToOneNotOwner = this.isOneToOne ? true : false;
147 this.isManyToManyNotOwner = this.isManyToMany ? true : false;
148 }
149 // ---------------------------------------------------------------------
150 // Public Methods
151 // ---------------------------------------------------------------------
152 /**
153 * Creates join column ids map from the given related entity ids array.
154 */
155 RelationMetadata.prototype.getRelationIdMap = function (entity) {
156 var joinColumns = this.isOwning ? this.joinColumns : this.inverseRelation.joinColumns;
157 var referencedColumns = joinColumns.map(function (joinColumn) { return joinColumn.referencedColumn; });
158 // console.log("entity", entity);
159 // console.log("referencedColumns", referencedColumns);
160 return EntityMetadata_1.EntityMetadata.getValueMap(entity, referencedColumns);
161 };
162 /**
163 * Ensures that given object is an entity id map.
164 * If given id is an object then it means its already id map.
165 * If given id isn't an object then it means its a value of the id column
166 * and it creates a new id map with this value and name of the primary column.
167 */
168 RelationMetadata.prototype.ensureRelationIdMap = function (id) {
169 if (id instanceof Object)
170 return id;
171 var joinColumns = this.isOwning ? this.joinColumns : this.inverseRelation.joinColumns;
172 var referencedColumns = joinColumns.map(function (joinColumn) { return joinColumn.referencedColumn; });
173 if (referencedColumns.length > 1)
174 throw new Error("Cannot create relation id map for a single value because relation contains multiple referenced columns.");
175 return referencedColumns[0].createValueMap(id);
176 };
177 /**
178 * Extracts column value from the given entity.
179 * If column is in embedded (or recursive embedded) it extracts its value from there.
180 */
181 RelationMetadata.prototype.getEntityValue = function (entity, getLazyRelationsPromiseValue) {
182 if (getLazyRelationsPromiseValue === void 0) { getLazyRelationsPromiseValue = false; }
183 if (entity === null || entity === undefined)
184 return undefined;
185 // extract column value from embeddeds of entity if column is in embedded
186 if (this.embeddedMetadata) {
187 // example: post[data][information][counters].id where "data", "information" and "counters" are embeddeds
188 // we need to get value of "id" column from the post real entity object
189 // first step - we extract all parent properties of the entity relative to this column, e.g. [data, information, counters]
190 var propertyNames = tslib_1.__spread(this.embeddedMetadata.parentPropertyNames);
191 // next we need to access post[data][information][counters][this.propertyName] to get column value from the counters
192 // this recursive function takes array of generated property names and gets the post[data][information][counters] embed
193 var extractEmbeddedColumnValue_1 = function (propertyNames, value) {
194 var propertyName = propertyNames.shift();
195 if (propertyName) {
196 if (value[propertyName]) {
197 return extractEmbeddedColumnValue_1(propertyNames, value[propertyName]);
198 }
199 return undefined;
200 }
201 return value;
202 };
203 // once we get nested embed object we get its column, e.g. post[data][information][counters][this.propertyName]
204 var embeddedObject = extractEmbeddedColumnValue_1(propertyNames, entity);
205 if (this.isLazy) {
206 if (embeddedObject["__" + this.propertyName + "__"] !== undefined)
207 return embeddedObject["__" + this.propertyName + "__"];
208 if (getLazyRelationsPromiseValue === true)
209 return embeddedObject[this.propertyName];
210 return undefined;
211 }
212 return embeddedObject ? embeddedObject[this.isLazy ? "__" + this.propertyName + "__" : this.propertyName] : undefined;
213 }
214 else { // no embeds - no problems. Simply return column name by property name of the entity
215 if (this.isLazy) {
216 if (entity["__" + this.propertyName + "__"] !== undefined)
217 return entity["__" + this.propertyName + "__"];
218 if (getLazyRelationsPromiseValue === true)
219 return entity[this.propertyName];
220 return undefined;
221 }
222 return entity[this.propertyName];
223 }
224 };
225 /**
226 * Sets given entity's relation's value.
227 * Using of this method helps to set entity relation's value of the lazy and non-lazy relations.
228 *
229 * If merge is set to true, it merges given value into currently
230 */
231 RelationMetadata.prototype.setEntityValue = function (entity, value) {
232 var propertyName = this.isLazy ? "__" + this.propertyName + "__" : this.propertyName;
233 if (this.embeddedMetadata) {
234 // first step - we extract all parent properties of the entity relative to this column, e.g. [data, information, counters]
235 var extractEmbeddedColumnValue_2 = function (embeddedMetadatas, map) {
236 // if (!object[embeddedMetadata.propertyName])
237 // object[embeddedMetadata.propertyName] = embeddedMetadata.create();
238 var embeddedMetadata = embeddedMetadatas.shift();
239 if (embeddedMetadata) {
240 if (!map[embeddedMetadata.propertyName])
241 map[embeddedMetadata.propertyName] = embeddedMetadata.create();
242 extractEmbeddedColumnValue_2(embeddedMetadatas, map[embeddedMetadata.propertyName]);
243 return map;
244 }
245 map[propertyName] = value;
246 return map;
247 };
248 return extractEmbeddedColumnValue_2(tslib_1.__spread(this.embeddedMetadata.embeddedMetadataTree), entity);
249 }
250 else {
251 entity[propertyName] = value;
252 }
253 };
254 /**
255 * Creates entity id map from the given entity ids array.
256 */
257 RelationMetadata.prototype.createValueMap = function (value) {
258 var _this = this;
259 var _a;
260 // extract column value from embeds of entity if column is in embedded
261 if (this.embeddedMetadata) {
262 // example: post[data][information][counters].id where "data", "information" and "counters" are embeddeds
263 // we need to get value of "id" column from the post real entity object and return it in a
264 // { data: { information: { counters: { id: ... } } } } format
265 // first step - we extract all parent properties of the entity relative to this column, e.g. [data, information, counters]
266 var propertyNames = tslib_1.__spread(this.embeddedMetadata.parentPropertyNames);
267 // now need to access post[data][information][counters] to get column value from the counters
268 // and on each step we need to create complex literal object, e.g. first { data },
269 // then { data: { information } }, then { data: { information: { counters } } },
270 // then { data: { information: { counters: [this.propertyName]: entity[data][information][counters][this.propertyName] } } }
271 // this recursive function helps doing that
272 var extractEmbeddedColumnValue_3 = function (propertyNames, map) {
273 var propertyName = propertyNames.shift();
274 if (propertyName) {
275 map[propertyName] = {};
276 extractEmbeddedColumnValue_3(propertyNames, map[propertyName]);
277 return map;
278 }
279 map[_this.propertyName] = value;
280 return map;
281 };
282 return extractEmbeddedColumnValue_3(propertyNames, {});
283 }
284 else { // no embeds - no problems. Simply return column property name and its value of the entity
285 return _a = {}, _a[this.propertyName] = value, _a;
286 }
287 };
288 // ---------------------------------------------------------------------
289 // Builder Methods
290 // ---------------------------------------------------------------------
291 /**
292 * Builds some depend relation metadata properties.
293 * This builder method should be used only after embedded metadata tree was build.
294 */
295 RelationMetadata.prototype.build = function () {
296 this.propertyPath = this.buildPropertyPath();
297 };
298 /**
299 * Registers given foreign keys in the relation.
300 * This builder method should be used to register foreign key in the relation.
301 */
302 RelationMetadata.prototype.registerForeignKeys = function () {
303 var foreignKeys = [];
304 for (var _i = 0; _i < arguments.length; _i++) {
305 foreignKeys[_i] = arguments[_i];
306 }
307 var _a;
308 (_a = this.foreignKeys).push.apply(_a, tslib_1.__spread(foreignKeys));
309 this.joinColumns = this.foreignKeys[0] ? this.foreignKeys[0].columns : [];
310 this.inverseJoinColumns = this.foreignKeys[1] ? this.foreignKeys[1].columns : [];
311 this.isOwning = this.isManyToOne || ((this.isManyToMany || this.isOneToOne) && this.joinColumns.length > 0);
312 this.isOneToOneOwner = this.isOneToOne && this.isOwning;
313 this.isOneToOneNotOwner = this.isOneToOne && !this.isOwning;
314 this.isManyToManyOwner = this.isManyToMany && this.isOwning;
315 this.isManyToManyNotOwner = this.isManyToMany && !this.isOwning;
316 this.isWithJoinColumn = this.isManyToOne || this.isOneToOneOwner;
317 };
318 /**
319 * Registers a given junction entity metadata.
320 * This builder method can be called after junction entity metadata for the many-to-many relation was created.
321 */
322 RelationMetadata.prototype.registerJunctionEntityMetadata = function (junctionEntityMetadata) {
323 this.junctionEntityMetadata = junctionEntityMetadata;
324 this.joinTableName = junctionEntityMetadata.tableName;
325 if (this.inverseRelation) {
326 this.inverseRelation.junctionEntityMetadata = junctionEntityMetadata;
327 this.joinTableName = junctionEntityMetadata.tableName;
328 }
329 };
330 /**
331 * Builds inverse side property path based on given inverse side property factory.
332 * This builder method should be used only after properties map of the inverse entity metadata was build.
333 */
334 RelationMetadata.prototype.buildInverseSidePropertyPath = function () {
335 if (this.givenInverseSidePropertyFactory) {
336 var ownerEntityPropertiesMap = this.inverseEntityMetadata.propertiesMap;
337 if (typeof this.givenInverseSidePropertyFactory === "function")
338 return this.givenInverseSidePropertyFactory(ownerEntityPropertiesMap);
339 if (typeof this.givenInverseSidePropertyFactory === "string")
340 return this.givenInverseSidePropertyFactory;
341 }
342 else if (this.isTreeParent && this.entityMetadata.treeChildrenRelation) {
343 return this.entityMetadata.treeChildrenRelation.propertyName;
344 }
345 else if (this.isTreeChildren && this.entityMetadata.treeParentRelation) {
346 return this.entityMetadata.treeParentRelation.propertyName;
347 }
348 return "";
349 };
350 /**
351 * Builds relation's property path based on its embedded tree.
352 */
353 RelationMetadata.prototype.buildPropertyPath = function () {
354 if (!this.embeddedMetadata || !this.embeddedMetadata.parentPropertyNames.length)
355 return this.propertyName;
356 return this.embeddedMetadata.parentPropertyNames.join(".") + "." + this.propertyName;
357 };
358 return RelationMetadata;
359}());
360exports.RelationMetadata = RelationMetadata;
361
362//# sourceMappingURL=RelationMetadata.js.map