1 | // Copyright IBM Corp. 2018. All Rights Reserved.
|
2 | // Node module: loopback-datasource-juggler
|
3 | // This file is licensed under the MIT License.
|
4 | // License text available at https://opensource.org/licenses/MIT
|
5 |
|
6 | import {Options} from './common';
|
7 | import {RelationDefinition} from './relation';
|
8 | import {PersistedModelClass} from './persisted-model';
|
9 |
|
10 | /**
|
11 | * Methods defined on this interface are mixed into a model class so that they
|
12 | * can be used to set up relations between models programmatically.
|
13 | */
|
14 | export interface RelationMixin {
|
15 | /**
|
16 | * Define a "one to many" relationship by specifying the model name.
|
17 | *
|
18 | * Examples:
|
19 | * ```
|
20 | * User.hasMany(Post, {as: 'posts', foreignKey: 'authorId'});
|
21 | * ```
|
22 | *
|
23 | * ```
|
24 | * Book.hasMany(Chapter);
|
25 | * ```
|
26 | * Or, equivalently:
|
27 | * ```
|
28 | * Book.hasMany('chapters', {model: Chapter});
|
29 | * ```
|
30 | *
|
31 | * Query and create related models:
|
32 | *
|
33 | * ```js
|
34 | * Book.create(function(err, book) {
|
35 | *
|
36 | * // Create a chapter instance ready to be saved in the data source.
|
37 | * var chapter = book.chapters.build({name: 'Chapter 1'});
|
38 | *
|
39 | * // Save the new chapter
|
40 | * chapter.save();
|
41 | *
|
42 | * // you can also call the Chapter.create method with the `chapters` property which will build a chapter
|
43 | * // instance and save the it in the data source.
|
44 | * book.chapters.create({name: 'Chapter 2'}, function(err, savedChapter) {
|
45 | * // this callback is optional
|
46 | * });
|
47 | *
|
48 | * // Query chapters for the book
|
49 | * book.chapters(function(err, chapters) {
|
50 | * // all chapters with bookId = book.id
|
51 | * console.log(chapters);
|
52 | * });
|
53 | *
|
54 | * // Query chapters for the book with a filter
|
55 | * book.chapters({where: {name: 'test'}, function(err, chapters) {
|
56 | * // All chapters with bookId = book.id and name = 'test'
|
57 | * console.log(chapters);
|
58 | * });
|
59 | * });
|
60 | * ```
|
61 | *
|
62 | * @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
63 | * @options {Object} params Configuration parameters; see below.
|
64 | * @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
65 | * @property {String} foreignKey Property name of foreign key field.
|
66 | * @property {String} polymorphic Define a polymorphic relation name.
|
67 | * @property {String} through Name of the through model.
|
68 | * @property {String} keyThrough Property name of the foreign key in the through model.
|
69 | * @property {Object|Function} scope Explicitly define additional scopes.
|
70 | * @property {Boolean} invert Specify if the relation is inverted.
|
71 | * @property {Object} model The model object.
|
72 | */
|
73 | hasMany(modelTo: PersistedModelClass, params?: Options): RelationDefinition;
|
74 |
|
75 | /**
|
76 | * Declare "belongsTo" relation that sets up a one-to-one connection with another model, such that each
|
77 | * instance of the declaring model "belongs to" one instance of the other model.
|
78 | *
|
79 | * For example, if an application includes users and posts, and each post can be written by exactly one user.
|
80 | * The following code specifies that `Post` has a reference called `author` to the `User` model via the `userId` property of `Post`
|
81 | * as the foreign key.
|
82 | * ```
|
83 | * Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
|
84 | * ```
|
85 | * You can then access the author in one of the following styles.
|
86 | * Get the User object for the post author asynchronously:
|
87 | * ```
|
88 | * post.author(callback);
|
89 | * ```
|
90 | * Get the User object for the post author synchronously:
|
91 | * ```
|
92 | * post.author();
|
93 | * ```
|
94 | * Set the author to be the given user:
|
95 | * ```
|
96 | * post.author(user)
|
97 | * ```
|
98 | * Examples:
|
99 | *
|
100 | * Suppose the model Post has a *belongsTo* relationship with User (the author of the post). You could declare it this way:
|
101 | * ```js
|
102 | * Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
|
103 | * ```
|
104 | *
|
105 | * When a post is loaded, you can load the related author with:
|
106 | * ```js
|
107 | * post.author(function(err, user) {
|
108 | * // the user variable is your user object
|
109 | * });
|
110 | * ```
|
111 | *
|
112 | * The related object is cached, so if later you try to get again the author, no additional request will be made.
|
113 | * But there is an optional boolean parameter in first position that set whether or not you want to reload the cache:
|
114 | * ```js
|
115 | * post.author(true, function(err, user) {
|
116 | * // The user is reloaded, even if it was already cached.
|
117 | * });
|
118 | * ```
|
119 | * This optional parameter default value is false, so the related object will be loaded from cache if available.
|
120 | *
|
121 | * @param {Class|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
122 | * @options {Object} params Configuration parameters; see below.
|
123 | * @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
124 | * @property {String} primaryKey Property name of primary key field.
|
125 | * @property {String} foreignKey Name of foreign key property.
|
126 | * @property {Object|Function} scope Explicitly define additional scopes.
|
127 | * @property {Object} properties Properties inherited from the parent object.
|
128 | * @property {Object} options Property level options.
|
129 | * @property {Boolean} options.invertProperties Specify if the properties should be inverted.
|
130 | */
|
131 | belongsTo(modelTo: PersistedModelClass, params?: Options): RelationDefinition;
|
132 |
|
133 | /**
|
134 | * A hasAndBelongsToMany relation creates a direct many-to-many connection with another model, with no intervening model.
|
135 | *
|
136 | * For example, if your application includes users and groups, with each group having many users and each user appearing
|
137 | * in many groups, you could declare the models this way:
|
138 | * ```
|
139 | * User.hasAndBelongsToMany('groups', {model: Group, foreignKey: 'groupId'});
|
140 | * ```
|
141 | * Then, to get the groups to which the user belongs:
|
142 | * ```
|
143 | * user.groups(callback);
|
144 | * ```
|
145 | * Create a new group and connect it with the user:
|
146 | * ```
|
147 | * user.groups.create(data, callback);
|
148 | * ```
|
149 | * Connect an existing group with the user:
|
150 | * ```
|
151 | * user.groups.add(group, callback);
|
152 | * ```
|
153 | * Remove the user from the group:
|
154 | * ```
|
155 | * user.groups.remove(group, callback);
|
156 | * ```
|
157 | *
|
158 | * @param {String|Object} modelTo Model object (or String name of model) to which you are creating the relationship.
|
159 | * @options {Object} params Configuration parameters; see below.
|
160 | * @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
161 | * @property {String} foreignKey Property name of foreign key field.
|
162 | * @property {String} throughTable The table name of the through model.
|
163 | * @property {String} through Name of the through model.
|
164 | * @property {String} polymorphic Define a polymorphic relation name.
|
165 | * @property {Object|Function} scope Explicitly define additional scopes.
|
166 | * @property {Object} model The model object.
|
167 | */
|
168 | hasAndBelongsToMany(
|
169 | modelTo: PersistedModelClass,
|
170 | params?: Options,
|
171 | ): RelationDefinition;
|
172 | /**
|
173 | * Define a "one to one" relationship by specifying the model name.
|
174 | *
|
175 | * Examples:
|
176 | * ```
|
177 | * Supplier.hasOne(Account, {as: 'account', foreignKey: 'supplierId'});
|
178 | * ```
|
179 | *
|
180 | * If the target model doesn’t have a foreign key property, LoopBack will add a property with the same name.
|
181 | *
|
182 | * The type of the property will be the same as the type of the target model’s id property.
|
183 | *
|
184 | * Please note the foreign key property is defined on the target model (in this example, Account).
|
185 | *
|
186 | * If you don’t specify them, then LoopBack derives the relation name and foreign key as follows:
|
187 | * - Relation name: Camel case of the model name, for example, for the “supplier” model the relation is “supplier”.
|
188 | * - Foreign key: The relation name appended with Id, for example, for relation name “supplier” the default foreign key is “supplierId”.
|
189 | *
|
190 | * Build a new account for the supplier with the supplierId to be set to the id of the supplier.
|
191 | * ```js
|
192 | * var supplier = supplier.account.build(data);
|
193 | * ```
|
194 | *
|
195 | * Create a new account for the supplier. If there is already an account, an error will be reported.
|
196 | * ```js
|
197 | * supplier.account.create(data, function(err, account) {
|
198 | * ...
|
199 | * });
|
200 | * ```
|
201 | *
|
202 | * Find the supplier's account model.
|
203 | * ```js
|
204 | * supplier.account(function(err, account) {
|
205 | * ...
|
206 | * });
|
207 | * ```
|
208 | *
|
209 | * Update the associated account.
|
210 | * ```js
|
211 | * supplier.account.update({balance: 100}, function(err, account) {
|
212 | * ...
|
213 | * });
|
214 | * ```
|
215 | *
|
216 | * Remove the account for the supplier.
|
217 | * ```js
|
218 | * supplier.account.destroy(function(err) {
|
219 | * ...
|
220 | * });
|
221 | * ```
|
222 | *
|
223 | * @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
224 | * @options {Object} params Configuration parameters; see below.
|
225 | * @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
226 | * @property {String} primaryKey Property name of primary key field.
|
227 | * @property {String} foreignKey Property name of foreign key field.
|
228 | * @property {String} polymorphic Define a polymorphic relation name.
|
229 | * @property {Object|Function} scope Explicitly define additional scopes.
|
230 | * @property {Object} model The model object.
|
231 | * @property {Object} properties Properties inherited from the parent object.
|
232 | * @property {Function} methods Scoped methods for the given relation.
|
233 | */
|
234 | hasOne(modelTo: PersistedModelClass, params?: Options): RelationDefinition;
|
235 |
|
236 | /**
|
237 | * References one or more instances of the target model.
|
238 | *
|
239 | * For example, a Customer model references one or more instances of the Account model.
|
240 | *
|
241 | * Define the relation in the model definition:
|
242 | *
|
243 | * - Definition of Customer model:
|
244 | * ```json
|
245 | * {
|
246 | "name": "Customer",
|
247 | "base": "PersistedModel",
|
248 | "idInjection": true,
|
249 | "properties": {
|
250 | "name": {
|
251 | "type": "string"
|
252 | },
|
253 | "age": {
|
254 | "type": "number"
|
255 | }
|
256 | },
|
257 | "validations": [],
|
258 | "relations": {
|
259 | "accounts": {
|
260 | "type": "referencesMany",
|
261 | "model": "Account",
|
262 | "foreignKey": "accountIds",
|
263 | "options": {
|
264 | "validate": true,
|
265 | "forceId": false
|
266 | }
|
267 | }
|
268 | },
|
269 | "acls": [],
|
270 | "methods": {}
|
271 | }
|
272 | * ```
|
273 | *
|
274 | * - Definition of Account model:
|
275 | * ```json
|
276 | * {
|
277 | "name": "Account",
|
278 | "base": "PersistedModel",
|
279 | "idInjection": true,
|
280 | "properties": {
|
281 | "name": {
|
282 | "type": "string"
|
283 | },
|
284 | "balance": {
|
285 | "type": "number"
|
286 | }
|
287 | },
|
288 | "validations": [],
|
289 | "relations": {},
|
290 | "acls": [],
|
291 | "methods": {}
|
292 | }
|
293 | * ```
|
294 | *
|
295 | * On the bootscript, create a customer instance and for that customer instance reference many account instances.
|
296 | *
|
297 | * For example:
|
298 | * ```javascript
|
299 | * var Customer = app.models.Customer;
|
300 | var accounts = [
|
301 | {
|
302 | name: 'Checking',
|
303 | balance: 5000
|
304 | },
|
305 | {
|
306 | name: 'Saving',
|
307 | balance: 2000
|
308 | }
|
309 | ];
|
310 | Customer.create({name: 'Mary Smith'}, function(err, customer) {
|
311 | console.log('Customer:', customer);
|
312 | async.each(accounts, function(account, done) {
|
313 | customer.accounts.create(account, done);
|
314 | }, function(err) {
|
315 | console.log('Customer with accounts:', customer);
|
316 | customer.accounts(console.log);
|
317 | cb(err);
|
318 | });
|
319 | });
|
320 | * ```
|
321 | *
|
322 | * Sample referencesMany model data:
|
323 | * ```javascript
|
324 | * {
|
325 | id: 1,
|
326 | name: 'John Smith',
|
327 | accounts: [
|
328 | "saving-01", "checking-01",
|
329 | ]
|
330 | }
|
331 | * ```
|
332 | *
|
333 | * Supported helper methods:
|
334 | * - customer.accounts()
|
335 | * - customer.accounts.create()
|
336 | * - customer.accounts.build()
|
337 | * - customer.accounts.findById()
|
338 | * - customer.accounts.destroy()
|
339 | * - customer.accounts.updateById()
|
340 | * - customer.accounts.exists()
|
341 | * - customer.accounts.add()
|
342 | * - customer.accounts.remove()
|
343 | * - customer.accounts.at()
|
344 | *
|
345 | * @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
346 | * @options {Object} params Configuration parameters; see below.
|
347 | * @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
348 | * @property {Any} default The default value.
|
349 | * @property {Object} options Options to specify for the relationship.
|
350 | * @property {Boolean} options.forceId Force generation of id for embedded items. Default is false.
|
351 | * @property {Boolean} options.validate Denote if the embedded items should be validated. Default is true.
|
352 | * @property {Boolean} options.persistent Denote if the embedded items should be persisted. Default is false.
|
353 | * @property {Object|Function} scope Explicitly define additional scopes.
|
354 | * @property {String} foreignKey Property name of foreign key field.
|
355 | * @property {Object} properties Properties inherited from the parent object.
|
356 | * @property {Function} methods Scoped methods for the given relation.
|
357 | */
|
358 | referencesMany(
|
359 | modelTo: PersistedModelClass,
|
360 | params?: Options,
|
361 | ): RelationDefinition;
|
362 |
|
363 | /**
|
364 | * Represent a model that embeds another model.
|
365 | *
|
366 | * For example, a Customer embeds one billingAddress from the Address model.
|
367 | *
|
368 | * - Define the relation in bootscript:
|
369 | * ```js
|
370 | * Customer.embedsOne(Address, {
|
371 | * as: 'address', // default to the relation name - address
|
372 | * property: 'billingAddress' // default to addressItem
|
373 | * });
|
374 | * ```
|
375 | *
|
376 | * OR, define the relation in the model definition:
|
377 | *
|
378 | * - Definition of Customer model:
|
379 | * ```json
|
380 | * {
|
381 | "name": "Customer",
|
382 | "base": "PersistedModel",
|
383 | "idInjection": true,
|
384 | "properties": {
|
385 | "name": {
|
386 | "type": "string"
|
387 | },
|
388 | "age": {
|
389 | "type": "number"
|
390 | }
|
391 | },
|
392 | "validations": [],
|
393 | "relations": {
|
394 | "address": {
|
395 | "type": "embedsOne",
|
396 | "model": "Address",
|
397 | "property": "billingAddress",
|
398 | "options": {
|
399 | "validate": true,
|
400 | "forceId": false
|
401 | }
|
402 | }
|
403 | },
|
404 | "acls": [],
|
405 | "methods": {}
|
406 | }
|
407 | * ```
|
408 | *
|
409 | * - Definition of Address model:
|
410 | * ```json
|
411 | * {
|
412 | "name": "Address",
|
413 | "base": "Model",
|
414 | "idInjection": true,
|
415 | "properties": {
|
416 | "street": {
|
417 | "type": "string"
|
418 | },
|
419 | "city": {
|
420 | "type": "string"
|
421 | },
|
422 | "state": {
|
423 | "type": "string"
|
424 | },
|
425 | "zipCode": {
|
426 | "type": "string"
|
427 | }
|
428 | },
|
429 | "validations": [],
|
430 | "relations": {},
|
431 | "acls": [],
|
432 | "methods": {}
|
433 | }
|
434 | * ```
|
435 | *
|
436 | * Sample embedded model data:
|
437 | * ```javascript
|
438 | * {
|
439 | id: 1,
|
440 | name: 'John Smith',
|
441 | billingAddress: {
|
442 | street: '123 Main St',
|
443 | city: 'San Jose',
|
444 | state: 'CA',
|
445 | zipCode: '95124'
|
446 | }
|
447 | }
|
448 | * ```
|
449 | *
|
450 | * Supported helper methods:
|
451 | * - customer.address()
|
452 | * - customer.address.build()
|
453 | * - customer.address.create()
|
454 | * - customer.address.update()
|
455 | * - customer.address.destroy()
|
456 | * - customer.address.value()
|
457 | *
|
458 | * @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
459 | * @options {Object} params Configuration parameters; see below.
|
460 | * @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
461 | * @property {String} property Name of the property for the embedded item.
|
462 | * @property {Any} default The default value.
|
463 | * @property {Object} options Options to specify for the relationship.
|
464 | * @property {Boolean} options.forceId Force generation of id for embedded items. Default is false.
|
465 | * @property {Boolean} options.validate Denote if the embedded items should be validated. Default is true.
|
466 | * @property {Boolean} options.persistent Denote if the embedded items should be persisted. Default is false.
|
467 | * @property {Object|Function} scope Explicitly define additional scopes.
|
468 | * @property {Object} properties Properties inherited from the parent object.
|
469 | * @property {Function} methods Scoped methods for the given relation.
|
470 | */
|
471 | embedsOne(modelTo: PersistedModelClass, params?: Options): RelationDefinition;
|
472 |
|
473 | /**
|
474 | * Represent a model that can embed many instances of another model.
|
475 | *
|
476 | * For example, a Customer can have multiple email addresses and each email address is a complex object that contains label and address.
|
477 | *
|
478 | * Define the relation code in bootscript:
|
479 | * ```javascript
|
480 | Customer.embedsMany(EmailAddress, {
|
481 | as: 'emails', // default to the relation name - emailAddresses
|
482 | property: 'emailList' // default to emailAddressItems
|
483 | });
|
484 | * ```
|
485 | *
|
486 | * OR, define the relation in the model definition:
|
487 | *
|
488 | * - Definition of Customer model:
|
489 | * ```json
|
490 | * {
|
491 | "name": "Customer",
|
492 | "base": "PersistedModel",
|
493 | "idInjection": true,
|
494 | "properties": {
|
495 | "name": {
|
496 | "type": "string"
|
497 | },
|
498 | "age": {
|
499 | "type": "number"
|
500 | }
|
501 | },
|
502 | "validations": [],
|
503 | "relations": {
|
504 | "emails": {
|
505 | "type": "embedsMany",
|
506 | "model": "EmailAddress",
|
507 | "property": "emailList",
|
508 | "options": {
|
509 | "validate": true,
|
510 | "forceId": false
|
511 | }
|
512 | }
|
513 | },
|
514 | "acls": [],
|
515 | "methods": {}
|
516 | }
|
517 | * ```
|
518 | *
|
519 | * - Definition of EmailAddress model:
|
520 | * ```json
|
521 | * {
|
522 | "name": "EmailAddress",
|
523 | "base": "Model",
|
524 | "idInjection": true,
|
525 | "properties": {
|
526 | "label": {
|
527 | "type": "string"
|
528 | },
|
529 | "address": {
|
530 | "type": "string"
|
531 | }
|
532 | },
|
533 | "validations": [],
|
534 | "relations": {},
|
535 | "acls": [],
|
536 | "methods": {}
|
537 | }
|
538 | * ```
|
539 | *
|
540 | * Sample embedded model data:
|
541 | * ```javascript
|
542 | * {
|
543 | id: 1,
|
544 | name: 'John Smith',
|
545 | emails: [{
|
546 | label: 'work',
|
547 | address: 'john@xyz.com'
|
548 | }, {
|
549 | label: 'home',
|
550 | address: 'john@gmail.com'
|
551 | }]
|
552 | }
|
553 | * ```
|
554 | *
|
555 | * Supported helper methods:
|
556 | * - customer.emails()
|
557 | * - customer.emails.create()
|
558 | * - customer.emails.build()
|
559 | * - customer.emails.findById()
|
560 | * - customer.emails.destroyById()
|
561 | * - customer.emails.updateById()
|
562 | * - customer.emails.exists()
|
563 | * - customer.emails.add()
|
564 | * - customer.emails.remove()
|
565 | * - customer.emails.at()
|
566 | * - customer.emails.value()
|
567 | *
|
568 | * @param {Object|String} modelTo Model object (or String name of model) to which you are creating the relationship.
|
569 | * @options {Object} params Configuration parameters; see below.
|
570 | * @property {String} as Name of the property in the referring model that corresponds to the foreign key field in the related model.
|
571 | * @property {String} property Name of the property for the embedded item.
|
572 | * @property {Any} default The default value.
|
573 | * @property {Object} options Options to specify for the relationship.
|
574 | * @property {Boolean} options.forceId Force generation of id for embedded items. Default is false.
|
575 | * @property {Boolean} options.validate Denote if the embedded items should be validated. Default is true.
|
576 | * @property {Boolean} options.persistent Denote if the embedded items should be persisted. Default is false.
|
577 | * @property {String} polymorphic Define a polymorphic relation name.
|
578 | * @property {Object|Function} scope Explicitly define additional scopes.
|
579 | * @property {Object} properties Properties inherited from the parent object.
|
580 | * @property {Function} methods Scoped methods for the given relation.
|
581 | */
|
582 | embedsMany(
|
583 | modelTo: PersistedModelClass,
|
584 | params?: Options,
|
585 | ): RelationDefinition;
|
586 | }
|