UNPKG

10.8 kBJavaScriptView Raw
1'use strict';
2var QueryObject = require('./query-object');
3var Relationship = require('./relationship');
4
5function ModelFactory() {
6 this.ORM = null;
7 this.Model = null;
8 this.tableName = '';
9 this.attributes = {};
10 this.relationships = {};
11 this.pre = {};
12 this.post = {};
13 this.static = {};
14 this.method = {};
15}
16
17//
18// Model Operations
19//
20
21ModelFactory.prototype.extend = function(ORM, tableName, data) {
22 this.ORM = ORM;
23 this.tableName = tableName;
24 this.Model = this.createModel(data.method);
25 this.addPre(data.pre);
26 this.addPost(data.post);
27 this.addStatic(data.static);
28 this.addTableAttributes(data.attributes);
29 console.log('Creating ModelFactory: ', this.tableName, '\n');
30 return this;
31};
32
33ModelFactory.prototype.createModel = function(methods) {
34 //
35 // Create base
36 var Model = function() {};
37 //
38 // Add methods to model prototype
39 for(var method in methods) {
40 Model.prototype[method] = methods[method];
41 }
42 //
43 // Return
44 return Model;
45};
46
47ModelFactory.prototype.addPre = function(pre) {
48 for(var method in pre) {
49 this.pre[method] = pre[method];
50 }
51};
52
53ModelFactory.prototype.addPost = function(post) {
54 for(var method in post) {
55 this.post[method] = post[method];
56 }
57};
58
59ModelFactory.prototype.addStatic = function(statics) {
60 for(var method in statics) {
61 this[method] = statics[method];
62 }
63};
64
65ModelFactory.prototype.addTableAttributes = function(attributes) {
66 for(var attribute in attributes) {
67 switch(typeof attributes[attribute]) {
68 case 'object': {
69 this.attributes[attribute] = attributes[attribute];
70 break;
71 }
72 case 'function': {
73 this.relationships[attribute] = attributes[attribute](this);
74 break;
75 }
76 }
77 }
78};
79
80//
81// Model Instance Operations
82//
83
84ModelFactory.prototype.initializeModel = function(data, created) {
85 var model = new this.Model();
86 // Set all attributes
87 for(var attribute in data) {
88 model[attribute] = data[attribute];
89 }
90 if(created) {
91 model.created = created;
92 }
93 // Set all model methods
94 return model;
95};
96
97ModelFactory.prototype.initializeModels = function(data) {
98 var self = this;
99 var models = data.map(function(d) {
100 return self.initializeModel(d);
101 });
102 return models;
103};
104
105//
106// Attribute Operations
107//
108
109ModelFactory.prototype.getReturnAttributes = function() {
110 // TEMP: Disable returning until I figure out a way to exclude instead of
111 // list what to include
112 /*
113 var returnAttributes = [];
114 for(var attribute in this.attributes) {
115 if(!this.attributes[attribute].private) {
116 returnAttributes.push(attribute);
117 }
118 }
119 */
120 var returnAttributes = ['*'];
121 return returnAttributes;
122};
123
124//
125// Relationship Operations
126//
127
128//
129// Private
130//
131
132// TODO: Make private
133ModelFactory.prototype.includeForMany = function(models, include) {
134 return Promise.all(models.map((model) => this.includeForOne(model, include), this));
135};
136
137// TODO: Make private
138ModelFactory.prototype.includeForOne = function(model, include) {
139 var self = this;
140 return new Promise((resolve, reject) => {
141 var promises = [];
142 include.forEach(function(item) {
143 let relationship = self.relationships[item.relation];
144 //
145 // Multi level include
146 let promise;
147 if(item.include) {
148 promise = relationship.include(item.relation, model).then((newModel) => {
149 //
150 // Get current tree level model
151 let modelFactory = self.ORM.models[relationship.modelFactoryName];
152 let models = newModel[item.relation];
153 //
154 // Remove current tree level and include for next set
155 if(models.length) {
156 return modelFactory.includeForMany(models, item.include);
157 }
158 else if(!models.length){
159 return modelFactory.includeForOne(models, item.include);
160 }
161 else {
162 return;
163 }
164 });
165 }
166 //
167 // Single level include
168 else {
169 promise = relationship.include(item.relation, model);
170 }
171 promises.push(promise);
172 });
173 Promise.all(promises).then(function(values) {
174 resolve(model);
175 });
176 });
177 return Promise.resolve(model);
178};
179
180//
181// Query Operations
182//
183
184ModelFactory.prototype.hook = function(hook, data) {
185 return new Promise(function(resolve, reject) {
186 if(hook) {
187 hook(data, resolve);
188 } else {
189 resolve(data);
190 }
191 });
192};
193
194ModelFactory.prototype.create = function(where, options) {
195 var model, query, initial;
196 // TODO: Refactor transaction flow
197 // Start transaction
198 if(!options) {
199 options = {};
200 if(!options.transaction) {
201 initial = true;
202 options.transaction = this.ORM.querier.transaction();
203 options.table = this.tableName;
204 }
205 }
206 return options.transaction
207 .then(() => {
208 return this.hook(this.pre.create, where);
209 })
210 // Pre
211 .then((newWhere) => {
212 where = Object.assign({}, where, newWhere);
213 query = new QueryObject(this.tableName, where, this.relationships, options);
214 })
215 // Create belongs to one
216 .then(() => {
217 return query.createBelongsToOne();
218 })
219 // Create belongs to many
220 .then(() => {
221 return query.createBelongsToMany();
222 })
223 // Attach belongs to one
224 .then(() => {
225 return query.attachBelongsToOne();
226 })
227 // Create belongs to one
228 .then(() => {
229 return this.ORM.query.create({
230 ORM: this.ORM,
231 table: query.getTable(),
232 data: query.getAttributes(),
233 returning: this.getReturnAttributes()
234 });
235 })
236 // Attach
237 .then((rawData) => {
238 model = this.initializeModel(rawData[0], true);
239 })
240 // Attach belongs to many
241 .then(() => {
242 return query.attachBelongsToMany(model, options);
243 })
244 // Create has one
245 .then(() => {
246 return query.createHasOne(model);
247 })
248 // Create has many
249 .then(() => {
250 return query.createHasMany(model);
251 })
252 // Concat relationships
253 .then(() => {
254 model = query.concatCreatedRelationships(model);
255 })
256 // Post
257 .then(() => {
258 return this.hook(this.post.create, model);
259 })
260 .then(() => {
261 if(initial) {
262 return this.ORM.querier.commit();
263 } else {
264 return Promise.resolve();
265 }
266 })
267 // Return
268 .then(() => {
269 return model;
270 })
271 // Error
272 .catch((error) => {
273 return this.ORM.querier.rollback().then(() => {
274 console.error(error);
275 return error;
276 });
277 });
278
279};
280
281ModelFactory.prototype.update = function(where, data, options) {
282 return this.find(where).then((models) => {
283 return Promise.all(models.map((model) => this.hook(this.pre.update, model)));
284 })
285 .then(() => {
286 // Create Query
287 var queryData = new QueryObject(this.tableName, data, this.relationships, options);
288 var queryWhere = new QueryObject(this.tableName, where, this.relationships, options);
289 // Make search
290 return this.ORM.query.update({
291 ORM: this.ORM,
292 table: queryWhere.getTable(),
293 where: queryWhere.getAttributes(),
294 data: queryData.getAttributes(),
295 returning: this.getReturnAttributes()
296 });
297 })
298 .then((models) => {
299 return Promise.all(models.map((model) => this.hook(this.post.update, model)));
300 });
301};
302
303ModelFactory.prototype.find = function(where, options) {
304 // Create Query
305 var query = new QueryObject(this.tableName, where, this.relationships, options);
306 // Make search
307 return this.ORM.query.find({
308 ORM: this.ORM,
309 table: query.getTable(),
310 join: query.getJoin(),
311 where: query.getAttributes(),
312 relationships: query.getRelationships(),
313 options: query.getOptions(),
314 returning: this.getReturnAttributes()
315 })
316 // Initialize model
317 .then((rawData) => {
318 return this.initializeModels(rawData);
319 })
320 // Include relationships
321 .then((models) => {
322 return (options && options.include) ?
323 this.includeForMany(models, options.include) :
324 models;
325 });
326};
327
328ModelFactory.prototype.findOne = function(where, options) {
329 options = Object.assign({}, options, { limit: 1 });
330 return this.find(where, options).then((models) => {
331 return models[0];
332 });
333};
334
335ModelFactory.prototype.findOrCreate = function(where, options) {
336 options = Object.assign({}, options, { limit: 1 });
337 return this.findOne(where, options).then((model) => {
338 if(model) {
339 return model;
340 } else {
341 return this.create(where);
342 }
343 });
344};
345
346ModelFactory.prototype.delete = function(where, options) {
347 return this.find(where).then((models) => {
348 return Promise.all(models.map((model) => this.hook(this.pre.delete, model)));
349 })
350 .then(() => {
351 // Create Query
352 var query = new QueryObject(this.tableName, where, this.relationships, options);
353 // Make search
354 return this.ORM.query.delete({
355 ORM: this.ORM,
356 table: query.getTable(),
357 where: query.getAttributes(),
358 returning: this.getReturnAttributes()
359 });
360 })
361 .then((models) => {
362 return Promise.all(models.map((model) => this.hook(this.post.delete, model)));
363 });
364};
365
366//
367// Relationship
368//
369
370ModelFactory.prototype.belongsToOne = function(modelFactoryName, parentIdentifer, childIdentifer) {
371 return new Relationship({
372 ORM: this.ORM,
373 tableName: this.tableName,
374 type: Relationship.Types.belongsToOne,
375 modelFactoryName: modelFactoryName,
376 parentIdentifer: parentIdentifer,
377 childIdentifer: childIdentifer
378 });
379};
380
381ModelFactory.prototype.belongsToMany = function(modelFactoryName, throughTableName, parentIdentifer, childIdentifer) {
382 return new Relationship({
383 ORM: this.ORM,
384 tableName: this.tableName,
385 throughTableName: throughTableName,
386 type: Relationship.Types.belongsToMany,
387 modelFactoryName: modelFactoryName,
388 parentIdentifer: parentIdentifer,
389 childIdentifer: childIdentifer
390 });
391};
392
393ModelFactory.prototype.hasMany = function(modelFactoryName, parentIdentifer, childIdentifer) {
394 return new Relationship({
395 ORM: this.ORM,
396 tableName: this.tableName,
397 type: Relationship.Types.hasMany,
398 modelFactoryName: modelFactoryName,
399 parentIdentifer: parentIdentifer,
400 childIdentifer: childIdentifer
401 });
402};
403
404ModelFactory.prototype.hasOne = function(modelFactoryName, parentIdentifer, childIdentifer) {
405 return new Relationship({
406 ORM: this.ORM,
407 tableName: this.tableName,
408 type: Relationship.Types.hasOne,
409 modelFactoryName: modelFactoryName,
410 parentIdentifer: parentIdentifer,
411 childIdentifer: childIdentifer
412 });
413};
414
415module.exports = ModelFactory;
\No newline at end of file