1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | var tslib_1 = require("tslib");
|
4 | var EntityMetadata_1 = require("./EntityMetadata");
|
5 | /**
|
6 | * Contains all information about some entity's relation.
|
7 | */
|
8 | var 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 | }());
|
360 | exports.RelationMetadata = RelationMetadata;
|
361 |
|
362 | //# sourceMappingURL=RelationMetadata.js.map
|