I have an Order model which hasMany OrderItem models. But once a client wants to create an Order, it has to create an Order object first then for each product he added to his basket, he needs to create responding OrderItems separately. As you may notice it causes many reduntant requests. May be I can make a custom method for OrderItems which consumes a product list. But i was wondering if there is a built in mechanism for this like createMany since it is a very useful operation.
ORDER MODEL
{
"name": "Order",
"plural": "Orders",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"customerId": {
"type": "number",
"required": true
},
"branchId": {
"type": "number",
"required": true
}
},
"validations": [],
"relations": {
"orderItems": {
"type": "hasMany",
"model": "OrderItem",
"foreignKey": "orderId"
}
},
"acls": [],
"methods": []
}
ORDERITEM MODEL
{
"name": "OrderItem",
"plural": "OrderItems",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"UnitPrice": {
"type": "number"
},
"productId": {
"type": "number",
"required": true
},
"purchaseOrderId": {
"type": "number",
"required": true
},
"quantity": {
"type": "number"
}
},
"validations": [],
"relations": {
"product": {
"type": "belongsTo",
"model": "Product",
"foreignKey": "productId"
},
"purchaseOrder": {
"type": "belongsTo",
"model": "PurchaseOrder",
"foreignKey": ""
}
},
"acls": [],
"methods": []
}
Loopback "create" method accepts also an array of objects (see PersistedModel.create docs) so you should try creating one "create" call and send an array of OrderItems.
Related
I am using Loopback 3 and i have the following three models:
UserFile.json
{
"name": "UserFile",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"id": {
"type": "number",
"id": true,
"generated": true
},
"name": {
"type": "string",
"required": true
},
"size": {
"type": "number",
"default": 0
},
"uploadedAt": {
"type": "date",
"default": "$now"
}
},
"relations": {
"hasFile": {
"type": "hasMany",
"model": "UploadedFile",
"foreignKey": "fileId"
}
}
}
UploadedFile.json
{
"name": "UploadedFile",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"hashId": {
"type": "string",
"required": true
},
......
},
"relations": {
"file": {
"type": "belongsTo",
"model": "UserFile",
"foreignKey": "fileId"
}
}
}
PartitionedFile.json
{
"name": "PartitionedFile",
"base": "UploadedFile",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"partSize": {
"type": "number",
"default": 0
}
},
"relations": {
"file": {
"type": "belongsTo",
"model": "UserFile",
"foreignKey": "fileId"
}
}
}
The idea is that the UserFile has either one UploadedFile or (two or more) many PartitionedFile.
I want to be able to get from the UserFile, from the same relation the file, whether it is an UploadedFile or a PartitionedFile. Is it possible to achieve this just from the models' definitions or it can only be done with a remote method?
A relation from a model to another is bound to only one model; so you cannot have one relation called "file" to point to different models. In your example, it's bound to UploadedFile. In order for your model UserFile to have relations with both UploadedFile and PartitionedFile, you'll need two different relations.
For your UserFile to have either one UploadedFile or many PartitionedFile, it needs to have them both within its relations:
{
"name": "UserFile",
...
"relations": {
"uploadedFiles": {
"type": "hasOne", // Zero on one uploaded file
"model": "UploadedFile",
"foreignKey": "uploadedFileId"
},
"partitionedFiles": {
"type": "hasMany", // Zero or many partitioned files
"model": "PartitionedFile"
}
}
}
Then, to retrieve files from both UploadedFiles and PartitionedFiles through UserFile, you don't need a remote, but to include the models within your request.
For example, in ReactJS, it would look like this:
const response = await Axios.get(`/api/UserFile`, {
params: {
filter: {
include: [
{ relation: 'uploadedFiles' },
{ relation: 'partitionedFiles' },
]
}
}
}
Another solution would be to specify another model File that contains all these relations (UploadedFile, PartitionedFile, etc), so that the model UserFile can refer to the table File as one unique relation to retrieve the files...
I am having a nearly identical problem to my previous question. When a user model is created, the following error is returned:
ReferenceError: g is not defined
at new ModelConstructor (eval at createModelClassCtor (/usr/src/app/node_modules/loopback-datasource-juggler/lib/model-builder.js:671:21), <anonymous>:10:27)
at user.ModelBaseClass._initProperties (/usr/src/app/node_modules/loopback-datasource-juggler/lib/model.js:349:28)
at user.ModelBaseClass (/usr/src/app/node_modules/loopback-datasource-juggler/lib/model.js:60:8)
at user.Model (eval at createModelClassCtor (/usr/src/app/node_modules/loopback-datasource-juggler/lib/model-builder.js:671:21), <anonymous>:12:24)
at user.PersistedModel (eval at createModelClassCtor (/usr/src/app/node_modules/loopback-datasource-juggler/lib/model-builder.js:671:21), <anonymous>:12:24)
at user.User (eval at createModelClassCtor (/usr/src/app/node_modules/loopback-datasource-juggler/lib/model-builder.js:671:21), <anonymous>:12:24)
at new user (eval at createModelClassCtor (/usr/src/app/node_modules/loopback-datasource-juggler/lib/model-builder.js:671:21), <anonymous>:12:24)
at Function.DataAccessObject.create (/usr/src/app/node_modules/loopback-datasource-juggler/lib/dao.js:359:13)
at /usr/src/app/node_modules/loopback-datasource-juggler/lib/dao.js:1262:13
at /usr/src/app/node_modules/loopback-datasource-juggler/lib/dao.js:2175:62
at /usr/src/app/node_modules/loopback-datasource-juggler/lib/dao.js:2111:9
at /usr/src/app/node_modules/loopback-datasource-juggler/node_modules/async/dist/async.js:1012:9
at /usr/src/app/node_modules/loopback-datasource-juggler/node_modules/async/dist/async.js:359:16
at eachOfArrayLike (/usr/src/app/node_modules/loopback-datasource-juggler/node_modules/async/dist/async.js:928:9)
at eachOf (/usr/src/app/node_modules/loopback-datasource-juggler/node_modules/async/dist/async.js:990:5)
at _asyncMap (/usr/src/app/node_modules/loopback-datasource-juggler/node_modules/async/dist/async.js:1005:5)
at Object.map (/usr/src/app/node_modules/loopback-datasource-juggler/node_modules/async/dist/async.js:995:16)
at allCb (/usr/src/app/node_modules/loopback-datasource-juggler/lib/dao.js:2025:13)
at /usr/src/app/node_modules/loopback-connector-mongodb/lib/mongodb.js:1155:9
at result (/usr/src/app/node_modules/mongodb/lib/utils.js:414:17)
at executeCallback (/usr/src/app/node_modules/mongodb/lib/utils.js:406:9)
at handleCallback (/usr/src/app/node_modules/mongodb/lib/utils.js:128:55)
Here are my models:
user.json:
{
"name": "user",
"plural": "users",
"base": "User",
"idInjection": false,
"options": {
"validateUpsert": true
},
"properties": {
"id": {
"type" : "string",
"id" : true,
"required" : true,
"defaultFn" : "guid"
},
"type": {
"type" : "[string]",
"required" : true,
"default" : ["student"]
},
"full_name": {
"type": "string",
"required": false
},
"office" : {
"type": "string",
"required" : false
},
"profile_img": {
"type": "string",
"required": false
},
"departmentId": {
"type": "string",
"required": false
}
},
"validations": [],
"relations": {
"department": {
"type": "belongsTo",
"model": "department"
},
"syncedcalendar" : {
"type" : "hasMany",
"model" : "syncedcalendar"
},
"accessTokens": {
"type": "hasMany",
"model": "accessToken",
"foreignKey": "userId",
"options": {
"disableInclude": true
}
}
},
"acls": [],
"methods": {}
}
accessToken.json
{
"name": "accessToken",
"plural": "accessTokens",
"base": "AccessToken",
"properties": {},
"validations": [],
"idInjection": false,
"relations": {
"user": {
"type": "belongsTo",
"model": "user",
"foreignKey": "userId"
}
},
"acls": [],
"methods": []
}
The error above (in both this case and my previous issue) resulted because loopback could not parse the model's json file. In this case, it was the type property of user:
"type": {
"type" : "[string]",
"required" : true,
"default" : ["student"]
},
It seems the default cannot be an array. Removing this default and enforcing this default in a hook resolved the issue.
I went through an issue that resembled yours. And yes, the error stack gives too little useful info. It should instead say: "please stick to Loopback types" because that's the problem. You're stuck with Loopback types.
In your case, the parser couldn not handle an array. Well, it turns out it won't parse an un recognized type string either. For example, if you try to declare a property as integer:
"id":
{
"type": "Integer", <----- not a loopback type
"id": 1,
"mysql":
{
"columnName": "id",
"dataType": "int",
}
},
It will throw, because the native type is Number. You can format it as integer, but don't mess with its type.
"id":
{
"type": "Number", <---- one of loopback types. Integer is not one of them
"id": 1,
"mysql":
{
"columnName": "id",
"dataType": "int",
}
"format":"integer" <---- here's where you can format or cast as desired
},
If your database values are json-like (so they should rehidrate as arrays and objects) just declare them as Objects.
I have the following model in a loopback application, that will be persisted in a MongoDB:
Model
Name Coffeshop:
Id
Name (string)
City (String)
Question:
Now i want to be able to store a list of strings in a new property called "tags":
Tags (Array of string)
There is no relation to other models necessary. I need just a plain flat list of strings.
How can i achieve this?
Code:
{
"name": "CoffeeShop",
"plural": "CoffeeShops",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
},
"city": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
Thats easy:
{
"name": "CoffeeShop",
"plural": "CoffeeShops",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
},
"city": {
"type": "string",
"required": true
},
"tags": {
"type": [
"string"
],
"required": false
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
I have a contact db table and model. Employee model inherits from contact.
If i do GET employees/ it returns all the contacts.
How should I set up my employee.json if I want to return only the contacts with partnerId = 1?
{
"name": "employee",
"base": "contact",
"strict": false,
"idInjection": false,
"options": {
"validateUpsert": true,
"postgresql": {
"schema": "public",
"table": "contact"
}
},
"scope": {
"where": {
"partnerId": 1
}
},
//...
}
Debug says calling GET employees/ makes the following query:
SELECT "name", "position", "email", "password", "id" FROM "public"."contact" ORDER BY "id"
It does not seem that scope is added.
models/partner.json
{
"name": "partner",
// ...
"properties": {
"name": {
"type": "string",
"required": true
},
// ...
},
"validations": [],
"relations": {
"contacts": {
"type": "hasMany",
"model": "contact"
}
//...
},
"acls": [],
"methods": {}
}
Try using the where filter, either in the REST API
/employees?filter[where][partnerId]=1
or in your Employee.js
Employee.find({ where: {partnerId:1} });
https://docs.strongloop.com/display/APIC/Where+filter
I have a couple relations I want to model,
A Message belongs to 2 Profile's (a sender and a recipient)
An Enrollment consists of a Profile and a Curriculum.
I tried to do #1 using 2 hasOne but ended up with Profile.messageId.
{
"name": "Message",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"id": {
"type": "number",
"id": true,
"required": true
},
"text": {
"type": "string",
"required": true
},
"created": {
"type": "date",
"required": true
},
"seen": {
"type": "boolean",
"required": true
}
},
"validations": [],
"relations": {
"sender": {
"type": "hasOne",
"model": "Profile",
"foreignKey": ""
},
"recipient": {
"type": "hasOne",
"model": "Profile",
"foreignKey": ""
}
},
"acls": [],
"methods": []
}
Same problem w/ #2...
{
"name": "Enrollment",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"id": {
"type": "number",
"id": true,
"required": true
},
"created": {
"type": "date",
"required": true
},
"currentPage": {
"type": "string",
"comments": "What page are they on in this curriculum?"
}
},
"validations": [],
"relations": {
"curriculums": {
"type": "hasOne",
"model": "Curriculum",
"foreignKey": ""
},
"profiles": {
"type": "hasOne",
"model": "Profile",
"foreignKey": ""
}
},
"acls": [],
"methods": []
}