From a4753d1db112f479ce94c7f75ea8113f22235b41 Mon Sep 17 00:00:00 2001 From: WimVelzeboer Date: Thu, 20 Apr 2023 16:21:00 +0100 Subject: [PATCH] Add getChildIdsById to fflib_SObjects2 To retrieve a map of related ids from (grand-)child relationships Signed-off-by: WimVelzeboer --- .../classes/domains/fflib_ISObjects2.cls | 1 + .../classes/domains/fflib_SObjects2.cls | 53 +++++++++++++++++++ .../classes/utils/fflib_ArrayUtils.cls | 37 +++++++++++++ .../classes/domains/fflib_SObjects2Test.cls | 53 ++++++++++++++++++- 4 files changed, 143 insertions(+), 1 deletion(-) 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 5fa6102..35a36ac 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 @@ -30,4 +30,5 @@ public interface fflib_ISObjects2 extends fflib_ISObjects { 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 01be92e..f2f4486 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 @@ -131,6 +131,59 @@ public virtual class fflib_SObjects2 return fflib_ArrayUtils.replaceValue(result, grandChildIdByChildId); } + /** + * Creates a map between the record Ids of this domain to the last given child 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 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); + * IAccount accountDomain = Accounts.newInstance(accountRecords); + * + * // A map with key - Account.Id to value - InvoiceLine.Id + * Map> invoiceLineIdsByAccountId = + * accountDomain.getChildIdsById( + * new List + * { + * invoicesDomain, + * invoiceLineDomain + * }); + */ + public virtual Map> getChildIdsById(List relatedDomains) + { + SObjectType thisDomainType = getSObjectType(); + fflib_SObjects2 relatedDomain = relatedDomains.remove(0); + Map relatedFieldByDomainType = relatedDomain.getRelatedFieldByDomainType(); + + if (relatedFieldByDomainType.containsKey(thisDomainType) == false) + throw new DeveloperException( + String.format( + 'RelatedDomain type {0} not present in method getRelatedFieldByDomainType', + new List {String.valueOf(thisDomainType)})); + + SObjectField relatedLookupFieldOnChild = relatedFieldByDomainType.get(thisDomainType); + Map> result = relatedDomain.getIdFieldsByIdField( + relatedDomain.getSObjectType().getDescribe().fields.getMap().get('Id'), + relatedLookupFieldOnChild); + + // Check if we need to resolve another child relation, if not return the results + if (relatedDomains.isEmpty()) return result; + + Map> childIdsById = relatedDomain.getChildIdsById(relatedDomains); + + // Replace the maps value (childIds) with the grand child Id + return fflib_ArrayUtils.replaceValues(result, childIdsById); + } + /** * @return Returns the contents of the Domain by their primary Key ('Id' field) */ diff --git a/sfdx-source/apex-extensions/main/default/classes/utils/fflib_ArrayUtils.cls b/sfdx-source/apex-extensions/main/default/classes/utils/fflib_ArrayUtils.cls index 2932c19..f70a49d 100644 --- a/sfdx-source/apex-extensions/main/default/classes/utils/fflib_ArrayUtils.cls +++ b/sfdx-source/apex-extensions/main/default/classes/utils/fflib_ArrayUtils.cls @@ -340,6 +340,43 @@ public class fflib_ArrayUtils return result; } + /** + * Takes two maps and replaces the value of the source with the values of the replacement, + * where the source value matches the replacement key. + * If multiple matches are found the values will merged into one set. + * The value will become null if the source value does not exists as key in replacement map + * + * @param source The map to replace its value + * @param replacement The map to use its values for replacement + * + * @return Returns a Map<{Source} Id, Set<{Replacement} Id>> + */ + public static Map> replaceValues(Map> source, Map> replacement) + { + Map> result = new Map>(); + for (Id keyId : source.keySet()) + { + for (Id sourceValue : source.get(keyId)) { + if (replacement.containsKey(sourceValue)) { + if (result.containsKey(keyId)) + { + result.get(keyId).addAll(replacement.get(sourceValue)); + } + else + { + result.put(keyId, replacement.get(sourceValue)); + } + } + } + // There is no replacement value for this keyId + if (result.containsKey(keyId) == false ) + { + result.put(keyId, null); + } + } + return result; + } + /** * Reverse the order of a list of objects * 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 c441b76..5168d6d 100644 --- a/sfdx-source/apex-extensions/tests/classes/domains/fflib_SObjects2Test.cls +++ b/sfdx-source/apex-extensions/tests/classes/domains/fflib_SObjects2Test.cls @@ -120,6 +120,48 @@ private class fflib_SObjects2Test // todo - add assertions } + @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 List + { + contactDomain, + 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() { @@ -668,6 +710,14 @@ private class fflib_SObjects2Test { clearField(Schema.Account.ShippingCountry); } + + protected override Map getRelatedFieldByDomainType() + { + return new Map + { + Schema.Contact.SObjectType => Contact.AccountId + }; + } } private class DomainContacts extends fflib_SObjects2 @@ -681,7 +731,8 @@ private class fflib_SObjects2Test { return new Map { - Schema.Account.SObjectType => Contact.AccountId + Schema.Account.SObjectType => Contact.AccountId, + Schema.Case.SObjectType => Case.ContactId }; } }