Merge pull request #48 from wimvelzeboer/feature/ChildMapMethodForDomain

Add getChildIdsById to fflib_SObjects2
This commit is contained in:
William Velzeboer
2023-04-20 16:45:02 +01:00
committed by GitHub
4 changed files with 143 additions and 1 deletions
@@ -30,4 +30,5 @@
public interface fflib_ISObjects2 extends fflib_ISObjects public interface fflib_ISObjects2 extends fflib_ISObjects
{ {
Map<Id, Id> getGrandParentIdById(List<fflib_ISObjects2> relatedDomains); Map<Id, Id> getGrandParentIdById(List<fflib_ISObjects2> relatedDomains);
Map<Id, Set<Id>> getChildIdsById(List<fflib_SObjects2> relatedDomains);
} }
@@ -131,6 +131,59 @@ public virtual class fflib_SObjects2
return fflib_ArrayUtils.replaceValue(result, grandChildIdByChildId); 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<Id, Set<Id>> invoiceLineIdsByAccountId =
* accountDomain.getChildIdsById(
* new List<fflib_SObjects2>
* {
* invoicesDomain,
* invoiceLineDomain
* });
*/
public virtual Map<Id, Set<Id>> getChildIdsById(List<fflib_SObjects2> relatedDomains)
{
SObjectType thisDomainType = getSObjectType();
fflib_SObjects2 relatedDomain = relatedDomains.remove(0);
Map<SObjectType, SObjectField> relatedFieldByDomainType = relatedDomain.getRelatedFieldByDomainType();
if (relatedFieldByDomainType.containsKey(thisDomainType) == false)
throw new DeveloperException(
String.format(
'RelatedDomain type {0} not present in method getRelatedFieldByDomainType',
new List<String> {String.valueOf(thisDomainType)}));
SObjectField relatedLookupFieldOnChild = relatedFieldByDomainType.get(thisDomainType);
Map<Id, Set<Id>> 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<Id, Set<Id>> 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) * @return Returns the contents of the Domain by their primary Key ('Id' field)
*/ */
@@ -340,6 +340,43 @@ public class fflib_ArrayUtils
return result; 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<Id, Set<Id>> replaceValues(Map<Id, Set<Id>> source, Map<Id, Set<Id>> replacement)
{
Map<Id, Set<Id>> result = new Map<Id, Set<Id>>();
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 * Reverse the order of a list of objects
* *
@@ -120,6 +120,48 @@ private class fflib_SObjects2Test
// todo - add assertions // 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<Account> {accountRecord});
DomainContacts contactDomain = new DomainContacts(new List<Contact> {contactRecordA, contactRecordB});
DomainCases caseDomain = new DomainCases(new List<Case> {caseRecordA, caseRecordB, caseRecordC, caseRecordZ});
// WHEN we request the relation between Case and Account
System.Test.startTest();
Map<Id, Set<Id>> result = accountDomain.getChildIdsById(
new List<fflib_SObjects2>
{
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<Id>{ caseIdA, caseIdZ }));
}
@IsTest @IsTest
static void itShouldReturnTheRelatedParentDomainId() static void itShouldReturnTheRelatedParentDomainId()
{ {
@@ -668,6 +710,14 @@ private class fflib_SObjects2Test
{ {
clearField(Schema.Account.ShippingCountry); clearField(Schema.Account.ShippingCountry);
} }
protected override Map<SObjectType, Schema.SObjectField> getRelatedFieldByDomainType()
{
return new Map<SObjectType, Schema.SObjectField>
{
Schema.Contact.SObjectType => Contact.AccountId
};
}
} }
private class DomainContacts extends fflib_SObjects2 private class DomainContacts extends fflib_SObjects2
@@ -681,7 +731,8 @@ private class fflib_SObjects2Test
{ {
return new Map<SObjectType, Schema.SObjectField> return new Map<SObjectType, Schema.SObjectField>
{ {
Schema.Account.SObjectType => Contact.AccountId Schema.Account.SObjectType => Contact.AccountId,
Schema.Case.SObjectType => Case.ContactId
}; };
} }
} }