Hi Payload team. I haven't been able to find substantial documentation for payload.update() when the updated field is
relationship
fieldhasMany: true
relationshipTo: [ // more than one collection // ]
The gist: I have a parent-child relationship between items of various collections (mixed-content tree). I want to re-parent the updated item by removing the child from the old parent's child
field and then add it to the new parent's child
field.
I have a hook set up to do this, but I cannot for the life of me figure out what the call to payload.update(...)
is expecting to receive.
If I manually add entries to the child
field for an item in the admin GUI then I get back an array in the child
field. This is sensible:
[
{
relationTo: 'pages',
value: {
parent: [Object],
title: 'Parent Page',
renderings: [],
children: [],
createdAt: '2021-06-20T06:04:33.461Z',
updatedAt: '2021-06-22T01:08:52.412Z',
id: '60ceda71361a3300191cfd4b'
}
},
{
relationTo: 'pages',
value: {
parent: [Object],
title: 'Sub Page',
renderings: [],
children: [],
createdAt: '2021-06-20T06:05:13.529Z',
updatedAt: '2021-06-22T00:57:55.799Z',
id: '60ceda99361a3300191cfd7b'
}
}
]
I would expect I can update this list by removing an item from the array or appending another object with the form
{
relationTo: 'datasource-folders',
value: {
id: '60ceda71361a3300191caa12'
}
}
however this will not save.
payload.update({
collection: originalParent.type,
id: originalParent.id,
data: {
children: updatedChildren // the array above
}
});
The error provided
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
makes it seem like Payload is expecting to receive data in the format
[
...
{
relationTo: 'datasource-folders',
value: '60ceda71361a3300191caa12'
},
]
however this is cursed. It doesn't work either and causes new craziness that encouraged me to drop the collection in Mongo and start fresh.
It would be great if you could point me to documentation for updating relationship
fields, especially where hasMany: true
and relationTo: [ // multiple collections // ]
.
If there isn't documentation but you're willing to explain it I would be happy to put in a PR for a documentation update.
TYSM
I feel you should look into hooks as quick advice. At least that's how I created something similar.
Yes thanks for the suggestion @Ontopic I am using hooks to implement this. Getting the hooks to fire was pretty straight forward, it's the interaction with the local API inside the hook where I am having no luck.
Hey @RobertAlbus — I can shine some light on this for you!
First up, from your question above, this snippet is an example for how a hasMany: true
and relationTo: [ ]
relationship will look when it is populated by the depth
parameter:
[
{
relationTo: 'pages',
value: {
parent: [Object],
title: 'Sub Page',
renderings: [],
children: [],
createdAt: '2021-06-20T06:05:13.529Z',
updatedAt: '2021-06-22T00:57:55.799Z',
id: '60ceda99361a3300191cfd7b'
}
}
]
However, the actual data that is stored in the database looks like this:
[
{
relationTo: 'pages',
value: ObjectID('60ceda99361a3300191cfd7b'),
}
]
So, to update, you need to send it data just like you mentioned:
[
...
{
relationTo: 'datasource-folders',
value: '60ceda71361a3300191caa12'
},
]
What is the error that is displaying when you try to update
with this data? From my perspective this looks exactly right!
This is super helpful @jmikrut. Thanks for clarifying the expected shape for updating a relationship field with hasMany: true
.
The error I see when I submit an update
const updatedChildren = [
...
{
relationTo: 'datasource-folders',
value: '60ceda71361a3300191caa12'
},
];
// this array can also be empty
payload.update({
collection: originalParent.type,
id: originalParent.id,
data: {
children: updatedChildren
},
});
to either remove or add an item to the relationship field, the server crashes with the following stack trace
app_1 | /node_modules/payload/dist/fields/relationshipPopulationPromise.js:19
app_1 | idString = idString.toString();
app_1 | ^
app_1 |
app_1 | TypeError: Cannot read property 'toString' of undefined
app_1 | at populate (/node_modules/payload/src/fields/relationshipPopulationPromise.ts:46:29)
app_1 | at rowPromise (/node_modules/payload/src/fields/relationshipPopulationPromise.ts:105:17)
app_1 | at /node_modules/payload/src/fields/relationshipPopulationPromise.ts:119:24
app_1 | at Array.forEach (<anonymous>)
app_1 | at /node_modules/payload/src/fields/relationshipPopulationPromise.ts:102:22
app_1 | at /node_modules/payload/src/fields/performFieldOperations.ts:118:86
app_1 | at Array.map (<anonymous>)
app_1 | at Payload.performFieldOperations (/node_modules/payload/src/fields/performFieldOperations.ts:118:66)
app_1 | at processTicksAndRejections (node:internal/process/task_queues:96:5)
app_1 | at Payload.update (/node_modules/payload/src/collections/operations/update.ts:272:12)
After this the admin panel starts to behave weird. The page that displays the collection list for the recently-associated item does not show any items, except for every 3 or 4 page refreshes. The app throws the same stack trace as above on every page refresh for this collection's list view.
I feel like I am just missing something basic but the stack trace, Payload source, documentation, and reviewing my code just aren't leading me where I need to go.
Hey @RobertAlbus — can you hook me up with your collection config?
I will do some testing with it. Should be able to figure this out. Nothing jumps out at me as being incorrect with the code that you've provided thus far!
@RobertAlbus another thought... one thing I am thinking is that you might have some bad data somewhere in your database.
The error that you sent would make sense if the shape of a document somewhere was bad. It's attempting to convert a Mongo ObjectID to a string and clearly failing. We will go ahead and add in a fix to that case to hopefully make everything a bit more stable for future users. But in the interim, I'd poke through your database for any documents affected by this update for an old or bad data shape.
@RobertAlbus I thought I'd just blow you up. You got me thinking.
Just deployed 0.7.2
which safely accesses the toString
method which was erroring out on what I imagine is potentially bad data. I'd be curious to see three things:
{
"_id":"ObjectId(""60d3b796ad579d0018fe1b27"")",
"title":"Kontralto Consulting Corp",
"children":[
{ // should have an ID but doesn't
"relationTo":"pages"
}
],
"createdAt":"ISODate(""2021-06-23T22:37:10.681Z"")",
"updatedAt":"ISODate(""2021-06-23T22:39:25.322Z"")",
"__v":0
}
I personally run into this every now and then, then I reseed my database. Not sure if that's an option for you, but otherwise fixes issues like this quite often.
Thanks for sharing your repo, interesting research in there!
then I reseed my database
Thanks for the prompt, I blew away the database and tried again for sanity's sake. Sanity not found :P it happens with a fresh database without fail.
I'm gonna be trying to get your repo to run and see what I can find. I personally only have experience with keeping one-to-one relationships in sync, which went fine, but one-to-many is definitely something I need to play around with anyway, for sanity's sake 🙃 Imagine what Payload could do with 39M...
Hey @RobertAlbus,
Thank you so much for sharing your repo, I like how you're building your collections and sharing functionality between them. This kind of smart achitecture is exactly the kinds of creative solutions we hoped people would do with Payload CMS.
I found the bug! I noticed that the call to update the parent in with the children objects is being given the id of the parent and not the child document. Here is a screenshot of my debugger before the call to payload.update() in your hook:
One important mention, You're using the beforeChange
hook but new documents will not have an id yet. To do what you want, you'll need to change the hooks to run afterChange
instead of beforeChange
at least for new documents. Then you'll be able to use the correct child id for children relationships when you update the parent document.
I hope this helps, let us know how it goes!
Thanks for the response @DanRibbens. I see what you're pointing out there.
My next steps were to move those hooks from beforeChange to afterChange. The issue was still present so I changed the hooks from field hooks to afterChange collection hooks but the issue of malformed data is still present.
doc
{
id: '60dce0b06a737200188df539',
parent: { relationTo: 'sites', value: '60dce0a56a737200188df506' },
heading: 'My Page',
renderings: [],
children: [],
createdAt: '2021-06-30T21:22:56.678Z',
updatedAt: '2021-06-30T21:22:56.678Z'
}
parent
{
id: '60dce0a56a737200188df506',
title: 'Kontralto Consulting Corp',
children: [],
createdAt: '2021-06-30T21:22:45.113Z',
updatedAt: '2021-06-30T21:22:45.113Z'
}
updated children list
[ { relationTo: 'pages', id: '60dce0b06a737200188df539' } ]
api call
payload.update({
collection: 'sites',
id: '60dce0a56a737200188df506',
data: { children: [ { relationTo: 'pages', id: '60dce0b06a737200188df539' } ] }
})
database content
> db.sites.find()
{ "_id" : ObjectId("60dce0a56a737200188df506"), "title" : "Kontralto Consulting Corp", "children" : [ { "relationTo" : "pages" } ], "createdAt" : ISODate("2021-06-30T21:22:45.113Z"), "updatedAt" : ISODate("2021-06-30T21:22:56.718Z"), "__v" : 0 }
> db.pages.find()
{ "_id" : ObjectId("60dce0b06a737200188df539"), "parent" : { "relationTo" : "sites", "value" : ObjectId("60dce0a56a737200188df506") }, "heading" : "My Page", "renderings" : [ ], "children" : [ ], "createdAt" : ISODate("2021-06-30T21:22:56.678Z"), "updatedAt" : ISODate("2021-06-30T21:22:56.678Z"), "__v" : 0 }
My apologies for continuing to bump this discussion - I really want to figure this out... I must just have something wrong.
Hey @RobertAlbus, No problem!
I just made a PR to your repo that fixes the bug, though you'll want to clean it up. The problem was the relationship object for the children was being sent as id
instead of value
. After making this change I was able to save a Page with a relationship to a Site without error and the database appears to be correct with both relationships being populated.
https://github.com/RobertAlbus/kto-cms/pull/1
We have a plan to update the documentation because I too was confused by this until I dug deeper. Beyond that we can work on adding validation so value
is required for relationTo of multiple types. That would have caught this immediately.
@DanRibbens ! This is fantastic. Thank you for digging into that. I checked out your branch and like what I see. Very instructive.
I agree with updating the docs and adding validation. If you want to ping me for a review on either of those please feel free.
Cool! Here is a PR for the documentation changes: #214
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.