3

I'm not sure if this is possible but I'm hoping an APEX ninja can help me with something I want to attempt.

I have a class called Process and at different entry points I intend to make a List<Process> that I can iterate through to implement dynamic business logic that should be applied to a List<SObject>.

IF for example these processes were fired during a trigger event, then while I iterate through the List<SObjects> within the Trigger I want to be able to apply a dynamic list of filters (defined within a given Process). For example in event-driven programming I would be able to do something like this:

for(SObject obj : Objects){
    for(Filter filter : Filters){
        // Apply filter to obj
    }
}

OR in my scenario this may look more like so:

for(SObject obj : Objects){
    for(Process p : Processes){
        for(Filter f : p.filters){
            // Apply filter to obj
        }
        p.run();
    }
}

Can you please confirm if this is or isn't possible and any details that might help me to implement a solution?

I'm not sure if I'm phrasing this question clearly enough, so if I need any additions please comment below and I'll make adjustments to clarify. Thank you!

  • 1
    developer.salesforce.com/page/… ? – sfdcfox Aug 16 '16 at 2:04
  • @sfdcfox This is great information. I feel like I'm trying to find a way to store the Filter or business logic in a List to be run at a later time, rather than storing the "results" of a filter to be run if that makes sense. Do you know of anything along those lines? – Xtremefaith Aug 16 '16 at 2:31
2

I don't know about ninjas, but the Selector package has worked very well for me in the past when I had to apply filters. I've even had to run a similar paradigm to what you describe. One nice thing about this package is you can compose many filters at once. You can also store the filter and run it later.

You could do something like:

public class Process
{
    final List<Select.Filter> filters;
    public class Process()
    {
        filters = new List<Select.Filter>();
    }
    public List<SObject> filter(List<SObject> input)
    {
        return Select.Records.all(filters).filter(input);
    }
}

The documentation (linked here again) is a great starter. Here are some of the more relevant segments:

introduction

Every time you write a new trigger you end up reimplementing the same filters over and over again.

List<Account> filteredAccounts = new List<Account>();

for ( Account newAccount : Trigger.new )
{
    Account oldAccount = Trigger.oldMap.get( newAccount.Id );

    if ( newAccount.Name != oldAccount.Name )
    {
        filteredAccounts.add( newAccount );
    }
}

return filteredAccounts;

Wouldn't you rather write this:

return Select.Field.hasChanged( Account.Name )
                   .filter( Trigger.new, Trigger.oldMap );

...

combining filters

If the filter you need is simply a logical combination of built-ins or existing custom filters, you can use the filter composition methods to build it up.

// !((filterA && filterB) || filterC)
Select.Filter myComplexFilter = filterA.andx( filterB )
                                       .orx( filterC )
                                       .notx();

...

installation

Install the managed package Latest:

  • Can you add extra details about the usage of the Select package, this is very new to me so I'm trying to understand it better. Currently reading through the link @sfcdfox provided in the comments. Are you suggesting to add this package apart from what he suggested? – Xtremefaith Aug 16 '16 at 2:33
  • Sure, I just copied in some sections of the readme. It's pretty intuitive once you use it a few times, especially if you end up writing your own predicates. I haven't needed them super often, but they can be rewarding to work with. – Adrian Larson Aug 16 '16 at 2:42
  • I need to add the entire package to my codebase in order to use any of this correct? The intro definitely relates to the problem I'm trying to solve, but perhaps because my mind is a little fried getting to this point that this feels overwhelming to implement at the moment. I will read through it more and see if it is something I can handle – Xtremefaith Aug 16 '16 at 2:46
  • @Xtreme The install link makes it pretty straightforward to import into your org. – Adrian Larson Aug 16 '16 at 2:49
0

To add to @AdrianLarson answer

You can even combine this to go from a filtered list of objects to a retrieved set of other objects with the Separation of Concerns fflib Selector layers in one glorious statement with a handy util method proposed by sfdcfox

Works nicely once you've adopted the fflib Selector layer (often passed a set of Ids, extensible for other types). Empty lists work fine. No need to test for null.

 // get all Contacts from Opportunities' Accounts when change in Opportunity name
Contact[] cList = ContactsSelector.newInstance().selectByAccountId(       //fflib
                          Util.getIdSetFromField( . // gets Ids from a list
                                    Select.Field.hasChanged(Opportunity.Name ) // the filtered list
                                       .filter( Trigger.new, Trigger.oldMap ),
                                    Opportunity.AccountId));


//  ------------------------------------------------------
//  getIdSetFromField   : gets a set of Ids from a list of sobjects in <field>
//  ------------------------------------------------------
public static Set<ID> getIdSetFromField(SObject[] records, SObjectField fieldname) {
    return new Set<ID> ((List<ID>) getValListFromField(List<ID>.class, records, fieldname));
}

//  --------------------------------------------------------
//  getValListFromField     : Returns a list of field values from a list of sobjects; ref: http://salesforce.stackexchange.com/a/64949/2602
//                          : Usage Ex: Decimal[] amountList = (Decimal[])Util.getValListFromField(Decimal[].class,Trigger.new,Opportunity.Amount);
//  --------------------------------------------------------
public static Object[] getValListFromField(Type resListType, SObject[] records, SObjectField fieldname) {
    Object[] res    = (Object[])resListType.newInstance();
    for (SObject sObj: records)
        res.add(sobj.get(fieldname));
    return res; 
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.