{"action":"created","issue":{"url":"https://api.github.com/repos/cfwheels/cfwheels/issues/490","labels_url":"https://api.github.com/repos/cfwheels/cfwheels/issues/490/labels{/name}","comments_url":"https://api.github.com/repos/cfwheels/cfwheels/issues/490/comments","events_url":"https://api.github.com/repos/cfwheels/cfwheels/issues/490/events","html_url":"https://github.com/cfwheels/cfwheels/issues/490","id":84688840,"number":490,"title":"Nested properties `sortProperty` argument causes issues when auto-deleting a nested record","user":{"login":"chrisdpeters","id":159187,"avatar_url":"https://avatars.githubusercontent.com/u/159187?v=3","gravatar_id":"","url":"https://api.github.com/users/chrisdpeters","html_url":"https://github.com/chrisdpeters","followers_url":"https://api.github.com/users/chrisdpeters/followers","following_url":"https://api.github.com/users/chrisdpeters/following{/other_user}","gists_url":"https://api.github.com/users/chrisdpeters/gists{/gist_id}","starred_url":"https://api.github.com/users/chrisdpeters/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chrisdpeters/subscriptions","organizations_url":"https://api.github.com/users/chrisdpeters/orgs","repos_url":"https://api.github.com/users/chrisdpeters/repos","events_url":"https://api.github.com/users/chrisdpeters/events{/privacy}","received_events_url":"https://api.github.com/users/chrisdpeters/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/cfwheels/cfwheels/labels/bug","name":"bug","color":"e11d21"}],"state":"open","locked":false,"assignee":{"login":"chrisdpeters","id":159187,"avatar_url":"https://avatars.githubusercontent.com/u/159187?v=3","gravatar_id":"","url":"https://api.github.com/users/chrisdpeters","html_url":"https://github.com/chrisdpeters","followers_url":"https://api.github.com/users/chrisdpeters/followers","following_url":"https://api.github.com/users/chrisdpeters/following{/other_user}","gists_url":"https://api.github.com/users/chrisdpeters/gists{/gist_id}","starred_url":"https://api.github.com/users/chrisdpeters/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chrisdpeters/subscriptions","organizations_url":"https://api.github.com/users/chrisdpeters/orgs","repos_url":"https://api.github.com/users/chrisdpeters/repos","events_url":"https://api.github.com/users/chrisdpeters/events{/privacy}","received_events_url":"https://api.github.com/users/chrisdpeters/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/cfwheels/cfwheels/milestones/3","html_url":"https://github.com/cfwheels/cfwheels/milestones/2.0.0","labels_url":"https://api.github.com/repos/cfwheels/cfwheels/milestones/3/labels","id":153101,"number":3,"title":"2.0.0","description":"Major release (breaks backwards compatibility).","creator":{"login":"rip747","id":23683,"avatar_url":"https://avatars.githubusercontent.com/u/23683?v=3","gravatar_id":"","url":"https://api.github.com/users/rip747","html_url":"https://github.com/rip747","followers_url":"https://api.github.com/users/rip747/followers","following_url":"https://api.github.com/users/rip747/following{/other_user}","gists_url":"https://api.github.com/users/rip747/gists{/gist_id}","starred_url":"https://api.github.com/users/rip747/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rip747/subscriptions","organizations_url":"https://api.github.com/users/rip747/orgs","repos_url":"https://api.github.com/users/rip747/repos","events_url":"https://api.github.com/users/rip747/events{/privacy}","received_events_url":"https://api.github.com/users/rip747/received_events","type":"User","site_admin":false},"open_issues":5,"closed_issues":1,"state":"open","created_at":"2012-07-28T12:37:39Z","updated_at":"2015-08-25T21:48:35Z","due_on":null,"closed_at":null},"comments":3,"created_at":"2015-06-03T17:51:41Z","updated_at":"2015-08-25T22:04:45Z","closed_at":null,"body":"Let's say I have a setup like this:\r\n```coldfusion\r\n<cfscript>\r\n\r\n// models/Invoice.cfc\r\ncomponent extends=\"Model\" {\r\n  function init() {\r\n    hasMany(\"items\");\r\n    nestedProperties(association=\"items\", allowDelete=true, sortProperty=\"position\");\r\n  }\r\n}\r\n\r\n</cfscript>\r\n```\r\nThen through the form, you mark an `item` for deletion. Here it is simulated in the controller:\r\n```coldfusion\r\n<cfscript>\r\n\r\n// controllers/Invoices.cfc\r\ncomponent extends=\"Controller\" {\r\n  function update() {\r\n    // Pretend this creates an invoice with 2 items\r\n    invoice = model(\"invoice\").findOne(where=\"id=#params.key#\", include=\"items\", order=\"position\");\r\n    invoice.items[1]._delete = true;\r\n    invoice.save();\r\n  }\r\n}\r\n\r\n</cfscript>\r\n```\r\nWhen you load the invoice with its associated items later, you get a nasty array with a Java `null` in the first position of the array:\r\n```coldfusion\r\n<cfscript>\r\n\r\ninvoice = model(\"invoice\").findOne(where=\"id=#params.key#\", include=\"items\", order=\"position\");\r\n// You'll see a null value as the first array item\r\nWriteDump(var=invoice, showUdfs=false, abort=true);\r\n\r\n</cfscript>\r\n```\r\nTo counter this, I've needed to write callbacks that adjust the `position` manually after save, and I need to do that manually every single time I use nested properties (which is a lot). I think it's probably time to fix this once and for all."},"comment":{"url":"https://api.github.com/repos/cfwheels/cfwheels/issues/comments/134755921","html_url":"https://github.com/cfwheels/cfwheels/issues/490#issuecomment-134755921","issue_url":"https://api.github.com/repos/cfwheels/cfwheels/issues/490","id":134755921,"user":{"login":"chrisdpeters","id":159187,"avatar_url":"https://avatars.githubusercontent.com/u/159187?v=3","gravatar_id":"","url":"https://api.github.com/users/chrisdpeters","html_url":"https://github.com/chrisdpeters","followers_url":"https://api.github.com/users/chrisdpeters/followers","following_url":"https://api.github.com/users/chrisdpeters/following{/other_user}","gists_url":"https://api.github.com/users/chrisdpeters/gists{/gist_id}","starred_url":"https://api.github.com/users/chrisdpeters/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chrisdpeters/subscriptions","organizations_url":"https://api.github.com/users/chrisdpeters/orgs","repos_url":"https://api.github.com/users/chrisdpeters/repos","events_url":"https://api.github.com/users/chrisdpeters/events{/privacy}","received_events_url":"https://api.github.com/users/chrisdpeters/received_events","type":"User","site_admin":false},"created_at":"2015-08-25T22:04:45Z","updated_at":"2015-08-25T22:04:45Z","body":"This gist demonstrates a fix we have to put in place to fix this issue:\r\nhttps://gist.github.com/chrisdpeters/4acd3ae3a6e0208e4ee5\r\n\r\nI believe this still leaves a problem open though.\r\n\r\nLet's say we have 2 items in the array:\r\n1.  Item is marked for deletion\r\n2.  Item not marked for deletion\r\n\r\nThe CFWheels process purges item 1 from the array and maybe has already told the database to delete the item.\r\n\r\nBut let's say validation fails after that. You're left with a single-item array containing item 2 as the new first item. Then CFWheels rolls back the transaction, including the item that was supposed to be deleted.\r\n\r\nThe form is displayed but only with the single-item array. The next time the user submits the form, the array submitted by the form doesn't match the data from the now-rolled-back database."}}