zealoushacker
zealoushacker

Reputation: 6906

How can I cleanly pull a Parse.Object relation's records when fetching the object?

In the Parse JavaScript guide, on the subject of Relational Data it is stated that

By default, when fetching an object, related Parse.Objects are not fetched. These objects' values cannot be retrieved until they have been fetched.

They also go on to state that when a relation field exists on a Parse.Object, one must use the relation's query().find() method. The example provided in the docs:

var user = Parse.User.current();
var relation = user.relation("likes");
relation.query().find({
  success: function(list) {
    // list contains the posts that the current user likes.
  }
});

I understand how this is a good thing, in terms of SDK design, because it prevents one from potentially grabbing hundreds of related records unnecessarily. Only get the data you need at the moment.

But, in my case, I know that there will never be a time when I'll have more than say ten related records that would be fetched. And I want those records to be fetched every time, because they will be rendered in a view.

Is there a cleaner way to encapsulate this functionality by extending Parse.Object?

Upvotes: 3

Views: 2602

Answers (2)

Michael Peterson
Michael Peterson

Reputation: 10532

Have you tried using include("likes")?

I'm not as familiar with he JavaScript API as the ObjC API.. so in the example below I'm not sure if "objectId" is the actual key name you need to use...

var user = Parse.User.current();
var query = new Parse.Query(Parse.User);
query.equalTo(objectId, user.objectId);
query.include("likes")
query.find({
  success: function(user) {
    // Do stuff
  }
});

In general, you want to think about reverse your relationship. I'm not sure it is a good idea be adding custom value to the User object. Think about creating a Like type and have it point to the user instead.

Example from Parse docs: https://parse.com/docs/js_guide#queries-relational

var query = new Parse.Query(Comment);

// Retrieve the most recent ones
query.descending("createdAt");

// Only retrieve the last ten
query.limit(10);

// Include the post data with each comment
query.include("post");

query.find({
  success: function(comments) {
    // Comments now contains the last ten comments, and the "post" field
    // has been populated. For example:
    for (var i = 0; i < comments.length; i++) {
      // This does not require a network access.
      var post = comments[i].get("post");
    }
  }
});

Upvotes: 4

zealoushacker
zealoushacker

Reputation: 6906

Parse.Object's {Parse.Promise} fetch(options) when combined with Parse.Promise's always(callback) are the key.

We may override fetch method when extending Parse.Object to always retrieve the relation's objects.

For example, let's consider the following example, where we want to retrieve a post and its comments (let's assume this is happening inside a view that wants to render the post and its comments):

var Post = Parse.Object.extend("Post"),
    postsQuery = new Parse.Query(Post),
    myPost;

postsQuery.get("xWMyZ4YEGZ", {
  success: function(post) {
    myPost = post;
  }
).then(function(post) {
  post.relation("comments").query().find({
    success: function(comments) {
      myPost.comments = comments;
    }
  });
});

If we had to do this every time we wanted to get a post and its comments, it would get very repetitive and very tiresome. And, we wouldn't be DRY, copying and pasting like 15 lines of code every time.

So, instead, let's encapsulate that by extending Parse.Object and overriding its fetch function, like so:

/*
  models/post.js
*/

window.myApp = window.myApp || {};

window.myApp.Post = Parse.Object.extend("Post", {
  fetch: function(options) {
    var _arguments = arguments;
    this.commentsQuery = this.relation("comments").query();
    return this.commentsQuery.find({
      success: (function(_this) {
        return function(comments) {
          return _this.comments = comments;
        };
      })(this)
    }).always((function(_this) {
      return function() {
        return _this.constructor.__super__.fetch.apply(_this, _arguments);
      };
    })(this));
  }
});

Disclaimer: you have to really understand how closures and IIFEs work, in order to fully grok how the above works, but here's what will happen when fetch is called on an existing Post, at a descriptive level:

  1. Attempt to retrieve the post's comments and set it to the post's comments attribute
  2. Regardless of the outcome of the above (whether it fails or not) operation, always perform the post's default fetch operation, and invoke all of that operation's callbacks

Upvotes: 3

Related Questions