reverse relationship lookup with polymorphic relation
Hello,
I'm using a Polymorphic relation to allow an Account to be associated with two different user types: Customer and Staff. I have the models defined as follows:
var Customer = bookshelf.Model.extend({
tableName: 'customer',
accounts: function() {
return this.morphMany(Account, 'concernable');
}
});
var Staff = bookshelf.Model.extend({
tableName: 'staff',
accounts: function() {
return this.morphMany(Account, 'concernable');
}
});
var Account = bookshelf.Model.extend({
tableName: 'account',
concernable: function () {
return this.morphTo('concernable', 'Staff', 'Customer');
}
});
What I'm attempting to do is fetch all accounts that belong to a specific business and return the customers associated with those accounts. Here's what I've tried with no luck:
Account.where({
business_id: filter.businessId,
concernable_type: 'customer'
})
.fetchAll({
withRelated: ['concernable']
})
.then((response) => {
return reply.jsonapi(response, 'customer');
});
I receive the following error: The target polymorphic model was not found. If I change withRelated to customer, then the error is that customer is not found on the model.
Is what I'm attempting to do possible?
Thanks! James
@rhys-vdw any chance you might be able to provide me with some insight on this? much appreciated!
I am running into this as well.
Looking at the tests, it looks like Account withRelated concernable should work: https://github.com/tgriesser/bookshelf/blob/master/test/integration/relations.js#L545
it('eager loads morphTo (photos -> imageable)', function() {
return Photo.fetchAll({withRelated: ['imageable']}).tap(checkTest(this));
});
https://github.com/tgriesser/bookshelf/blob/master/test/integration/helpers/objects.js#L215
var Photo = Bookshelf.Model.extend({
tableName: 'photos',
imageable: function() {
return this.morphTo('imageable', Site, Author);
},
imageableParsed: function() {
return this.morphTo('imageable', SiteParsed, AuthorParsed);
}
});
https://github.com/tgriesser/bookshelf/blob/master/test/integration/helpers/objects.js#L54
var Site = Bookshelf.Model.extend({
...
photos: function() {
return this.morphMany(Photo, 'imageable');
},
...
});
Good find, @BKStephens. Suppose it could possible be a bug then.
I am experiencing the same error
It could be related to the registry plugin.
return this.morphTo('concernable', 'Staff', 'Customer');
instead of
return this.morphTo('concernable', Staff, Customer);
@dpiccone This is related to #1195 #1194.
Sorry guys, I expect this was a bug, as detailed in #1194. Just merged the PR. I'll try to push an update soon.
:bow:
Thanks, @rhys-vdw -- really appreciate it!
@rhys-vdw any eta on when you might push out that update? Thanks!
Resolved by PR #1195
@vellotis any idea when we might see this in a new release?
@vellotis I'm still seeing this issue as of v0.10
@dpiccone was this ever resolved for you?
@vellotis I've tracked this down to the following line: https://github.com/tgriesser/bookshelf/blob/6a77562cc9d4ceac1370fb1231fccdd9f7f7c467/src/eager.js#L45
The issue is that assumptions are made as to how the morphed fields are named. In my models, everything is camelCased via the format() method, so when the morphed column is retrieved, it comes back empty and the aforementioned error is thrown.
All that said, it seems like it should be calling the Model's format() method, but unfortunately, I'm struggling to find a way to call it in that file.
Thoughts?
cc: @rhys-vdw
Just an FYI that if I change those lines to be camelCased attributes, I no longer receive the error and I can see that the proper queries are executed, but the returned models contain no relations 😢
@jamesdixon #1195 is in v0.10.
I'm reopening it. Could you provide valid and invalid queries for that issue?
@vellotis I can't really generate a valid/invalid query for this as it never gets to that point because of the aforementioned bug.
However, in my case, I'm trying to retrieve all accounts, which sit on the opposite side of the polymorphic relation. I would expect that Bookshelf looks at the polymorphic fields (concernable_id and concernable_type) and generates the proper lookup query from the concernable_type field.
The issue is that Bookshelf is making an assumption about how the field is named on the model. In cases where someone is using format to override the defaults, this causes breakage as demonstrated. Let me know if this is helpful.
@vellotis?
@vellotis Here is more details :
- When I let the default
columnNamesin snake_case it works whenhelpers.js#morphCandidateis called byrelations.jsbecause my attributes are in snake_case. But when the function is called byeager.jsmy attributes are formated so they are in camelCase so theforeignTableisundefinedinhelpers.js#morphCandidate - I tried to specify the
columnNamesin camelCase in my model but obviously it works when it's call byeager.jsbut notrelations.js
It seems that for relation.js the function format is correctly called, but not for eager.js as @jamesdixon already mentioned.
cc: @rhys-vdw
Facing the exact same issue in 0.10.2
var Post = bookshelf.Model.extend({
tableName: 'posts',
comments: function() {
return this.morphMany(Comment, 'keyable');
}
});
var Comment = bookshelf.Model.extend({
tableName: 'comments',
keyable: function() {
return this.morphTo('keyable', Post);
}
});
I found that the function is called by eager.js (same as in @TinOo512 's case) foreignTable's value is Post but the value in candidates comes as posts in helpers.js#morphCandidate
@DGulshan Are you using Model Registry ? Otherwise if you override format/parse it should be the same issue than mine so this fork should fix this. Feel free to try it and add :+1: if it resolve your issue :)
@TinOo512 Yes, I'm using Model Registry. So I still get the same error. Thanks though! :)
Is there any workaround for this bug?
My models are camelCased, and when I call .fetch(withRelated{['myCamelCasesMorphTo']} I get the The target polymorphic model was not found error.
Request Table
const bookshelf = require('./bookshelf');
require('./Comment');
require('./InventoryItem');
require('./User');
require('./Activity');
const UserAssignment = require('./UserAssignment');
module.exports = bookshelf.model('Request', {
tableName: 'requests',
uuid: true,
hasTimestamps: true,
comments() {
return this.morphMany('Comment', 'note');
},
inventory_item() {
return this.belongsTo('InventoryItem');
},
activities() {
return this.morphMany('Activity', 'parent');
},
user_assignments() {
return this.morphMany(UserAssignment, 'parent');
},
});
User Assignment Table
const bookshelf = require('./bookshelf');
require('./User');
require('./Assignment');
const Request = require('./Request');
const InventoryItem = require('./InventoryItem');
module.exports = bookshelf.model('UserAssignment', {
tableName: 'user_assignments',
uuid: true,
hasTimestamps: true,
user() {
return this.belongsTo('User', 'user_id', 'global_id');
},
parent() {
return this.morphTo('parent', 'Request');
},
});
It was not accepting 'requests' or 'Requests' as parent_type until I made the following update, which does NOT match this documentation
- return this.morphTo('parent', Request, InventoryItem);
+ return this.morphTo('parent', 'Request', 'InventoryItem');