BelongsToMany relations as $collections?
Closed this issue · 2 comments
I've been struggling with this one, and would really appreciate some advice or a push in the right direction if anyone can help. My models look as follows:
var Organization = restmod.model('/organizations/organization', {
orders: {belongsToMany: 'Order'},
});
var Order = restmod.model('/orders/order', {
...
});
The behavior I want is for the relation of orders
on an Organization
to not be scoped to the parent url (hence the usage of belongsToMany
as opposed to hasMany
) however I still wish for Organization.orders
to be a Collection. i.e. so that I can still call:
Organization.orders.$create()
and it will be added Organization.orders
, but it will also make a POST request to /orders/order
and not /organizations/organization/<pk>/orders/order
. To elaborate with some more examples:
var newOrganization = Organization.$build();
newOrganization.orders.$create({date: '2015-11-18'}); // POST /orders/order
newOrganization.orders[0].date === '2015-11-18'; // true
newOrganization.orders[0].date = '2016-1-1';
newOrganization.orders[0].$save(); // PATCH /orders/order/<pk>
newOrganization.orders[0].$destroy(); // DELETE /orders/order/<pk>
newOrganization.orders.length === 0; // true
Additionally, my API returns the orders attached to an organization as inlined data, and I wish for that inlined data to be put into a Collection that is only scoped to the /orders/order
url. i.e.
// GET /organizations/organization/1
{
"id": 1,
"orders": [
{"id": 1, "date": "2015-11-18"},
{"id": 2, "date": "2015-12-18"}
]
}
var existingOrg = Organization.$find(1);
existingOrg.orders.length === 2; // true
existingOrg.orders.$create({date: '2017-1-1'}); // POST /orders/order
// Note the call to $create, as I want the inlined data to be unpacked into a collection
existingOrg.orders.length === 3; // true
Effectively I want the scoping of the belongsToMany
relations, which use the urls of the nested model, but I want the 'orders' object itself to be a Collection so I can still call collection methods such as $create
on it.
Is this at all possible, either by using a hasMany
and hacking the urls to the way I want, or by using a `belongsToMany`` and forcing it to return a Collection?
You could go around the problem by configuring the model to mimic the relation behavior, like this:
// The Organization model:
restmod.model('/organizations/organization', {
orders: {
init: function() {
// create a new order collection for every new organization.
return Order.$collection();
},
decode: function(_raw) {
// pass inlined orders to collection on server response decoding
this.orders.$reset().$decode(_raw);
},
mask: 'CU' // prevent orders attribute from being sent on create or update.
}
});
I started doing it that way, but our entire API is structured like this, so I was getting tired of doing this in many different places. Instead, I ended up using a hasMany
relation, and just altered how urls were determined. This allowed the relations to function as collections as I desired, but using the urls I wanted. For reference, it looked something like this:
.factory('NestedCollectionApi', [
'restmod',
function (restmod) {
return restmod.mixin({
$config: {
hasMany: {
hooks: {
'after-has-many-init': function () {
this.$url = _.partial(this.$urlFor, this);
}
}
},
hasOne: {
hooks: {
'after-has-one-init': function () {
this.$url = _.partial(this.$urlFor, this);
}
}
}
}
});
}
])
This seemed to perform the behavior I wanted, by ensuring that whenever I used a model as a nested relation, the urls used were not scoped underneath the parent. I'll go ahead and mark this as closed.