How to deal with promises in computed properties - ember-data

I'm wondering how should I deal with async belongsTo into computed properties of my models. Consider the following models :
App.Invoice = DS.Model.extend()
App.Group = DS.Model.extend
invoice: DS.belongsTo('group', {async: true})
App.Quotation = DS.Model.extend
group: DS.belongsTo('group', {async: true})
hasGroupInvoice: (->
// if #get('group.invoice') ?
).property('group.invoice')
Here's my problem: As long as quotation.group and group.invoice are async belongsTo I don't know how to do.
I would like my computed property to returns true if #get('group.invoice') exist otherwise it should returns false
Thanks

Related

Undefined model by using Join Query in Laravel

I am indexing apples with their specified properties (such as color) using API Laravel. I use join to retrieve apples which are related to a specified brand. but it does not retrieve apples with their own specified properties which are defined in another DB and models.
public function index(Brand $brand)
{
$apples = Apple::join('brands', 'brand_id', 'brands.id')->where('brand_id', $brand->id)->get();
return returnSuccessfulResponse(
trans('api.response.successful.index'),
Resource::collection($apples)
);
}
Apple model:
public function brand()
{
return $this->belongsTo(Brand::class);
}
public function appleProperties()
{
return $this->hasMany(AppleProperty::class);
}
Resource:
return [
'id' => $this->brand->id,
'name' => $this->brand->name,
'apple-properties' => $this->appleProperties,
];
Route:
Route::apiResource('brands/{brand}/apples', 'AppleController');
It is not retrieving appleProperties. I do not understand that reason!
When you use join() method in your queries, it is recommended to use select() as well, so that is no longer ambiguous which table you referenced to. In your code, the query may be something like this:
$apples = Apple::join('brands', 'brand_id', 'brands.id')->select('apples.*')->where('brand_id', $brand->id)->get();

sequelize: how to do equivalent of findAndCountAll for associations

