Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sails.js populate nested associations

I've got myself a question regarding associations in Sails.js version 0.10-rc5. I've been building an app in which multiple models are associated to one another, and I've arrived at a point where I need to get to nest associations somehow.

There's three parts:

First there's something like a blog post, that's being written by a user. In the blog post I want to show the associated user's information like their username. Now, everything works fine here. Until the next step: I'm trying to show comments which are associated with the post.

The comments are a separate Model, called Comment. Each of which also has an author (user) associated with it. I can easily show a list of the Comments, although when I want to display the User's information associated with the comment, I can't figure out how to populate the Comment with the user's information.

In my controller i'm trying to do something like this:

Post   .findOne(req.param('id'))   .populate('user')   .populate('comments') // I want to populate this comment with .populate('user') or something   .exec(function(err, post) {     // Handle errors & render view etc.   }); 

In my Post's 'show' action i'm trying to retrieve the information like this (simplified):

<ul>    <%- _.each(post.comments, function(comment) { %>     <li>       <%= comment.user.name %>       <%= comment.description %>     </li>   <% }); %> </ul> 

The comment.user.name will be undefined though. If I try to just access the 'user' property, like comment.user, it'll show it's ID. Which tells me it's not automatically populating the user's information to the comment when I associate the comment with another model.

Anyone any ideals to solve this properly :)?

Thanks in advance!

P.S.

For clarification, this is how i've basically set up the associations in different models:

// User.js posts: {   collection: 'post' },    hours: {   collection: 'hour' }, comments: {   collection: 'comment' }  // Post.js user: {   model: 'user' }, comments: {   collection: 'comment',   via: 'post' }  // Comment.js user: {   model: 'user' }, post: {   model: 'post' } 
like image 369
Lars Dol Avatar asked May 03 '14 15:05

Lars Dol


1 Answers

Or you can use the built-in Blue Bird Promise feature to make it. (Working on [email protected])

See the codes below:

var _ = require('lodash');  ...  Post   .findOne(req.param('id'))   .populate('user')   .populate('comments')   .then(function(post) {     var commentUsers = User.find({         id: _.pluck(post.comments, 'user')           //_.pluck: Retrieves the value of a 'user' property from all elements in the post.comments collection.       })       .then(function(commentUsers) {         return commentUsers;       });     return [post, commentUsers];   })   .spread(function(post, commentUsers) {     commentUsers = _.indexBy(commentUsers, 'id');     //_.indexBy: Creates an object composed of keys generated from the results of running each element of the collection through the given callback. The corresponding value of each key is the last element responsible for generating the key     post.comments = _.map(post.comments, function(comment) {       comment.user = commentUsers[comment.user];       return comment;     });     res.json(post);   })   .catch(function(err) {     return res.serverError(err);   }); 

Some explanation:

  1. I'm using the Lo-Dash to deal with the arrays. For more details, please refer to the Official Doc
  2. Notice the return values inside the first "then" function, those objects "[post, commentUsers]" inside the array are also "promise" objects. Which means that they didn't contain the value data when they first been executed, until they got the value. So that "spread" function will wait the acture value come and continue doing the rest stuffs.
like image 152
Fermin Yang Avatar answered Oct 05 '22 03:10

Fermin Yang