Problem/Motivation
New contact methods are often saved before the parent contact exists, with crm_contact left empty. The contact is then expected to fill that parent reference in Contact::postSave().
That hook only walks the three default fields:
emails, telephones, addresses
Inline REST writes, by contrast, honor every contact-method reference field listed in crm.serialization.settings (ContactRestWriteService::processInlineContactMethods() via ContactSerializationService::getEnabledContactMethodFields()). Creating or updating those extra fields (REST POST/PATCH, or the contact form / IEF) can leave methods referenced on the contact but still parentless.
Effects of an unparented method:
- Queries, Views, search, and access that key off
crm_contactmiss the method. - REST PATCH removal skips it:
shouldSkipRemoval()treats a method as not owned whencrm_contactdoes not match the contact, so omitted items are not deleted.
The default emails, telephones, and addresses fields are not affected.
Steps to reproduce
- Add a contact-method entity-reference field on a contact bundle (for example a second email-style field) and include it in Contact method fields at
/admin/config/crm/serialization. - POST or PATCH a contact with an inline nested entity on that field (no
crm_contactin the nested payload; the write path strips it). - Load the new contact method.
crm_contactis empty even though the contact references it. - (Optional) PATCH the same field without that method. The method entity remains because removal only deletes methods whose
crm_contactmatches the contact.
Proposed resolution
In Contact::postSave(), parent any referenced crm_contact_method whose crm_contact is empty. Do not hard-code field names. Walk every eligible contact-method reference field on the saved contact (same idea as ContactSerializationService::isEligibleField(): entity reference / primary entity reference / entity reference revisions targeting crm_contact_method).
That covers REST, IEF, and programmatic saves. PATCH deletion of extra fields should then work because those methods will have a parent.
Remaining tasks
- Replace the hardcoded field list in
Contact::postSave(). - Kernel coverage: create a contact with a non-default contact-method reference field and assert the method’s
crm_contactis set. - Kernel coverage: PATCH-omit that method after it is parented and assert it is deleted.
User interface changes
None.
API changes
None. Behavior for the three default fields stays the same. Extra contact-method reference fields start getting a parent, which is the intended contract.
Data model changes
None. Existing orphan methods are not repaired by this change unless the parent contact is saved again.
Issue fork crm-3622723
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
bluegeek9 commented