Advanced
Data Modelling
IN The Frontend
/whois
Jan-Oliver Pantel
Backend Developer @CouchCommerce
Twitter: @JanPantel

Data Modelling
In The Backend
- ORMs
- Rails, Symfony, Laravel are shipped with an ORM
- even Microsoft MVC
Laravel Model Example
class User extends Eloquent {
protected $fields = array('id', 'name', 'password');
public function posts() {
$this->hasMany('Post');
}
}
class Post extends Eloquent {
protected $fields = array('id', 'title', 'body');
public function user() {
$this->belongsTo('User');
}
}
$posts = User::find(1)->posts()->get();
//SELECT p.* FROM user AS u WHERE u.id = 1
//INNER JOIN post AS p ON p.user_id = u.id
$user = Post::find($post->id)->user()->get();
//SELECT u.* FROM post AS p WHERE p.id = 1
//INNER JOIN user AS u ON p.user_id = u.id
SQL and orms
- Building SQL dynamically via an ORM increases testability of the code
- Switching databases becomes easier due to access layer abstraction
- Renaming a database table or column doesn't trigger a big refactoring of wild SQL statements within the code
What's About the frontend?
$http
$http.get('http://www.mysite.com/user/1/posts')
.success(function (response) {
$scope.posts = response.data;
});
Ok, we can live with things like that
but
$http.get(
'http://www.mysite.com/posts/'
+ $scope.chosenPost.id
+ '/comments'
).success(function (response) {
$scope.comments = response;
})

$resource
var User = $resource('/user/:userId');
var Post = $resource('/user/:userId/posts');
var Comment = $resource('/post/:postId/comments');
var user = User.get({ userId: 1 }, function () {
var posts = Post.query({ userId: user.id }, function () {
$scope.posts = posts;
});
});
$scope.changePost = function (post) {
var comments = Comment.query({
postId: post.id
}, function () {
$scope.comments = comments;
});
};

But
but
- No promises
- You can't use it in ui-router's resolve without workarounds
resolve: {
user: function ($q, $resource) {
var d = $q.defer();
var User = $resource('/user/:id');
var user = User.get({ id: 1 }, function () {
d.resolve(user);
});
return d.promise;
}
}
- You have to define a $resource object for each request
- No custom methods
- No relations between entities
Restangular
Restangular.one('user', '1').get().then(function (user) {
user.all('posts').getList().then(function (posts) {
$scope.posts = posts;
});
});
$scope.changePost = function (post) {
Restangular.one('post', post.id).all('comments').getList()
.then(function (comments) {
$scope.comments = comments;
});
};
Not bad at all
but
- You have to restart the query if you don't subquery any more
var posts = Restangular.one('user', 1).all('posts').getList();
// => /user/1/comments
var comments = posts[0].all('comments').getList();
// => /user/1/posts/<id>/comments
Restangular.one('post', <id>).all('comments').getList();
// => /post/<id>/comments
- The majority of REST APIs won't have such a deep entity query
- Most times you get your comments via /user/1/posts
and comments via /post/<id>/comments - Configuration only globally during app init
- STRINGS :(
And now changing api endpoints

Rest Orm
Why?

The frontend Got his SQL called
REST
Models
orm.register('User', {
url: 'user', //optional
relations: {
hasMany: {
Post: {
model: 'Post', //optional
//will get pluralized automatically
}
},
hasOne: {
Girlfriend: {
model: 'User'
//will get singularized by default
}
}
}
});
QuerIes
DataContext.User.find(1).posts.get().success(function (posts) {
$scope.posts = posts;
});
// => /user/1/posts
$scope.changePost = function (post) {
post.reset().comments.get().success(function (comments) {
$scope.comments = comments;
});
// => /post/<id>/comments
};
If you call reset the query will be changed
But
Query States
DataContext.Post.find(1).User.get().success(function (user) { user.reset().girlfriend.get().success(function (girlfriend) { $scope.girlfriend = girlfriend; });// => /user/1/girlfrienduser.posts.get().success(function (posts) { $scope.posts = posts; });// => /user/1/posts });
user.clone().reset().girlfriend.get().success(function (girlfriend) {
$scope.girlfriend = girlfriend;
});
//OR SUGAR BABY
user.cloneReset().girlfriend....CRUD
DataContext.User.find(1).remove();
//OR
DataContext.User.remove(1);
// => DELETE /user/1DataContext.User.find(1).get().success(function (user) {
user.name = "Jan";
user.save();
// => PUT /user/1
});
DataContext.User.create({ name: "Jan" }).success(function (user) {
user.girlfriend.attach(gf);
// => PUT /user/<user.id>/girlfriend/<gf.id>
});
// => POST /user
Working with Relations
DataContext.User.find(1).girlfriend.detach().success(function () {
location.href = "www.youp***.com";
});
// => DELETE /user/1/girlfriendDataContext.User.find(1).girlfriend.attach(gf).success(function (user) {
console.log('Gratz bro ;)');
});
// => PUT /user/1/girlfriend/<gf.id>
DataContext.User.find(1).posts.remove(post).success(/*...*/);
// => DELETE /user/1/posts/<post.id>
DataContext.User.find(1).posts.add(post).success(/*...*/);
// => PUT /user/1/posts/<post.id>
Interested?
Not everything done yet
Check: janpantel/angular-restorm
@github (development branch)
Also beta testers are welcome
RoadMAP
- Configurable caching
- Documentation
Interested In Contributing Code or Ideads?
Twitter: @JanPantel
Mail: jan.pantel@gmail.com
Thanks

Advanced Data ModellingIn The Frontend
By Jan Pantel
Advanced Data ModellingIn The Frontend
- 370
