MentalGrinds
MentalGrinds

Reputation: 71

loopback save related hasmany models in single request

I have two models that are related through a hasMany relationship.

Customer hasMany CustomerPhones

When creating a new Customer, I would like to pass the related CustomerPhones as part a single request. This seems like a common need, if the approach I am looking to implement in wrong, what is the preferred way of doing this?

This is the url for creating a customer: POST /api/Customers

The request for above url would be req.body

{
  "name": "Foo",
  "customerPhones": [
    { "phoneNumber": "8085551234" },
    { "phoneNumber": "8085554567" }
  ]
}

Loopback models configurations:

Customer.json

{
  "name": "Customer",
  "base": "User",
  "properties": {
    "name": {
      "type": "string",
      "required": true
    }
  },
  "relations": {
    "customerPhones": {
      "type": "hasMany",
      "model": "CustomerPhone",
      "foreignKey": ""
    }
  }
}

CustomerPhone.json

{
  "name": "CustomerPhone",
  "base": "PersistedModel",
  "properties": {
    "phoneNumber": {
      "type": "string",
      "required": true
    },
    "customerId": {
      "type": "number",
      "required": true
    }
  },
  "relations": {
    "customer": {
      "type": "belongsTo",
      "model": "Customer",
      "foreignKey": "customerId"
    }
  }
}

Upvotes: 6

Views: 1654

Answers (4)

Ahmed Ismail
Ahmed Ismail

Reputation: 932

Unfortunately this is still not implemented by loopback. Please refer to this issue https://github.com/strongloop/loopback-datasource-juggler/issues/846.

Upvotes: 0

MentalGrinds
MentalGrinds

Reputation: 71

I am not sure if this is the best solution, but here it what I ended up doing. I created a new RemoteMethod named createNew on Customer. Within this new remote method I use the methods added through the model relationships.

Customer.createNew = function (data) {
  var newCustomerId;
  var customerPhones = null;

  if (data.customerPhones && data.customerPhones.length) {
    customerPhones = data.customerPhones;
  }

  return Customer
    .create(data)
    .then(function createCustomerPhones (customer) {
      newCustomerId = customer.id;
      if (customerPhones) {
        customer.customerPhones.create(customerPhones);
      }
    })
    .then(function fetchNewCustomerIncludeRelated () {
      return Customer
        .findById(newCustomerId, {
          include: [ 'customerPhones' ]
        });
    })
    .catch(function (err) {
      return err;
    });
};

To make this a bit safer I will need to wrap it in a transaction. I was hoping to use the base CRUD methods, but this solution if fairly clean.

Upvotes: 1

Anoop Thiruonam
Anoop Thiruonam

Reputation: 2872

If this could be any help, instead of iterating you can insert the numbers in a single step like this:

curl -X POST --header "Content-Type: application/json" --header "Accept: application/json" -d "[{
        \"number\": \"1000\",
        \"type\": \"mobile\",
        \"customerId\": 1
    }, {
        \"number\": \"1004\",
        \"type\": \"home\",
        \"customerId\": 1
    }, {
        \"number\": \"1400\",
        \"type\": \"work\",
        \"customerId\": 1
    }]" "http://127.0.0.1:3000/api/customers/1/phones"

Upvotes: 0

xangy
xangy

Reputation: 1205

If you are using NoSQL database connector, then you could ignore another CustomerPhone model and add the customerPhones property as an array in the Customer model.

Else for SQL database connector, you could create a remote method that performs both POST /api/Customers and POST /api/Customers/id/CustomerPhones together. For multiple phone numbers you could iterate the customerPhones field in the req.body and perform POST /api/Customers/id/CustomerPhones everytime.

Upvotes: 0

Related Questions