Skip to content

Commit a455555

Browse files
authored
Merge pull request #184 from JonnyPower/feature/uow-register-dirty-fields
Feature/uow register dirty fields
2 parents 6b0d4b4 + dfce16d commit a455555

File tree

4 files changed

+90
-1
lines changed

4 files changed

+90
-1
lines changed

fflib/src/classes/fflib_ISObjectUnitOfWork.cls

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ public interface fflib_ISObjectUnitOfWork
5959
* @param record An existing record
6060
**/
6161
void registerDirty(SObject record);
62+
/**
63+
* Register specific fields on record to be updated when work is commited
64+
*
65+
* If the record has previously been registered as dirty, the dirty fields on the record in this call will overwrite
66+
* the values of the previously registered dirty record
67+
*
68+
* @param record An existing record
69+
* @param dirtyFields The fields to update if record is already registered
70+
**/
71+
void registerDirty(SObject record, List<SObjectField> dirtyFields);
6272
/**
6373
* Register an existing record to be updated when commitWork is called,
6474
* you may also provide a reference to the parent record instance (should also be registered as new separatly)

fflib/src/classes/fflib_SObjectMocks.cls

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ public class fflib_SObjectMocks
8181
mocks.mockVoidMethod(this, 'registerDirty', new List<Type> {SObject.class}, new List<Object> {record});
8282
}
8383

84+
public void registerDirty(SObject record, List<SObjectField> dirtyFields)
85+
{
86+
mocks.mockVoidMethod(this, 'registerDirty', new List<Type> {
87+
SObject.class, System.Type.forName('List<SObjectField>')
88+
}, new List<Object> {
89+
record, dirtyFields
90+
});
91+
}
92+
8493
public void registerDirty(SObject record, Schema.sObjectField relatedToParentField, SObject relatedToParentRecord)
8594
{
8695
mocks.mockVoidMethod(this, 'registerDirty', new List<Type> {SObject.class}, new List<Object> {record});

fflib/src/classes/fflib_SObjectUnitOfWork.cls

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,35 @@ public virtual class fflib_SObjectUnitOfWork
247247
* @param record An existing record
248248
**/
249249
public void registerDirty(SObject record)
250+
{
251+
registerDirty(record, new List<SObjectField>());
252+
}
253+
254+
public void registerDirty(SObject record, List<SObjectField> dirtyFields)
250255
{
251256
if(record.Id == null)
252257
throw new UnitOfWorkException('New records cannot be registered as dirty');
253258
String sObjectType = record.getSObjectType().getDescribe().getName();
254259
if(!m_dirtyMapByType.containsKey(sObjectType))
255260
throw new UnitOfWorkException(String.format('SObject type {0} is not supported by this unit of work', new String[] { sObjectType }));
256-
m_dirtyMapByType.get(sObjectType).put(record.Id, record);
261+
262+
// If record isn't registered as dirty, or no dirty fields to drive a merge
263+
if (!m_dirtyMapByType.get(sObjectType).containsKey(record.Id) || dirtyFields.isEmpty())
264+
{
265+
// Register the record as dirty
266+
m_dirtyMapByType.get(sObjectType).put(record.Id, record);
267+
}
268+
else
269+
{
270+
// Update the registered record's fields
271+
SObject registeredRecord = m_dirtyMapByType.get(sObjectType).get(record.Id);
272+
273+
for (SObjectField dirtyField : dirtyFields) {
274+
registeredRecord.put(dirtyField, record.get(dirtyField));
275+
}
276+
277+
m_dirtyMapByType.get(sObjectType).put(record.Id, registeredRecord);
278+
}
257279
}
258280

259281
/**

fflib/src/classes/fflib_SObjectUnitOfWorkTest.cls

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,54 @@ private with sharing class fflib_SObjectUnitOfWorkTest
338338
, uow.getCommitWorkEventsFired(), new Set<Schema.SObjectType>(MY_SOBJECTS), uow.getRegisteredTypes());
339339
}
340340

341+
/**
342+
* Try registering two instances of the same record as dirty. Second register should overwrite first.
343+
*
344+
* Testing:
345+
*
346+
* - Exception is thrown stopping second registration
347+
*/
348+
@isTest
349+
private static void testRegisterDirty_ExpectReplacement() {
350+
final Opportunity insertedOpp = new Opportunity(Name = 'Original', StageName = 'Open', CloseDate = System.today());
351+
insert insertedOpp;
352+
353+
Opportunity opp = new Opportunity(Id = insertedOpp.Id, Name = 'Never');
354+
Opportunity opp2 = new Opportunity(Id = insertedOpp.Id, Name = 'Expected');
355+
356+
fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS);
357+
uow.registerDirty(opp);
358+
uow.registerDirty(opp2);
359+
uow.commitWork();
360+
361+
Opportunity updated = [select Id, Name from Opportunity where Id = :insertedOpp.Id];
362+
System.assertEquals('Expected', updated.Name);
363+
}
364+
365+
/**
366+
* Try registering a single field as dirty.
367+
*
368+
* Testing:
369+
*
370+
* - field is updated
371+
*/
372+
@isTest
373+
private static void testRegisterDirty_field() {
374+
Opportunity opp = new Opportunity(Name = 'test name', StageName = 'Open', CloseDate = System.today());
375+
insert opp;
376+
377+
Opportunity nameUpdate = new Opportunity(Id = opp.Id, Name = 'UpdateName');
378+
Opportunity amountUpdate = new Opportunity(Id = opp.Id, Amount = 250);
379+
fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS);
380+
uow.registerDirty(nameUpdate);
381+
uow.registerDirty(amountUpdate, new List<SObjectField> { Opportunity.Amount } );
382+
uow.commitWork();
383+
384+
opp = [select Name, Amount from Opportunity where Id = :opp.Id];
385+
System.assertEquals(opp.Name, nameUpdate.Name);
386+
System.assertEquals(opp.Amount, amountUpdate.Amount);
387+
}
388+
341389
/**
342390
* Assert that actual events exactly match expected events (size, order and name)
343391
* and types match expected types

0 commit comments

Comments
 (0)