PDA

View Full Version : Model Association Query



sg707
28 Feb 2011, 3:10 PM
I'm trying to invoke the uri like

/users/1/posts

This will return all "posts" from user account ID 1.

I tried to read the blog but I couldn't get it to work.

Here is what I have




Ext.regModel('User', {
fields : [
{
name : 'id',
type : 'int'
}, {
name : 'name',
type : 'string'
}
],
hasMany: 'Posts',
proxy : {
type : 'rest',
url : 'users',
format : 'json',
reader : {
type : 'json',
root : 'users'
}
}

});

Ext.regModel('Post', {
fields : [
{
name : 'id',
type : 'int'
},
{
name: 'title',
type : 'string'
}
],
belongsTo: 'User'
});

Ext.onReady(function() {
var User = Ext.ModelMgr.getModel('User');

User.load('0', {
success : function(record, operations) {
console.log(record);
console.log(record.get('name'));
record.posts(); //I am randomly guessing..and obviously i am wrong

});


As you can see I tried to invoke record.posts()...however there is no "posts" method defined. The reason I used "posts" method is from the code I've seen on the blog



User.load(123, {
success: function(user) {
console.log("User: " + user.get('name'));

user.posts().each(function(post) {
console.log("Comments for post: " + post.get('title'));

post.comments().each(function(comment) {
console.log(comment.get('message'));
});
});
}
});



Please help!!!

sg707
28 Feb 2011, 3:27 PM
Had some more progress.

Changed to


Ext.regModel('Post', {
fields : [
{
name : 'id',
type : 'int'
}, {
name : 'title',
type : 'string'
}
],
belongsTo : 'User',
proxy : {
type : 'rest',
url : 'posts',
format : 'json',
reader : {
type : 'json',
root : 'users'
}
}
});


added


new Ext.data.Store( {
id : 'postStore',
model : 'Post'
});


now when I execute


record.posts().load()

it's invoking this URL

posts.json?_dc=1298935347935&limit=25&filter=[{%22property%22%3A%22user_id%22%2C%22value%22%3A1}]

which clearly isn't the URI I'm trying to get to...which is

/users/1/posts

I hope this is doable.

sg707
28 Feb 2011, 3:48 PM
Yup...keep answering to myself... and I've resolved it. I'm pretty sure this will be useful for others who wants to follow similar URI standard.

I've changed to


User.load('1', {
success : function(record, operations) {
record.posts().proxy.url = record.proxy.url + "/" + record.get('id') + "/posts";
record.posts().load();
}
});

sg707
28 Feb 2011, 3:51 PM
Is it me but passing Get parameter "filter" kind of breaks what REST is for?



filter[{"property":"user_id","value":1}]
I really prefer /users/1/posts.json

then

/posts.json?filter=[{"property":"user_id","value":1}]

No? :-/:-/:-/

mdavis6890
1 Mar 2011, 8:27 AM
Not really. With REST, the specified URL for the Model should return all of those items. So '/posts' should return all the posts.

If you want just some of the data, rather than all, you would send additional parameters to the server as with the filter above. The same goes for sort order and pages.

The non-RESTful aspect is the /users/1/posts syntax. What if you had other models like 'Day,' 'Thread,' and 'Group' that all haveMany 'Posts?' Would you then have a bunch of different URLs to get to the same type of data? No, better would be just to use '/posts' with an appropriate filter.

Michael

sg707
1 Mar 2011, 8:39 AM
I'm not quite sure if it's non-RESTful though.. this standard is part of Ruby on Rails. From what I heard, they are the king of RESTful framework.

I think creating separate URL makes more sense than dumping all /posts in one URL
for example

/users/1/posts.json - returns all posts related to user id = 1
/groups/2/post.json - returns all posts related to group id = 2
/day/3/posts.json - returns all posts related to a day id = 3

If it requires more filter then specifying the "id" then passing as a parameter makes sense.

The logic of gathering the "posts" is all different from users/groups/day. Not sure why I would tie all that logic into one URL. In my back-end code, I probably have

// UserController maps to all url /users/**
// map the url /users/{userId}/posts.json
UserController.getPosts

// GroupController maps to all url /groups/**
// map the url /groups/{groupId}/posts.json
GroupController.getPosts

// DayController maps to all url /day/**
// map the url /day/{dayId}/posts.json
DayController.getPosts

For each of the getPost method contains corresponding business logic that's related to the Class name. Not sure how making global

PostController.getPosts

which will have numerous "if" to determine for user/group/day/etc... also filter variables will vary depending on the request as well.

I guess I could create

// map it to /post/user/{userId}.json
PostController.getUserPosts
// map it to /post/group/{groupId}.json
PostController.getGroupPosts
// map it to /post/day/{dayId}.json
PostController.getDayPosts

But the URL structure doesn't seem RESTful to me...

Then again, this is my opinion though... To me, the above approach is much cleaner approach. Still, let me know of your opinion as I'm thinking of this approach for refactoring my project.

mdavis6890
1 Mar 2011, 9:18 AM
I see what you mean, but in that case Ruby is handling a lot of the heavy lifting with business logic that's external to the general concept of 'posts.'

When using a filter, there's not any required business logic on the server side at all. You don't really even need a special 'posts' script.

You use the first part of the URL ('/posts/') to get the table or view you want to act on, and the optional second part ('/123') to identify the ID of the item you want to get, or all if absent. Then you can check for a filter parameter and add 'WHERE blah' code as necessary.

This is just my approach, but I tend not to use frameworks too much.

Maybe there's no right answer here. I'd be interested to get some third and forth opinions.

Michael

sg707
2 Mar 2011, 1:02 PM
I think you're right on how Sencha views the Server Side architecture on Data Access Layer.

However, just like Model Association that ExtJS employed... my Server Side also is using Model Association. Meaning that I can do Model Association Query on Server side like



List<Posts> posts = userDAO.findById(1).getPosts();
userDAO.findById(1) will execute SQL Query

then the subsequent method .getPosts() will create yet another SQL Query. I understand if you're using SQL directly then your approach makes sense... however when using popular Object Relational Mapping framework then I'm querying from Object point of view or you can say Model point of view.

This is why I really like the URI


/users/1/posts.json
as this really mirrors


userDAO.findById(1).getPosts()
No?

atwoodjw
14 Apr 2011, 8:19 AM
So I'm a little late to this party, but I was just noticing the same thing about REST in Sencha Touch / Ext JS 4. It's worth noting that
/posts.json?filter=[{"property":"user_id","value":1}] and
/users/1/posts.json are both RESTful. The difference, in Rails, is that
/users/1/posts.json assumes a nested relationship. Posts are logically children of Users.

http://edgeguides.rubyonrails.org/routing.html#nested-resources

Separate from this is specifying hasMany/hasOne relationships in Rails and Sencha.

edspencer
16 Apr 2011, 5:58 PM
It should be easy enough to create a RailsProxy that extends RestProxy and provides its own buildUrl function. The buildUrl function is passed an Ext.data.Operation object that should contain all of the information you need. We might bundle a RailsProxy in 4.1 but you should be able to create one earlier than that without too much difficulty.

jiunjiunma
26 Jun 2011, 4:57 PM
Can you provide some sample on how to extend the proxy? I am new to Ext-JS so it will take me a while to figure out how to do it. Thanks.