From 1b03239c6ded4e813ae7961a6bb82923776b4855 Mon Sep 17 00:00:00 2001 From: WimVelzeboer Date: Thu, 27 Apr 2023 11:27:45 +0100 Subject: [PATCH] Refactor of getChildIdsById and getGrandParentIdById of fflib_SObject2 The methods are no longer depended on the domain method getRelatedFieldByDomainType, which could only relate a relationship with a single field. This change will allow to build the map with the correct fields when there are multiple relationships to the related SObjectType. for example if an SObject has two relations to Account; BillingAccountId and AccountId, then we can now specify in the method parameters which relationship should be taken. Signed-off-by: WimVelzeboer --- .../classes/domains/fflib_ISObjects2.cls | 4 + .../classes/domains/fflib_SObjects2.cls | 97 ++++++++++++++----- .../classes/domains/fflib_SObjects2Test.cls | 89 ++++++++++++++++- 3 files changed, 165 insertions(+), 25 deletions(-) diff --git a/sfdx-source/apex-extensions/main/default/classes/domains/fflib_ISObjects2.cls b/sfdx-source/apex-extensions/main/default/classes/domains/fflib_ISObjects2.cls index 35a36ac..6e1778d 100644 --- a/sfdx-source/apex-extensions/main/default/classes/domains/fflib_ISObjects2.cls +++ b/sfdx-source/apex-extensions/main/default/classes/domains/fflib_ISObjects2.cls @@ -29,6 +29,10 @@ */ public interface fflib_ISObjects2 extends fflib_ISObjects { + Map> getChildIdsById(Map domainsByRelationshipField); + Map getParentIdById(Map domainsByRelationshipField); + + // DEPRECATED METHODS Map getGrandParentIdById(List relatedDomains); Map> getChildIdsById(List relatedDomains); } \ No newline at end of file diff --git a/sfdx-source/apex-extensions/main/default/classes/domains/fflib_SObjects2.cls b/sfdx-source/apex-extensions/main/default/classes/domains/fflib_SObjects2.cls index f2f4486..b5f5654 100644 --- a/sfdx-source/apex-extensions/main/default/classes/domains/fflib_SObjects2.cls +++ b/sfdx-source/apex-extensions/main/default/classes/domains/fflib_SObjects2.cls @@ -81,33 +81,57 @@ public virtual class fflib_SObjects2 /** * Creates a map between the record Ids of this domain to the last given grandparent domain. - * This method is using getRelatedFieldByDomainType to determine which SObjectField - * is related to the given parent domain * - * @param relatedDomains A List of domains in hierarchical order from first parent to grandparent + * @param domainsByRelationshipField A Map of domains and the in hierarchical order from first parent to grandparent * * @return Returns a map with KEY: The Ids of the records in this domain, * to the VALUE: The Id of the last grandparent record listed in the relatedDomains, * null if no grandparent exists. * - * @see getRelatedFieldByDomainType - * * @example - * IInvoiceLines invoiceLineDomain = InvoiceLines.newInstance(invoiceLineRecords); - * IInvoices invoicesDomain = Invoices.newInstance(invoiceRecords); + * ICases casesDomain = Cases.newInstance(caseRecords); + * IContacts contactsDomain = Contacts.newInstance(contactRecords); * IAccount accountDomain = Accounts.newInstance(accountRecords); * - * // A map with key - InvoiceLine.Id to value - Account.Id - * Map accountIdByInvoiceLineId = - * invoiceLineDomain.getGrandParentIdById( - * new List + * // A map with key - Case.Id to value - Account.Id + * Map accountIdByCaseId = + * casesDomain.getParentIdById( + * new Map * { - * invoicesDomain, - * accountDomain + * Schema.Case.ContactId => contactDomain, + * Schema.Contact.AccountId => accountDomain * }); */ + public virtual Map getParentIdById(Map domainsByRelationshipField) + { + // Takes the highest related domain from the domainsByRelationshipField + Schema.SObjectField relationshipField = + new List(domainsByRelationshipField.keySet()) + .get(0); + fflib_SObjects2 relatedDomain = domainsByRelationshipField.remove(relationshipField); + + // Extract the Id values of the related SObjectType + Map result = getIdFieldByIdField( + relationshipField, + getSObjectType().getDescribe().fields.getMap().get('Id')); + + // Check if we need to resolve another parent relation, if not return the results + if (domainsByRelationshipField.isEmpty()) return result; + + // Fetch the Ids of the related SObjectType + Map parentIdByChildId = relatedDomain.getParentIdById(domainsByRelationshipField); + + // Replace the maps value (ParentId) with the grand parent Id + return fflib_ArrayUtils.replaceValue(result, parentIdByChildId); + } + + /** DEPRECATED **/ public virtual Map getGrandParentIdById(List relatedDomains) { + System.debug(LoggingLevel.WARN, + 'The \'getGrandParentIdById(List relatedDomains)\' method is deprecated,' + + 'please refactor to \'getParentIdById(Map relatedDomains)\''); + fflib_ISObjects2 relatedDomain = relatedDomains.remove(0); SObjectType relatedDomainType = relatedDomain.getSObjectType(); Map relatedFieldByDomainType = getRelatedFieldByDomainType(); @@ -136,30 +160,55 @@ public virtual class fflib_SObjects2 * This method is using getRelatedFieldByDomainType to determine which SObjectField * is related to the given parent domain * - * @param relatedDomains A List of domains in hierarchical order from first child to grandchild + * @param domainsByRelationshipField A Map of domains in hierarchical order from first child to grandchild * * @return Returns a map with KEY: The Ids of the records in this domain, * to the VALUE: The record Ids of the lowest child related record listed in the relatedDomains, * null if no grandchild exists. * - * @see getRelatedFieldByDomainType - * * @example - * IInvoiceLines invoiceLineDomain = InvoiceLines.newInstance(invoiceLineRecords); - * IInvoices invoicesDomain = Invoices.newInstance(invoiceRecords); + * ICases casesDomain = Cases.newInstance(caseRecords); + * IContacts contactsDomain = Contacts.newInstance(contactRecords); * IAccount accountDomain = Accounts.newInstance(accountRecords); * - * // A map with key - Account.Id to value - InvoiceLine.Id - * Map> invoiceLineIdsByAccountId = - * accountDomain.getChildIdsById( - * new List + * // A map with key - Case.Id to value - Account.Id + * Map> caseIdsByAccountId = + * accountsDomain.getChildIdsById( + * new Map * { - * invoicesDomain, - * invoiceLineDomain + * Schema.Contact.AccountId => contactsDomain + * Schema.Case.ContactId => casesDomain, * }); */ + public virtual Map> getChildIdsById(Map domainsByRelationshipField) + { + // Takes the highest related domain from the domainsByRelationshipField + Schema.SObjectField relationshipField = + new List(domainsByRelationshipField.keySet()) + .get(0); + fflib_SObjects2 relatedDomain = domainsByRelationshipField.remove(relationshipField); + + Map> result = relatedDomain.getIdFieldsByIdField( + relatedDomain.getSObjectType().getDescribe().fields.getMap().get('Id'), + relationshipField); + + // Check if we need to resolve another child relation, if not return the results + if (domainsByRelationshipField.isEmpty()) return result; + + Map> childIdsById = relatedDomain.getChildIdsById(domainsByRelationshipField); + + // Replace the maps value (childIds) with the grand child Id + return fflib_ArrayUtils.replaceValues(result, childIdsById); + } + + /** DEPRECATED **/ public virtual Map> getChildIdsById(List relatedDomains) { + System.debug(LoggingLevel.WARN, + 'The \'getChildIdsById(List relatedDomains)\' method is deprecated,' + + 'please refactor to \'getChildIdsById(Map relatedDomains)\''); + + SObjectType thisDomainType = getSObjectType(); fflib_SObjects2 relatedDomain = relatedDomains.remove(0); Map relatedFieldByDomainType = relatedDomain.getRelatedFieldByDomainType(); diff --git a/sfdx-source/apex-extensions/tests/classes/domains/fflib_SObjects2Test.cls b/sfdx-source/apex-extensions/tests/classes/domains/fflib_SObjects2Test.cls index 5168d6d..5cd5edf 100644 --- a/sfdx-source/apex-extensions/tests/classes/domains/fflib_SObjects2Test.cls +++ b/sfdx-source/apex-extensions/tests/classes/domains/fflib_SObjects2Test.cls @@ -121,7 +121,7 @@ private class fflib_SObjects2Test } @IsTest - static void itShouldReturnTheRelatedChildDomainId() + static void itShouldReturnTheRelatedChildDomainId_deprecated() { // GIVEN a case A & Z related to contact A related to Account, Id caseIdA = fflib_IDGenerator.generate(Schema.Case.SObjectType); @@ -161,6 +161,47 @@ private class fflib_SObjects2Test System.Assert.areEqual(2, result.get(accountId).size(), 'Incorrect amount of related cases to the account'); System.Assert.isTrue(result.get(accountId).containsAll(new Set{ caseIdA, caseIdZ })); } + @IsTest + static void itShouldReturnTheRelatedChildDomainId() + { + // GIVEN a case A & Z related to contact A related to Account, + Id caseIdA = fflib_IDGenerator.generate(Schema.Case.SObjectType); + Id caseIdZ = fflib_IDGenerator.generate(Schema.Case.SObjectType); + Id caseIdB = fflib_IDGenerator.generate(Schema.Case.SObjectType); + Id caseIdC = fflib_IDGenerator.generate(Schema.Case.SObjectType); + Id contactIdA = fflib_IDGenerator.generate(Schema.Contact.SObjectType); + Id contactIdB = fflib_IDGenerator.generate(Schema.Contact.SObjectType); + Id accountId = fflib_IDGenerator.generate(Schema.Account.SObjectType); + Account accountRecord = new Account(Id = accountId); + Contact contactRecordA = new Contact(Id = contactIdA, AccountId = accountId); + Contact contactRecordB = new Contact(Id = contactIdB); + Case caseRecordA = new Case(Id = caseIdA, ContactId = contactIdA); + Case caseRecordZ = new Case(Id = caseIdZ, ContactId = contactIdA); + Case caseRecordB = new Case(Id = caseIdB, ContactId = contactIdB); + Case caseRecordC = new Case(Id = caseIdC); + + DomainAccounts accountDomain = new DomainAccounts(new List {accountRecord}); + DomainContacts contactDomain = new DomainContacts(new List {contactRecordA, contactRecordB}); + DomainCases caseDomain = new DomainCases(new List {caseRecordA, caseRecordB, caseRecordC, caseRecordZ}); + + // WHEN we request the relation between Case and Account + System.Test.startTest(); + Map> result = accountDomain.getChildIdsById( + new Map + { + Contact.AccountId => contactDomain, + Case.ContactId => caseDomain + } + ); + System.Test.stopTest(); + System.debug('result: ' + JSON.serializePretty(result)); + + // THEN it should return the AccountId By CaseId + System.Assert.areEqual(1, result.size(), 'Expected one account'); + System.Assert.isTrue(result.containsKey(accountId), 'Account Id should be the main key'); + System.Assert.areEqual(2, result.get(accountId).size(), 'Incorrect amount of related cases to the account'); + System.Assert.isTrue(result.get(accountId).containsAll(new Set{ caseIdA, caseIdZ })); + } @IsTest static void itShouldReturnTheRelatedParentDomainId() @@ -186,6 +227,52 @@ private class fflib_SObjects2Test DomainContacts contactDomain = new DomainContacts(new List {contactRecordA, contactRecordB}); DomainCases caseDomain = new DomainCases(new List {caseRecordA, caseRecordB, caseRecordC}); + // WHEN we request the relation between Case and Account + System.Test.startTest(); + Map result = caseDomain.getParentIdById( + new Map + { + Schema.Case.ContactId => contactDomain, + Schema.Contact.AccountId => accountDomain + } + ); + System.Test.stopTest(); + + // THEN it should return the AccountId By CaseId + System.Assert.areEqual(3, result.size(), 'Expected one related account to the case'); + System.Assert.isTrue(result.containsKey(caseIdA)); + System.Assert.isTrue(result.containsKey(caseIdB)); + System.Assert.isTrue(result.containsKey(caseIdC)); + System.Assert.areEqual(accountId, result.get(caseIdA), 'Account Id should be related to the Case Id A'); + System.Assert.isNull(result.get(caseIdB), 'Case Id B should have no related Account Id'); + System.Assert.isNull(result.get(caseIdC), 'Case Id B should have no related Account Id'); + } + + /** Unit test for the deprecated method **/ + @IsTest + static void itShouldReturnTheRelatedParentDomainId_deprecated() + { + // GIVEN a case related to contact related to Account , + // a case record related to contact but not to Account, + // and a case record related to nothing. + // All encapsulated in domains. + Id caseIdA = fflib_IDGenerator.generate(Schema.Case.SObjectType); + Id caseIdB = fflib_IDGenerator.generate(Schema.Case.SObjectType); + Id caseIdC = fflib_IDGenerator.generate(Schema.Case.SObjectType); + Id contactIdA = fflib_IDGenerator.generate(Schema.Contact.SObjectType); + Id contactIdB = fflib_IDGenerator.generate(Schema.Contact.SObjectType); + Id accountId = fflib_IDGenerator.generate(Schema.Account.SObjectType); + Account accountRecord = new Account(Id = accountId); + Contact contactRecordA = new Contact(Id = contactIdA, AccountId = accountId); + Contact contactRecordB = new Contact(Id = contactIdB); + Case caseRecordA = new Case(Id = caseIdA, ContactId = contactIdA); + Case caseRecordB = new Case(Id = caseIdB, ContactId = contactIdB); + Case caseRecordC = new Case(Id = caseIdC); + + DomainAccounts accountDomain = new DomainAccounts(new List {accountRecord}); + DomainContacts contactDomain = new DomainContacts(new List {contactRecordA, contactRecordB}); + DomainCases caseDomain = new DomainCases(new List {caseRecordA, caseRecordB, caseRecordC}); + // WHEN we request the relation between Case and Account System.Test.startTest(); Map result = caseDomain.getGrandParentIdById(