UNPKG

8.17 kBJavaScriptView Raw
1/**
2 * @copyright Copyright (c) 2019 Maxim Khorin <maksimovichu@gmail.com>
3 */
4'use strict';
5
6const Base = require('../base/Base');
7
8module.exports = class ActiveLinker extends Base {
9
10 async link (name, model, extraColumns) {
11 const relation = this.owner.getRelation(name);
12 const method = relation.isOuterLink() ? 'linkVia' : 'linkInternal';
13 await this[method](relation, model, extraColumns);
14 if (!relation.isMultiple()) {
15 this.owner.populateRelation(name, model);
16 return PromiseHelper.setImmediate();
17 }
18 const related = this.owner.getRelated(name);
19 if (!related) {
20 return PromiseHelper.setImmediate();
21 }
22 const index = relation.getIndex();
23 if (index) {
24 related[index] = model;
25 } else {
26 related.push(model);
27 }
28 return PromiseHelper.setImmediate();
29 }
30
31 linkInternal (relation, model) {
32 return relation.isBackRef()
33 ? this.bindModels(relation.refKey, relation.linkKey, model, this.owner, relation)
34 : this.bindModels(relation.linkKey, relation.refKey, this.owner, model, relation);
35 }
36
37 linkVia (relation, model, extraColumns) {
38 const via = relation.getViaTable() || relation.getViaRelation();
39 const columns = {
40 [via.refKey]: this.owner.get(via.linkKey),
41 [relation.linkKey]: model.get(relation.refKey)
42 };
43 if (extraColumns) {
44 Object.assign(columns, extraColumns);
45 }
46 if (relation.getViaTable()) {
47 return this.owner.getDb().insert(via.getTable(), columns);
48 }
49 // unset relation so that it can be reloaded to reflect the change
50 this.owner.unsetRelated(relation.getViaRelationName());
51 const viaModel = this.owner.spawn(via.model.constructor);
52 viaModel.assign(columns);
53 return viaModel.insert();
54 }
55
56 linkViaModel (relation, targets, model) {
57 const via = relation.getViaRelation();
58 if (!model) {
59 model = this.owner.spawn(via.model.constructor);
60 } else if (!(model instanceof via.model.constructor)) {
61 throw new Error(this.wrapMessage('Invalid link model'));
62 }
63 model.set(relation.linkKey, this.owner.get(relation.refKey));
64 model.set(via.refKey, this.owner.get(via.linkKey));
65 return model.save();
66 }
67
68 async unlink (name, model, deleted) {
69 const relation = this.owner.getRelation(name);
70 if (deleted === undefined) {
71 deleted = relation.getDeleteOnUnlink();
72 }
73 const method = relation.isOuterLink() ? 'unlinkVia' : 'unlinkInternal';
74 await this[method](relation, model, deleted);
75 this.unsetUnlinked(name, model, relation);
76 return PromiseHelper.setImmediate();
77 }
78
79 async unlinkInternal (relation, model, deleted) {
80 const ref = model.get(relation.refKey);
81 const link = this.owner.get(relation.linkKey);
82 relation.isBackRef()
83 ? await QueryHelper.unlinkInternal(ref, link, model, relation.refKey)
84 : await QueryHelper.unlinkInternal(link, ref, this.owner, relation.linkKey);
85 return deleted ? model.delete() : null;
86 }
87
88 unlinkVia (relation, model, deleted) {
89 const via = relation.getViaTable() || relation.getViaRelation();
90 const condition = {
91 [via.refKey]: this.owner.get(via.linkKey),
92 [relation.linkKey]: model.get(relation.refKey)
93 };
94 const nulls = {
95 [via.refKey]: null,
96 [relation.linkKey]: null
97 };
98 if (deleted === undefined) {
99 deleted = via.getDeleteOnUnlink();
100 }
101 if (relation.getViaTable()) {
102 return deleted
103 ? this.owner.getDb().delete(via.getTable(), condition)
104 : this.owner.getDb().update(via.getTable(), condition, nulls);
105 }
106 this.owner.unsetRelated(relation.getViaRelationName());
107 return deleted
108 ? via.model.find(condition).delete()
109 : via.model.find(condition).updateAll(nulls);
110 }
111
112 async unlinkAll (name, deleted) {
113 const relation = this.owner.getRelation(name);
114 if (!relation) {
115 return false;
116 }
117 if (deleted === undefined) {
118 deleted = relation.getDeleteOnUnlink();
119 }
120 const method = relation.isOuterLink() ? 'unlinkViaAll' : 'unlinkInternalAll';
121 await this[method](relation, deleted);
122 this.owner.unsetRelated(name);
123 }
124
125 async unlinkViaAll (relation, deleted) {
126 if (relation.getViaRelation()) {
127 this.owner.unsetRelated(relation.getViaRelationName());
128 }
129 const via = relation.getViaTable() || relation.getViaRelation();
130 let condition = {[via.refKey]: this.owner.get(via.linkKey)};
131 if (via.getWhere()) {
132 condition = ['AND', condition, via.getWhere()];
133 }
134 let nulls = {[via.refKey]: null};
135 if (relation.getViaTable()) {
136 condition = this.owner.getDb().buildCondition(condition);
137 deleted ? await this.owner.getDb().delete(via.getTable(), condition)
138 : await this.owner.getDb().update(via.getTable(), condition, nulls);
139 } else if (deleted) {
140 for (const model of await via.model.find(condition).all()) {
141 await model.delete();
142 }
143 } else {
144 await via.model.find(condition).updateAll(nulls);
145 }
146 }
147
148 async unlinkInternalAll (relation, deleted) {
149 // relation via array valued attr
150 if (!deleted && Array.isArray(this.owner.get(relation.linkKey))) {
151 this.owner.set(relation.linkKey, []);
152 return this.owner.forceSave();
153 }
154 let condition = {[relation.refKey]: this.owner.get(relation.linkKey)};
155 if (relation.getWhere()) {
156 condition = ['AND', condition, relation.getWhere()];
157 }
158 const nulls = {[relation.refKey]: null};
159 if (deleted) {
160 for (const model of await relation.all()) {
161 await model.delete();
162 }
163 } else if (relation.getViaArray()) {
164 await relation.model.getDb().updateAllPull(relation.model.getTable(), {}, condition);
165 } else {
166 await relation.model.find(condition).updateAll(nulls);
167 }
168 }
169
170 unsetUnlinked (name, model, relation) {
171 if (!relation.isMultiple()) {
172 return this.owner.unsetRelated(name);
173 }
174 const models = this.owner.getRelated(name);
175 if (Array.isArray(models)) {
176 for (let i = models.length - 1; i >= 0; --i) {
177 if (CommonHelper.isEqual(model.getId(), models[i].getId())) {
178 models.splice(i, 1);
179 }
180 }
181 }
182 }
183
184 bindModels (foreignKey, primaryKey, foreignModel, primaryModel, relation) {
185 const value = primaryModel.get(primaryKey);
186 if (!value) {
187 throw new Error(this.wrapMessage('Primary key is null'));
188 }
189 if (!relation.getViaArray()) {
190 foreignModel.set(foreignKey, value);
191 return foreignModel.forceSave();
192 }
193 if (!Array.isArray(foreignModel.get(foreignKey))) {
194 foreignModel.set(foreignKey, []);
195 }
196 if (!ArrayHelper.includes(value, foreignModel.get(foreignKey))) {
197 foreignModel.get(foreignKey).push(value);
198 return foreignModel.forceSave();
199 }
200 }
201
202 wrapMessage (message) {
203 return `${this.constructor.name}: ${this.owner.wrapMessage(message)}`;
204 }
205};
206
207const ArrayHelper = require('../helper/ArrayHelper');
208const CommonHelper = require('../helper/CommonHelper');
209const PromiseHelper = require('../helper/PromiseHelper');
210const QueryHelper = require('../helper/QueryHelper');
\No newline at end of file