Cascade deleting associated to-many records when it's empty
I'm having troubles figuring out how to cascade save an empty slice of associated records.
Let's say I have:
type ShoppingCart struct {
ID string `db:"id,primary"`
Items []ShoppingCartItem `ref:"id" fk:"cart_id" autoload:"true" autosave:"true"`
}
type ShoppingCartItem struct {
ID string `db:"id,primary"`
ProductID string `db:"product_id"`
Quantity int32 `db:"quantity"`
}
When saving shopping cart, items are automatically cascaded, unless there are no items. If I want to empty shopping cart I want to save it like so:
// user adds item to the shopping cart
cart.Items = []ShoppingCartItem{item1}
rel.Update(ctx, cart) // item1 inserted ✅
// user empties cart
cart.Items = nil // or []ShoppingCartItem{}
rel.Update(ctx, cart) // item1 is not deleted ⚠️
The issue here is that Update thinks items haven't been loaded, so it does not delete existing items.
Is there way to force make rel to delete item1?
deleting association in structset not currently implemented (code) need to do delete it manually for now
if you are interested to submit PR, you can follow this map.go code to use mutation.DeletedIDs
I would like to make a PR to address this issue. But I would appreciate if you could suggest a solution here.
I'm thinking we need to preserve existing behaviour: nil and empty slice - no cascading, so we don't break anything for anyone.
From top of my head we could:
- Add an option
rel.ForceCascade("items")- this will tell rel we are sure items were preloaded, so if they are empty delete everything. - Create a generic wrapper
Items rel.ToMany[X], this wrapper will explicitly mark assoc as loaded and will help track associated elements.
Let me know and I will try to make a PR.
rel.ForceCascade sounds good to me
rel.ToMany[X] is a class that need to be embedded?
ForceCascade it is :)
For rel.ToMany[X] I thought to introduce a generic which would provide additional information about association. Something like:
type ShoppingCart struct {
ID string `db:"id,primary"`
Items rel.ToMany[ShoppingCartItem] `ref:"id" fk:"cart_id" autoload:"true" autosave:"true"`
}
type ShoppingCartItem struct {
ID string `db:"id,primary"`
ProductID string `db:"product_id"`
Quantity int32 `db:"quantity"`
}
The generic would be something like
type ToMany[A any] struct {
Elements []A
loaded bool
}
Iterating becomes a bit cumbersome, but rel gets a place to store additional information about loaded state.
I can take a look into it, if you're not getting any time for it @skolodyazhnyy ?