One of my biggest pains with sequelize is to basically "paginate and count" for associations.
Using findAndCountAll is fairly straightforward for one-off models, a bit painful for has-many, but utterly impossible to work with with many-to-many.
As an example, I have this many-to-many association established, where user can belong to many groups:
const UserGroup = db.define('user_groups', {
})
User.belongsToMany(Group, {through: UserGroup})
Group.belongsToMany(User, {through: UserGroup})
getting all users from a group would be straightforward:
user.getGroups().then....
but porting this to findAndCountAll just doesn't seem to work the same way:
User.findAndCountAll({
include: [{model: Group,
through: UserGroup,
where: { groupId : group.id,
userId : {$ne: user.id}}
}],
limit: limit,
offset: offset, ...
this doesn't work, as it associates keys from the where clause to the Group model.
I also tried:
User.findAndCountAll({
include: [{model: Group,
include: {
model: UserGroup,
where: { groupId : group.id,
userId : {$ne: user.id}}}
}],
limit: limit,
offset: offset, ...
but it fails as well, with Error: user_groups is not associated to groups!, which is not really true.
Is there a clean way to do this, preferably with the helper methods?
I had this problem earlier, and I decided to change functionality in this way...
const include = [
{
model: models.UserGroup,
attributes: [],
as: 'groups'
}
];
const [total, data] = await Promise.all([
User.count({ distinct: true, include }),
User.findAll({
order: [sequelize.literal(`MAX("groups"."createdAt") desc`)],
group: ['User.id'],
include
})
]);
I'm afraid there is no possibility to use findAndCountAll method when eager loading associations (or at least when you also want to add some conditions to associations). There is no option to get information about total count of associated items using, in your case, user.getGroups().
Although you can use Sequelize's shorthand method for counting association's instances, in your case, user.countGroups() before returning user.getGroups(). If you want to fetch related groups with some conditions, simply pass them as getGroups() method parameter (it takes same options parameter as standard findAll)

one to many - sequelize update - not removing/inserting children

So I've been struggling for a few hours now with a one-to-many mapping update.
I've got a project which has certain tasks (for example).
I add and remove tasks through the frontend and send the revised object to by backend running with sequelize.
Then I tried to update the records as follows:
return models.Project
.findOne({
where: { id: projectToUpdate.id },
include: [models.Task]
})
.then(function (ProjectFromDb) {
return models.sequelize
.transaction({
isolationLevel: models.sequelize.Transaction.ISOLATION_LEVELS.READ_COMMITTED
},
function (t) {
return ProjectFromDb
.update(projectToUpdate,
{
include: [{ model: models.Task }]
})
});
})
.then(function (result) {
return output.getSuccessResult(....
})
.catch(function (error) {
return output.getErrorResult(....
});
But this would only update the Project
Next I tried to update them with an additional then call:
.then(function (updateResult) {
return updateResult.setTasks(projectToUpdate.Tasks, {transaction: t})
})
But this would give me the result that he is trying to update the Task and set the ProjectId to NULL which is not possible because it is non-nullable.
I am currently "manually" adding the tasks and removing them but this seems to be a silly way of using the framework.
Can anyone tell me how to properly make this work with a one-to-many relationship without me calling Tasks.createBulk and Tasks.destroy?
EDIT TO INCLUDE MODEL
JSON object looks like this:
{
id: 1,
projectName: 'nameOfTheProject',
Tasks: [
projectId: 1,
name: 'taskName'
]
}
Please try changing the property name projectId to ProjectId on your Tasks objects that are nested to the projectToUpdate object.
Update
Looking at sequelize's source, it seems that the Instance.$save() function (which is called by Instance.$update() that you're using) does not support nested models creation when you're updating it - it checks if the flag wasNewRecord is true before doing it.

Get model with both id and query parameters

I have a route that should load a model (BatchDetail) and a number of related items (BatchItems). Since there are a great number of items I should be able to do pagination with the help of two request parameters, limit and offset.
Here is the route I set up:
App.BatchDetailRoute = Ember.Route.extend({
model: function(params) {
var store = this.get('store');
var adapter = store.get('adapter');
var id = params.batch_detail_id;
var rejectionHandler = function(reason) {
Ember.Logger.error(reason, reason.message);
throw reason
}
return adapter.ajax("/batch_details/" + id, "GET", {
data: { limit: 50, offset: 100 }
}).then(function(json) {
adapter.didFindRecord(store, App.BatchDetail, json, id);
}).then(null, rejectionHandler);
},
setupController: function(controller, model) {
return this.controllerFor('batchItems').set('model', model.get('items'));
}
})
This way, when I go to /batch_details/1 my REST adapter will fetch the correct data which I receive in json in the above code.
Now, the model hook should return a model object or a promise that can be resolved to a model object, and that's where the problem lies. In setupController (which runs after the model hook) model is set to undefined and so my code explodes.
That means that whatever adapter.ajax returns does not resolve correctly but instead returns undefined. I'm baffled, since the above mechanism is exactly how the different find methods in ember-data (findById, findByQuery, etc.) work and that's where I got my idea from.
Can you shed some light on what I'm not getting?
Thank you.

Nesting Backbone models or collections so that they are linked

I have a User entity that has a subscriptions property. This is an array of IDs.
When I perform a fetch, the API will populate those subscriptions, and return something like this:
{
subscriptions: [1, 2, 3],
__subscriptions: [
{
id: 1,
name: 'Example'
},
{
id: 2,
name: 'Example'
},
{
id: 3,
name: 'Example'
}
]
}
I have done this so that I can still perform actions on the original subscriptions and then save them back to the API. Any changes I make to __subscriptions will not be persisted as the API doesn't recognise this field – it is simply the populated data.
In the parse function of my User, I create the nested collection:
parse: function (response) {
this.subscriptions = new Subscriptions(response.__subscriptions)
}
However, if I want to remove a subscription, I have to splice it from the subscriptions field of the User entity, and then I also have to remove it from the subscriptions collected that is nested as a property on the User:
// Clone the subscriptions property, delete the model with a matching ID, and then set it again.
var value = _.clone(this.get('subscriptions'))
// Use splice instead of delete so that we don't leave an undefined value
// in the array
value.splice(value.indexOf(model.id), 1)
// Also remove the same model from the nested collection
var removedSubscription = this.subscriptions.get(model)
this.subscriptions.remove(removedSubscription)
this.set('subscriptions', value)
this.save()
This is sort of annoying. Ideally, removing an ID from the subscriptions property should automatically update the collection.
Does this seem like a good way to deal with nested models and collections? I've heard bad things about Backbone.Relational so I was interested in a simpler solution.
I would listen to events of Subscriptions collection and update subscriptions argument accordingly.
var User = Backbone.Model.extend({
initialize: function () {
this.subscriptions = new Subscriptions;
this.subscriptions.on('add remove', this.updateSubscriptions, this)
},
updateSubscriptions: function() {
this.set('subscriptions', this.subscriptions.pluck('id'))
},
parse: function (response) {
this.subscriptions.reset(response.__subscriptions);
return Backbone.Model.parse.call(this, response);
}
});
So then removing subscription will update subscriptions attribute of user model:
user.subscriptions.remove(subscription)