CodamAIDocs
Topicdone

Deleting by changing

A PUT or PATCH that removes a dependent child from a list or a reference deletes it. This page explains when that happens.

Variants
PUT: list missing → all dependent children deletedPUT/PATCH: child missing from the list → deletedPATCH: list missing → unchangedsingle reference null → deletedwithout DELETE flag → only unlinkedroles and hooks

What this is about

DELETE is not the only thing that deletes. A PUT or PATCH on the parent can delete children too, namely when a dependent child (relation with the DELETE flag) no longer belongs to it afterwards. This happens when

  • a list is sent without the child,
  • a list counts as empty,
  • a single reference is cleared.

This is intended: an invoice item that drops out of its invoice makes no sense without the invoice. But it is also the most common way to delete data by accident.

Before and after

A contact with a main phone mainPhone and further phones phones. Both relations have the DELETE flag.

Fieldbeforerequestafter
phonesP2, P3"phones": [{ "id": "P2…" }]P2; P3 deleted
phonesP2, P3"phones": []empty; P2 and P3 deleted
mainPhoneP1"mainPhone": nullempty; P1 deleted
phonesP2, P3PUT without phonesempty; P2 and P3 deleted
phonesP2, P3PATCH without phonesP2, P3, unchanged

The last two rows are the difference between PUT and PATCH: with PUT, “missing” means the same as “empty”; with PATCH, “missing” means “do not touch”.

PATCH that deletes P3
Request
PATCH /api/rest/crm/contact/update/c052…
{
  "data": {
    "id": "c052…",
    "phones": [ { "id": "P2…" } ]
  },
  "response": ["id", { "field": "phones", "response": ["id", "number"] }]
}
Response
{ "data": { "id": "c052…",
            "phones": [ { "id": "P2…", "number": "0611-17277000" } ] },
  "meta": { "error": false } }

Afterwards P3 has not only disappeared from the list, it is deleted. /phone/read/P3… returns 404.

When a child is deleted

An existing child after PUT or PATCH
VerbField in the requestChild contained in itFlag DELETEResult for the child
–list or referenceyes–stays connected
–list without the child, [] or nullnoyesdeleted
–list without the child, [] or nullnonounlinked, stays
PUTmissing–yesdeleted (missing = empty)
PUTmissing–nounlinked (missing = empty)
PATCHmissing––unchanged
–single reference null–yesdeleted

“Contained” means: the child is in the list or the reference with its id. Which other fields you send along makes no difference for this. See Lists as target state.

What happens

  1. 1
    Client→CDMS
    sends PATCH /contact/update/c052… with phones: [P2]
  2. 2
    CDMS
    checks visibility and update role of the contact
  3. 3
    CDMS
    compares the list with the stored state: P3 is missing
  4. 4
    CDMS
    phones has DELETE → P3 is deleted
    Without the DELETE flag, P3 would only be unlinked: its reference to the contact becomes empty, P3 stays.
  5. 5
    CDMS
    checks the delete role of the phone model (or a field role on phones)
  6. 6
    CDMS→Client
    role missing → 403, nothing is saved, not even the other changes
  7. 7
    Hook
    DELETE hooks of P3 run, together with the UPDATE or PATCH hooks of the contact
  8. 8
    CDMS→Database
    deletes P3 with its own dependent children and files, saves the contact, reads back

Everything from Dependent objects (cascades) applies to the deleted child: it needs the delete role of its model, its own dependent children go along, its other relations are unlinked.

Pitfalls

What comes next

Sources in the code and the knowledge base
  • CDMS/cdms-system-layer – AbstractLayer.recursiveUpdate, recursivePatch, reduceToTargetState, detachOrDeleteModel, detachOrDeleteMember, recursiveDelete
  • CDMS/cdms-integrationtest – AbstractUpdateTest (updateOneToManyListRecursiveDeleteReallyDeletes), AbstractRecursiveUpdate, AbstractRecursivePatch
  • documentation/20-api/04-schreibsemantik.md
Search